diff options
author | Amar Tumballi <amar@gluster.com> | 2009-11-30 01:16:35 +0000 |
---|---|---|
committer | Anand V. Avati <avati@dev.gluster.com> | 2009-12-01 10:02:48 -0800 |
commit | 936001b147a3adcf9731d02e8f2fb62e5e50caf4 (patch) | |
tree | ed0592d8276a3ff16ec3e15e58c4819b33352992 /xlators/features/trash/src | |
parent | 90cebe19380b4319e3a4345f31268563b51e08c3 (diff) |
features/trash initial cleanup commit
* support O_TRUNC flag in open (call comes to FS as 'truncate'
or 'ftruncate' fop)
* option to provide 'eliminate-pattern' so those files matching
won't be kept in trash. Eg:
option eliminate-pattern *.out|*~
* option to give 'maximum' file size to keep in trash, ie, when a
20GB file is deleted, it won't be kept in trash if max file size
given is 1GB. Eg:
option max-trashable-file-size 1GB
These particular features are developped as a part of college project
by following team:
Sunil bhagwath <sunilkbhagwat@gmail.com>
Yashaswi Kumar <yashaswikumar@gmail.com>
Rashmi B K <rashmibk17@gmail.com>
Sandeep M <astrophyster@gmail.com>
Submitting after minor coding standard edits and memoryleak fixes.
Signed-off-by: Amar Tumballi <amar@gluster.com>
Signed-off-by: Anand V. Avati <avati@dev.gluster.com>
BUG: 142 (enhance features/trash translator so it can work on client side too..)
URL: http://bugs.gluster.com/cgi-bin/bugzilla3/show_bug.cgi?id=142
Diffstat (limited to 'xlators/features/trash/src')
-rw-r--r-- | xlators/features/trash/src/Makefile.am | 2 | ||||
-rw-r--r-- | xlators/features/trash/src/trash.c | 722 | ||||
-rw-r--r-- | xlators/features/trash/src/trash.h | 80 |
3 files changed, 194 insertions, 610 deletions
diff --git a/xlators/features/trash/src/Makefile.am b/xlators/features/trash/src/Makefile.am index 52fced5247a..15998a56ec7 100644 --- a/xlators/features/trash/src/Makefile.am +++ b/xlators/features/trash/src/Makefile.am @@ -6,6 +6,8 @@ trash_la_LDFLAGS = -module -avoidversion trash_la_SOURCES = trash.c trash_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la +noinst_HEADERS = trash.h + AM_CFLAGS = -fPIC -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -Wall -D$(GF_HOST_OS)\ -I$(top_srcdir)/libglusterfs/src -shared -nostartfiles $(GF_CFLAGS) diff --git a/xlators/features/trash/src/trash.c b/xlators/features/trash/src/trash.c index b2de4cdbe50..98d0663279c 100644 --- a/xlators/features/trash/src/trash.c +++ b/xlators/features/trash/src/trash.c @@ -22,556 +22,7 @@ #include "config.h" #endif - -#include "glusterfs.h" -#include "logging.h" -#include "dict.h" -#include "xlator.h" -#include "defaults.h" - -#include <libgen.h> - -/* TODO: currently it can work only above posix, no other translators - * between them. Not a good thing. Try making more reliable methods. - */ - -struct trash_struct { - inode_t *inode; - loc_t loc1; - loc_t loc2; - char origpath[ZR_PATH_MAX]; - char newpath[ZR_PATH_MAX]; - char oldpath[ZR_PATH_MAX]; // used only in case of rename -}; -typedef struct trash_struct trash_local_t; - -struct trash_priv { - char trash_dir[ZR_PATH_MAX]; -}; -typedef struct trash_priv trash_private_t; - -int32_t -trash_unlink_rename_cbk (call_frame_t *frame, - void *cookie, - xlator_t *this, - int32_t op_ret, - int32_t op_errno, - struct stat *buf, - struct stat *preoldparent, - struct stat *postoldparent, - struct stat *prenewparent, - struct stat *postnewparent); -int32_t -trash_rename_rename_cbk (call_frame_t *frame, - void *cookie, - xlator_t *this, - int32_t op_ret, - int32_t op_errno, - struct stat *buf, - struct stat *preoldparent, - struct stat *postoldparent, - struct stat *prenewparent, - struct stat *postnewparent); - -/** - * trash_common_unwind_cbk - - */ -int32_t -trash_common_unwind_cbk (call_frame_t *frame, - void *cookie, - xlator_t *this, - int32_t op_ret, - int32_t op_errno, - struct stat *preparent, - struct stat *postparent) -{ - trash_local_t *local = frame->local; - - if (!local) - goto out; - - if (local->loc1.path) - loc_wipe (&local->loc1); - - if (local->loc2.path) - loc_wipe (&local->loc2); - - out: - STACK_UNWIND (frame, op_ret, op_errno); - return 0; -} - - -/** - * trash_rename_unwind_buf_cbk - - */ -int32_t -trash_rename_unwind_buf_cbk (call_frame_t *frame, - void *cookie, - xlator_t *this, - int32_t op_ret, - int32_t op_errno, - struct stat *buf, - struct stat *preoldparent, - struct stat *postoldparent, - struct stat *prenewparent, - struct stat *postnewparent) -{ - trash_local_t *local = frame->local; - - if (!local) - goto out; - - if (local->loc1.path) - loc_wipe (&local->loc1); - - if (local->loc2.path) - loc_wipe (&local->loc2); - - out: - STACK_UNWIND (frame, op_ret, op_errno, buf); - return 0; -} - - -/** - * trash_common_unwind_buf_cbk - - */ -int32_t -trash_common_unwind_buf_cbk (call_frame_t *frame, - void *cookie, - xlator_t *this, - int32_t op_ret, - int32_t op_errno, - struct stat *buf) -{ - trash_local_t *local = frame->local; - - if (!local) - goto out; - - if (local->loc1.path) - loc_wipe (&local->loc1); - - if (local->loc2.path) - loc_wipe (&local->loc2); - - out: - STACK_UNWIND (frame, op_ret, op_errno, buf); - return 0; -} - -int32_t -trash_mkdir_cbk (call_frame_t *frame, - void *cookie, - xlator_t *this, - int32_t op_ret, - int32_t op_errno, - inode_t *inode, - struct stat *stbuf, - struct stat *preparent, - struct stat *postparent) -{ - trash_local_t *local = frame->local; - char *tmp_str = strdup (local->newpath); - int32_t count = 0; - char *tmp_path = NULL; - char *tmp_dirname = NULL; - - if (op_ret == -1 && op_errno == ENOENT) { - tmp_dirname = strchr (tmp_str, '/'); - while (tmp_dirname) { - count = tmp_dirname - tmp_str; - if (count == 0) - count = 1; - tmp_path = CALLOC (1, count + 1); - ERR_ABORT (tmp_path); - memcpy (tmp_path, local->newpath, count); - loc_t tmp_loc = { - .inode = NULL, - .path = tmp_path, - }; - - /* TODO:create the directory with proper permissions */ - STACK_WIND_COOKIE (frame, - trash_mkdir_cbk, - tmp_path, - this->children->xlator, - this->children->xlator->fops->mkdir, - &tmp_loc, - 0777); - tmp_dirname = strchr (tmp_str + count + 1, '/'); - } - free (cookie); - free (tmp_str); - return 0; - } - char *dir_name = dirname (tmp_str); - if (strcmp((char*)cookie, dir_name) == 0) { - loc_t new_loc = { - .inode = NULL, - .path = local->newpath - }; - STACK_WIND (frame, - trash_unlink_rename_cbk, - this->children->xlator, - this->children->xlator->fops->rename, - &local->loc2, - &new_loc); - - } - free (cookie); /* strdup (dir_name) was sent here :) */ - free (tmp_str); - return 0; -} - -/** - * trash_unlink_rename_cbk - - */ -int32_t -trash_unlink_rename_cbk (call_frame_t *frame, - void *cookie, - xlator_t *this, - int32_t op_ret, - int32_t op_errno, - struct stat *buf, - struct stat *preoldparent, - struct stat *postoldparent, - struct stat *prenewparent, - struct stat *postnewparent) -{ - trash_local_t *local = frame->local; - if (op_ret == -1 && op_errno == ENOENT) { - /* check for the errno, if its ENOENT create directory and call - * rename later - */ - char *tmp_str = strdup (local->newpath); - char *dir_name = dirname (tmp_str); - loc_t tmp_loc = { - .inode = NULL, - .path = dir_name, - }; - /* TODO: create the directory with proper permissions */ - STACK_WIND_COOKIE (frame, - trash_mkdir_cbk, - strdup (dir_name), - this->children->xlator, - this->children->xlator->fops->mkdir, - &tmp_loc, - 0777); - free (tmp_str); - } else if (op_ret == -1 && op_errno == ENOTDIR) { - gf_log (this->name, GF_LOG_WARNING, - "Target exists, cannot keep the copy, deleting"); - STACK_WIND (frame, - trash_common_unwind_cbk, - this->children->xlator, - this->children->xlator->fops->unlink, - &local->loc2); - } else if (op_ret == -1 && op_errno == EISDIR) { - gf_log (this->name, GF_LOG_WARNING, - "Target exists as a directory, cannot keep the copy, " - "deleting"); - STACK_WIND (frame, - trash_common_unwind_cbk, - this->children->xlator, - this->children->xlator->fops->unlink, - &local->loc2); - } else { - /* */ - STACK_UNWIND (frame, 0, op_errno); - } - - return 0; -} - - -/** - * trash_unlink - - */ -int32_t -trash_unlink (call_frame_t *frame, - xlator_t *this, - loc_t *loc) -{ - trash_private_t *priv = this->private; - trash_local_t *local = NULL; - time_t utime = 0; - struct tm *tm = NULL; - char timestr[256]; - - if (strncmp (loc->path, priv->trash_dir, - strlen(priv->trash_dir)) == 0) { - /* Trying to rename from the trash can dir, do the - actual unlink */ - STACK_WIND (frame, - trash_common_unwind_cbk, - this->children->xlator, - this->children->xlator->fops->unlink, - loc); - } else { - local = CALLOC (1, sizeof (trash_local_t)); - if (!local) { - STACK_UNWIND (frame, -1, ENOMEM); - return 0; - } - frame->local = local; - - loc_copy (&local->loc2, loc); - - strcpy (local->newpath, priv->trash_dir); - strcat (local->newpath, loc->path); - - utime = time (NULL); - tm = localtime (&utime); - strftime (timestr, 256, ".%Y%m%d%H%M%S", tm); - strcat (local->newpath, timestr); - - { - loc_t new_loc = { - .inode = NULL, - .path = local->newpath - }; - STACK_WIND (frame, - trash_unlink_rename_cbk, - this->children->xlator, - this->children->xlator->fops->rename, - loc, - &new_loc); - } - } - return 0; -} - -/* */ -int32_t -trash_rename_mkdir_cbk (call_frame_t *frame, - void *cookie, - xlator_t *this, - int32_t op_ret, - int32_t op_errno, - inode_t *inode, - struct stat *stbuf, - struct stat *preparent, - struct stat *postparent) -{ - trash_local_t *local = frame->local; - char *tmp_str = strdup (local->newpath); - - if (op_ret == -1 && op_errno == ENOENT) { - int32_t count = 0; - char *tmp_path = NULL; - char *tmp_dirname = strchr (tmp_str, '/'); - - while (tmp_dirname) { - count = tmp_dirname - tmp_str; - if (count == 0) - count = 1; - tmp_path = CALLOC (1, count + 2); - ERR_ABORT (tmp_path); - memcpy (tmp_path, local->newpath, count); - loc_t tmp_loc = { - .inode = NULL, - .path = tmp_path, - }; - - /* TODO:create the directory with proper permissions */ - STACK_WIND_COOKIE (frame, - trash_rename_mkdir_cbk, - tmp_path, - this->children->xlator, - this->children->xlator->fops->mkdir, - &tmp_loc, - 0777); - tmp_dirname = strchr (tmp_str + count + 1, '/'); - } - free (cookie); - free (tmp_str); - return 0; - } - char *dir_name = dirname (tmp_str); - if (strcmp((char*)cookie, dir_name) == 0) { - loc_t new_loc = { - .inode = NULL, - .path = local->newpath - }; - STACK_WIND (frame, - trash_rename_rename_cbk, - this->children->xlator, - this->children->xlator->fops->rename, - &local->loc2, - &new_loc); - - } - free (cookie); /* strdup (dir_name) was sent here :) */ - free (tmp_str); - return 0; -} - - -/** - * trash_unlink_rename_cbk - - */ -int32_t -trash_rename_rename_cbk (call_frame_t *frame, - void *cookie, - xlator_t *this, - int32_t op_ret, - int32_t op_errno, - struct stat *buf, - struct stat *preoldparent, - struct stat *postoldparent, - struct stat *prenewparent, - struct stat *postnewparent) -{ - trash_local_t *local = frame->local; - if (op_ret == -1 && op_errno == ENOENT) { - /* check for the errno, if its ENOENT create directory and call - * rename later - */ - char *tmp_str = strdup (local->newpath); - char *dir_name = dirname (tmp_str); - loc_t tmp_loc = { - .inode = NULL, - .path = dir_name, - }; - /* TODO: create the directory with proper permissions */ - STACK_WIND_COOKIE (frame, - trash_rename_mkdir_cbk, - strdup (dir_name), - this->children->xlator, - this->children->xlator->fops->mkdir, - &tmp_loc, - 0777); - free (tmp_str); - return 0; - } else if (op_ret == -1 && op_errno == ENOTDIR) { - gf_log (this->name, GF_LOG_WARNING, - "Target exists, cannot keep the dest entry %s, " - "renaming", - local->loc2.path); - } else if (op_ret == -1 && op_errno == EISDIR) { - gf_log (this->name, GF_LOG_WARNING, - "Target exists as a directory, cannot keep the " - "copy %s, renaming", - local->loc2.path); - } - loc_t new_loc = { - .inode = NULL, - .parent = local->loc2.parent, - .path = local->loc2.path, - }; - STACK_WIND (frame, - trash_rename_unwind_buf_cbk, - this->children->xlator, - this->children->xlator->fops->rename, - &local->loc1, - &new_loc); - - return 0; -} - -/** - * trash_rename_lookup_cbk - - */ -int32_t -trash_rename_lookup_cbk (call_frame_t *frame, - void *cookie, - xlator_t *this, - int32_t op_ret, - int32_t op_errno, - inode_t *inode, - struct stat *buf, - dict_t *xattr, - struct stat *postparent) -{ - trash_local_t *local = frame->local; - - if (op_ret == -1) { - STACK_WIND (frame, - trash_rename_unwind_buf_cbk, - this->children->xlator, - this->children->xlator->fops->rename, - &local->loc1, - &local->loc2); - return 0; - } - - loc_t oldloc = { - .parent = local->loc2.parent, - .inode = inode, - .path = local->loc2.path, - }; - loc_t newloc = { - .inode = NULL, - .path = local->newpath - }; - STACK_WIND (frame, - trash_rename_rename_cbk, - this->children->xlator, - this->children->xlator->fops->rename, - &oldloc, - &newloc); - - return 0; -} - - -/** - * trash_rename - - */ -int32_t -trash_rename (call_frame_t *frame, - xlator_t *this, - loc_t *oldloc, - loc_t *newloc) -{ - trash_private_t *priv = this->private; - trash_local_t *local = NULL; - time_t utime = 0; - struct tm *tm = NULL; - char timestr[256]; - - if (strncmp (oldloc->path, priv->trash_dir, - strlen(priv->trash_dir)) == 0) { - /* Trying to rename from the trash can dir, - do the actual rename */ - STACK_WIND (frame, - trash_rename_unwind_buf_cbk, - this->children->xlator, - this->children->xlator->fops->rename, - oldloc, - newloc); - } else { - /* Trying to rename a regular file from GlusterFS */ - local = CALLOC (1, sizeof (trash_local_t)); - if (!local) { - STACK_UNWIND (frame, -1, ENOMEM, NULL); - return 0; - } - frame->local = local; - loc_copy (&local->loc1, oldloc); - loc_copy (&local->loc2, newloc); - - strcpy (local->newpath, priv->trash_dir); - strcat (local->newpath, newloc->path); - - utime = time (NULL); - tm = localtime (&utime); - strftime (timestr, 256, ".%Y%m%d%H%M%S", tm); - strcat (local->newpath, timestr); - - /* Send a lookup call on newloc, to ensure we are not - overwriting */ - STACK_WIND (frame, - trash_rename_lookup_cbk, - this->children->xlator, - this->children->xlator->fops->lookup, - newloc, - 0); - } - return 0; -} +#include "trash.h" /** * trash_init - @@ -579,70 +30,115 @@ trash_rename (call_frame_t *frame, int32_t init (xlator_t *this) { - data_t *trash_dir = NULL; - xlator_list_t *trav = NULL; - trash_private_t *_priv = NULL; - - /* Create .trashcan directory in init */ - if (!this->children || this->children->next) { - gf_log (this->name, GF_LOG_ERROR, - "not configured with exactly one child. exiting"); - return -1; - } - - if (!this->parents) { - gf_log (this->name, GF_LOG_WARNING, - "dangling volume. check volfile "); - } - - trav = this->children; - while (trav->xlator->children) - trav = trav->xlator->children; - - if (strncmp ("storage/", trav->xlator->type, 8)) - { - gf_log (this->name, GF_LOG_ERROR, - "'trash' translator not loaded over storage " - "translator, not a supported setup"); - return -1; - } - - _priv = CALLOC (1, sizeof (*_priv)); - ERR_ABORT (_priv); - - trash_dir = dict_get (this->options, "trash-dir"); - if (!trash_dir) { - gf_log (this->name, GF_LOG_WARNING, - "no option specified for 'trash-dir', " - "using \"/.trashcan/\""); - strcpy (_priv->trash_dir, "/.trashcan"); - } else { - /* Need a path with '/' as the first char, if not - given, append it */ - if (trash_dir->data[0] == '/') { - strcpy (_priv->trash_dir, trash_dir->data); - } else { - strcpy (_priv->trash_dir, "/"); - strcat (_priv->trash_dir, trash_dir->data); - } - } - - this->private = (void *)_priv; - return 0; + int32_t ret = 0; + data_t *data = NULL; + trash_private_t *_priv = NULL; + trash_elim_pattern_t *trav = NULL; + char *tmp_str = NULL; + char *strtokptr = NULL; + char *component = NULL; + char trash_dir[PATH_MAX] = {0,}; + + /* Create .trashcan directory in init */ + if (!this->children || this->children->next) { + gf_log (this->name, GF_LOG_ERROR, + "not configured with exactly one child. exiting"); + return -1; + } + + if (!this->parents) { + gf_log (this->name, GF_LOG_WARNING, + "dangling volume. check volfile "); + } + + _priv = CALLOC (1, sizeof (*_priv)); + if (!_priv) { + gf_log (this->name, GF_LOG_ERROR, "out of memory"); + return -1; + } + + data = dict_get (this->options, "trash-dir"); + if (!data) { + gf_log (this->name, GF_LOG_NORMAL, + "no option specified for 'trash-dir', " + "using \"/.trashcan/\""); + _priv->trash_dir = strdup ("/.trashcan"); + } else { + /* Need a path with '/' as the first char, if not + given, append it */ + if (data->data[0] == '/') { + _priv->trash_dir = strdup (data->data); + } else { + /* TODO: Make sure there is no ".." in the path */ + strcpy (trash_dir, "/"); + strcat (trash_dir, data->data); + _priv->trash_dir = strdup (trash_dir); + } + } + + data = dict_get (this->options, "eliminate-pattern"); + if (!data) { + gf_log (this->name, GF_LOG_TRACE, + "no option specified for 'eliminate', using NULL"); + } else { + tmp_str = strdup (data->data); + if (!tmp_str) { + gf_log (this->name, GF_LOG_DEBUG, "out of memory"); + } + + /* Match Filename to option specified in eliminate. */ + component = strtok_r (tmp_str, "|", &strtokptr); + while (component) { + trav = CALLOC (1, sizeof (*trav)); + if (!trav) { + gf_log (this->name, GF_LOG_DEBUG, "out of memory"); + } + trav->pattern = component; + trav->next = _priv->eliminate; + _priv->eliminate = trav; + + component = strtok_r (NULL, "|", &strtokptr); + } + } + + /* TODO: do gf_string2sizet () */ + data = dict_get (this->options, "max-trashable-file-size"); + if (!data) { + gf_log (this->name, GF_LOG_DEBUG, + "no option specified for 'max-trashable-file-size', " + "using default = %lld MB", + GF_DEFAULT_MAX_FILE_SIZE / GF_UNIT_MB); + _priv->max_trash_file_size = GF_DEFAULT_MAX_FILE_SIZE; + } else { + ret = gf_string2bytesize (data->data, + &_priv->max_trash_file_size); + if( _priv->max_trash_file_size > GF_ALLOWED_MAX_FILE_SIZE ) { + gf_log (this->name, GF_LOG_DEBUG, + "Size specified for max-size(in MB) is too " + "large so using 1GB as max-size (NOT IDEAL)"); + _priv->max_trash_file_size = GF_ALLOWED_MAX_FILE_SIZE; + } + gf_log (this->name, GF_LOG_DEBUG, "%"GF_PRI_SIZET" max-size", + _priv->max_trash_file_size); + } + + this->private = (void *)_priv; + return 0; } void fini (xlator_t *this) { - trash_private_t *priv = this->private; - FREE (priv); - return; -} + trash_private_t *priv = NULL; + + priv = this->private; + if (priv) + FREE (priv); + return; +} struct xlator_fops fops = { - .unlink = trash_unlink, - .rename = trash_rename, }; struct xlator_mops mops = { @@ -653,8 +149,14 @@ struct xlator_cbks cbks = { }; struct volume_options options[] = { - { .key = { "trash-dir" }, - .type = GF_OPTION_TYPE_PATH - }, - { .key = {NULL} }, + { .key = { "trash-directory" }, + .type = GF_OPTION_TYPE_PATH, + }, + { .key = { "eliminate-pattern" }, + .type = GF_OPTION_TYPE_STR, + }, + { .key = { "max-trashable-file-size" }, + .type = GF_OPTION_TYPE_SIZET, + }, + { .key = {NULL} }, }; diff --git a/xlators/features/trash/src/trash.h b/xlators/features/trash/src/trash.h new file mode 100644 index 00000000000..7f0e13085b1 --- /dev/null +++ b/xlators/features/trash/src/trash.h @@ -0,0 +1,80 @@ +/* + Copyright (c) 2006, 2007, 2008 Z RESEARCH, Inc. <http://www.zresearch.com> + This file is part of GlusterFS. + + GlusterFS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + GlusterFS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + <http://www.gnu.org/licenses/>. +*/ + +#ifndef __TRASH_H__ +#define __TRASH_H__ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "glusterfs.h" +#include "logging.h" +#include "dict.h" +#include "xlator.h" +#include "defaults.h" +#include "inode.c" +#include "fnmatch.h" + +#include <libgen.h> + +#ifndef GF_BLOCK_READV_SIZE +#define GF_BLOCK_READV_SIZE (128 * GF_UNIT_KB) +#endif + +#ifndef GF_DEFAULT_MAX_FILE_SIZE +#define GF_DEFAULT_MAX_FILE_SIZE (200 * GF_UNIT_MB) +#endif + +#ifndef GF_ALLOWED_MAX_FILE_SIZE +#define GF_ALLOWED_MAX_FILE_SIZE (1 * GF_UNIT_GB) +#endif + + +struct trash_struct { + fd_t *fd; /* for the fd of existing file */ + fd_t *newfd; /* for the newly created file */ + loc_t loc; /* to store the location of the existing file */ + loc_t newloc; /* to store the location for the new file */ + size_t fsize; /* for keeping the size of existing file */ + off_t cur_offset; /* current offset for read and write ops */ + off_t fop_offset; + char origpath[PATH_MAX]; + char newpath[PATH_MAX]; + int32_t loop_count; + struct stat preparent; + struct stat postparent; +}; +typedef struct trash_struct trash_local_t; + +struct _trash_elim_pattern; +typedef struct _trash_elim_pattern { + struct _trash_elim_pattern *next; + char *pattern; +} trash_elim_pattern_t; + +struct trash_priv { + char *trash_dir; + trash_elim_pattern_t *eliminate; + size_t max_trash_file_size; +}; +typedef struct trash_priv trash_private_t; + +#endif /* __TRASH_H__ */ |