diff options
Diffstat (limited to 'xlators')
-rw-r--r-- | xlators/features/trash/src/trash.c | 445 |
1 files changed, 443 insertions, 2 deletions
diff --git a/xlators/features/trash/src/trash.c b/xlators/features/trash/src/trash.c index ad8d1a7e3fd..2efca78b725 100644 --- a/xlators/features/trash/src/trash.c +++ b/xlators/features/trash/src/trash.c @@ -24,6 +24,18 @@ #include "trash.h" + +int32_t +trash_truncate_writev_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + struct stat *prebuf, struct stat *postbuf); + +int32_t +trash_truncate_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); + 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, @@ -232,6 +244,16 @@ trash_unlink_rename_cbk (call_frame_t *frame, void *cookie, xlator_t *this, } + +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 *prebuf, struct stat *postbuf) +{ + TRASH_STACK_UNWIND (frame, op_ret, op_errno, prebuf, postbuf); + return 0; +} + int trash_common_rename_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct stat *stbuf, @@ -608,6 +630,424 @@ trash_unlink (call_frame_t *frame, xlator_t *this, loc_t *loc) return 0; } +int32_t +trash_truncate_unlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + struct stat *preparent, struct stat *postparent) +{ + /* use this Function when a failure occurs, and + delete the newly created file. */ + trash_local_t *local = NULL; + + local = frame->local; + + if (op_ret == -1) { + gf_log (this->name, GF_LOG_DEBUG, + "deleting the newly created file: %s", + strerror (op_errno)); + } + + STACK_WIND (frame, trash_common_unwind_buf_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->truncate, + &local->loc, local->fop_offset); + + return 0; +} + +int32_t +trash_truncate_readv_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + struct iovec *vector, int32_t count, + struct stat *stbuf, struct iobref *iobuf) +{ + trash_local_t *local = NULL; + + local = frame->local; + + if (op_ret == -1) { + gf_log (this->name, GF_LOG_DEBUG, + "readv on the existing file failed: %s", + strerror (op_errno)); + + STACK_WIND (frame, trash_truncate_unlink_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->unlink, + &local->newloc); + goto out; + } + + local->fsize = stbuf->st_size; + STACK_WIND (frame, trash_truncate_writev_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->writev, + local->newfd, vector, count, local->cur_offset, iobuf); + +out: + return 0; + +} + +int32_t +trash_truncate_writev_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + struct stat *prebuf, struct stat *postbuf) +{ + trash_local_t *local = NULL; + + local = frame->local; + + if (op_ret == -1) { + /* Let truncate work, but previous copy is not preserved. */ + gf_log (this->name, GF_LOG_DEBUG, + "writev on the existing file failed: %s", + strerror (op_errno)); + + STACK_WIND (frame, trash_truncate_unlink_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->unlink, &local->newloc); + goto out; + } + + if (local->cur_offset < local->fsize) { + local->cur_offset += GF_BLOCK_READV_SIZE; + /* Loop back and Read the contents again. */ + STACK_WIND (frame, trash_truncate_readv_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->readv, + local->fd, (size_t)GF_BLOCK_READV_SIZE, + local->cur_offset); + goto out; + } + + + /* OOFH.....Finally calling Truncate. */ + STACK_WIND (frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->truncate, &local->loc, + local->fop_offset); + +out: + return 0; +} + + + +int32_t +trash_truncate_open_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, fd_t *fd) +{ + trash_local_t *local = NULL; + + local = frame->local; + + if (op_ret == -1) { + //Let truncate work, but previous copy is not preserved. + gf_log (this->name, GF_LOG_DEBUG, + "open on the existing file failed: %s", + strerror (op_errno)); + + STACK_WIND (frame, trash_truncate_unlink_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->unlink, + &local->newloc); + goto out; + } + + local->cur_offset = local->fop_offset; + + STACK_WIND (frame, trash_truncate_readv_cbk, + FIRST_CHILD (this), FIRST_CHILD (this)->fops->readv, + local->fd, (size_t)GF_BLOCK_READV_SIZE, local->cur_offset); + +out: + return 0; +} + + +int32_t +trash_truncate_create_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, fd_t *fd, + inode_t *inode, struct stat *buf, + struct stat *preparent, struct stat *postparent) +{ + trash_local_t *local = NULL; + char *tmp_str = NULL; + char *dir_name = NULL; + char *tmp_path = NULL; + int32_t flags = 0; + loc_t tmp_loc = {0,}; + + local = frame->local; + + if ((op_ret == -1) && (op_errno == ENOENT)) { + //Creating the directory structure here. + tmp_str = strdup (local->newpath); + if (!tmp_str) { + gf_log (this->name, GF_LOG_DEBUG, "out of memory"); + } + dir_name = dirname (tmp_str); + + tmp_path = strdup (dir_name); + if (!tmp_path) { + gf_log (this->name, GF_LOG_DEBUG, "out of memory"); + } + tmp_loc.path = tmp_path; + + /* TODO: create the directory with proper permissions */ + STACK_WIND_COOKIE (frame, trash_truncate_mkdir_cbk, + tmp_path, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->mkdir, + &tmp_loc, 0755); + free (tmp_str); + goto out; + } + + if (op_ret == -1) { + //Let truncate work, but previous copy is not preserved. + //Deleting the newly created copy. + gf_log (this->name, GF_LOG_DEBUG, + "creation of new file in trash-dir failed, " + "when truncate was called: %s", strerror (op_errno)); + + STACK_WIND (frame, trash_common_unwind_buf_cbk, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->truncate, &local->loc, + local->fop_offset); + goto out; + } + + flags = O_RDONLY; + + local->fd = fd_create (local->loc.inode, frame->root->pid); + + STACK_WIND (frame, trash_truncate_open_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->open, &local->loc, flags, + local->fd, 0); +out: + return 0; +} + +int32_t +trash_truncate_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 = NULL; + char *tmp_str = NULL; + char *tmp_path = NULL; + char *tmp_dirname = NULL; + char *dir_name = NULL; + int32_t count = 0; + int32_t flags = 0; + int32_t loop_count = 0; + int i = 0; + loc_t tmp_loc = {0,}; + + local = frame->local; + if (!local) + return 0; + + loop_count = local->loop_count; + + tmp_str = strdup (local->newpath); + if (!tmp_str) { + gf_log (this->name, GF_LOG_DEBUG, "out of memory"); + } + + 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; + i++; + if (i > loop_count) + break; + tmp_dirname = strchr (tmp_str + count + 1, '/'); + } + tmp_path = strndup (local->newpath, count); + if (!tmp_path) { + gf_log (this->name, GF_LOG_DEBUG, "out of memory"); + } + tmp_loc.path = tmp_path; + STACK_WIND_COOKIE (frame, trash_truncate_mkdir_cbk, + tmp_path, this->children->xlator, + this->children->xlator->fops->mkdir, + &tmp_loc, 0755); + + goto out; + } + + if (op_ret == 0) { + dir_name = dirname (tmp_str); + if (strcmp ((char*)cookie, dir_name) == 0) { + flags = O_CREAT|O_EXCL|O_WRONLY; + + //Call create again once directory structure is created. + STACK_WIND (frame, trash_truncate_create_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->create, + &local->newloc, flags, local->loc.inode->st_mode, + local->newfd); + goto out; + } + } + + LOCK (&frame->lock); + { + loop_count = ++local->loop_count; + } + UNLOCK (&frame->lock); + tmp_dirname = strchr (tmp_str, '/'); + while (tmp_dirname) { + count = tmp_dirname - tmp_str; + if (count == 0) + count = 1; + + i++; + if ((i > loop_count) || (count > PATH_MAX)) + break; + tmp_dirname = strchr (tmp_str + count + 1, '/'); + } + tmp_path = strndup (local->newpath, count); + if (!tmp_path) { + gf_log (this->name, GF_LOG_DEBUG, "out of memory"); + } + tmp_loc.path = tmp_path; + + STACK_WIND_COOKIE (frame, trash_truncate_mkdir_cbk, tmp_path, + this->children->xlator, + this->children->xlator->fops->mkdir, + &tmp_loc, 0755); + +out: + free (cookie); /* strdup (dir_name) was sent here :) */ + free (tmp_str); + + return 0; +} + + +int32_t +trash_truncate_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct stat *buf) +{ + trash_private_t *priv = NULL; + trash_local_t *local = NULL; + struct tm *tm = NULL; + char timestr[256] = {0,}; + char loc_newname[PATH_MAX] = {0,}; + time_t utime = 0; + int32_t flags = 0; + + priv = this->private; + local = frame->local; + + if (op_ret == -1) { + gf_log (this->name, GF_LOG_DEBUG, + "fstat on the file failed: %s", + strerror (op_errno)); + + TRASH_STACK_UNWIND (frame, op_ret, op_errno, buf); + return 0; + } + + if ((buf->st_size == 0) || (buf->st_size > priv->max_trash_file_size)) { + // If the file is too big, just unlink it. + if (buf->st_size > priv->max_trash_file_size) + gf_log (this->name, GF_LOG_DEBUG, "%s: file too big, " + "not moving to trash", local->loc.path); + + STACK_WIND (frame, trash_common_unwind_buf_cbk, + this->children->xlator, + this->children->xlator->fops->truncate, + &local->loc, local->fop_offset); + return 0; + } + + strcpy (local->newpath, priv->trash_dir); + strcat (local->newpath, local->loc.path); + + { + utime = time (NULL); + tm = localtime (&utime); + strftime (timestr, 256, ".%Y-%m-%d-%H%M%S", tm); + strcat (local->newpath, timestr); + } + strcpy (loc_newname,local->loc.name); + strcat (loc_newname,timestr); + + local->newloc.name = strdup (loc_newname); + local->newloc.path = strdup (local->newpath); + local->newloc.inode = inode_new (local->loc.inode->table); + local->newloc.ino = local->newloc.inode->ino; + local->newfd = fd_create (local->newloc.inode, frame->root->pid); + + flags = O_CREAT|O_EXCL|O_WRONLY; + + STACK_WIND (frame, trash_truncate_create_cbk, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->create, + &local->newloc, flags, local->loc.inode->st_mode, + local->newfd); + + return 0; +} + +int32_t +trash_truncate (call_frame_t *frame, xlator_t *this, loc_t *loc, + off_t offset) +{ + trash_elim_pattern_t *trav = NULL; + trash_private_t *priv = NULL; + trash_local_t *local = NULL; + int32_t match = 0; + + priv = this->private; + if (priv->eliminate) { + trav = priv->eliminate; + while (trav) { + if (fnmatch(trav->pattern, loc->name, 0) == 0) { + match++; + break; + } + trav = trav->next; + } + } + + if ((strncmp (loc->path, priv->trash_dir, + strlen (priv->trash_dir)) == 0) || (offset) || (match)) { + if (match) { + gf_log (this->name, GF_LOG_DEBUG, + "%s: file not moved to trash as per option " + "'eliminate'", loc->path); + } + + // Trying to truncate from the trash can dir, + // do the actual truncate without moving to trash-dir. + STACK_WIND (frame, trash_common_unwind_buf_cbk, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->truncate, loc, offset); + goto out; + } + + LOCK_INIT (&frame->lock); + + local = CALLOC (1, sizeof (trash_local_t)); + if (!local) { + gf_log (this->name, GF_LOG_DEBUG, "out of memory"); + TRASH_STACK_UNWIND (frame, -1, ENOMEM, NULL); + return 0; + } + + loc_copy (&local->loc, loc); + + local->fop_offset = offset; + + frame->local = local; + + STACK_WIND (frame, trash_truncate_stat_cbk, + this->children->xlator, + this->children->xlator->fops->stat, loc); + +out: + return 0; +} + /** * trash_init - */ @@ -723,8 +1163,9 @@ fini (xlator_t *this) } struct xlator_fops fops = { - .unlink = trash_unlink, - .rename = trash_rename, + .unlink = trash_unlink, + .rename = trash_rename, + .truncate = trash_truncate, }; struct xlator_mops mops = { |