diff options
Diffstat (limited to 'source3/smbd/close.c')
-rw-r--r-- | source3/smbd/close.c | 260 |
1 files changed, 258 insertions, 2 deletions
diff --git a/source3/smbd/close.c b/source3/smbd/close.c index afba07f1f5..05c3c709a1 100644 --- a/source3/smbd/close.c +++ b/source3/smbd/close.c @@ -656,6 +656,263 @@ static NTSTATUS close_normal_file(struct smb_request *req, files_struct *fsp, file_free(req, fsp); return status; } +/**************************************************************************** + Static function used by reply_rmdir to delete an entire directory + tree recursively. Return True on ok, False on fail. +****************************************************************************/ + +static bool recursive_rmdir(TALLOC_CTX *ctx, + connection_struct *conn, + struct smb_filename *smb_dname) +{ + const char *dname = NULL; + char *talloced = NULL; + bool ret = True; + long offset = 0; + SMB_STRUCT_STAT st; + struct smb_Dir *dir_hnd; + + SMB_ASSERT(!is_ntfs_stream_smb_fname(smb_dname)); + + dir_hnd = OpenDir(talloc_tos(), conn, smb_dname->base_name, NULL, 0); + if(dir_hnd == NULL) + return False; + + while((dname = ReadDirName(dir_hnd, &offset, &st, &talloced))) { + struct smb_filename *smb_dname_full = NULL; + char *fullname = NULL; + bool do_break = true; + NTSTATUS status; + + if (ISDOT(dname) || ISDOTDOT(dname)) { + TALLOC_FREE(talloced); + continue; + } + + if (!is_visible_file(conn, smb_dname->base_name, dname, &st, + false)) { + TALLOC_FREE(talloced); + continue; + } + + /* Construct the full name. */ + fullname = talloc_asprintf(ctx, + "%s/%s", + smb_dname->base_name, + dname); + if (!fullname) { + errno = ENOMEM; + goto err_break; + } + + status = create_synthetic_smb_fname(talloc_tos(), fullname, + NULL, NULL, + &smb_dname_full); + if (!NT_STATUS_IS_OK(status)) { + goto err_break; + } + + if(SMB_VFS_LSTAT(conn, smb_dname_full) != 0) { + goto err_break; + } + + if(smb_dname_full->st.st_ex_mode & S_IFDIR) { + if(!recursive_rmdir(ctx, conn, smb_dname_full)) { + goto err_break; + } + if(SMB_VFS_RMDIR(conn, + smb_dname_full->base_name) != 0) { + goto err_break; + } + } else if(SMB_VFS_UNLINK(conn, smb_dname_full) != 0) { + goto err_break; + } + + /* Successful iteration. */ + do_break = false; + + err_break: + TALLOC_FREE(smb_dname_full); + TALLOC_FREE(fullname); + TALLOC_FREE(talloced); + if (do_break) { + ret = false; + break; + } + } + TALLOC_FREE(dir_hnd); + return ret; +} + +/**************************************************************************** + The internals of the rmdir code - called elsewhere. +****************************************************************************/ + +static NTSTATUS rmdir_internals(TALLOC_CTX *ctx, files_struct *fsp) +{ + connection_struct *conn = fsp->conn; + struct smb_filename *smb_dname = fsp->fsp_name; + int ret; + + SMB_ASSERT(!is_ntfs_stream_smb_fname(smb_dname)); + + /* Might be a symlink. */ + if(SMB_VFS_LSTAT(conn, smb_dname) != 0) { + return map_nt_error_from_unix(errno); + } + + if (S_ISLNK(smb_dname->st.st_ex_mode)) { + /* Is what it points to a directory ? */ + if(SMB_VFS_STAT(conn, smb_dname) != 0) { + return map_nt_error_from_unix(errno); + } + if (!(S_ISDIR(smb_dname->st.st_ex_mode))) { + return NT_STATUS_NOT_A_DIRECTORY; + } + ret = SMB_VFS_UNLINK(conn, smb_dname); + } else { + ret = SMB_VFS_RMDIR(conn, smb_dname->base_name); + } + if (ret == 0) { + notify_fname(conn, NOTIFY_ACTION_REMOVED, + FILE_NOTIFY_CHANGE_DIR_NAME, + smb_dname->base_name); + return NT_STATUS_OK; + } + + if(((errno == ENOTEMPTY)||(errno == EEXIST)) && lp_veto_files(SNUM(conn))) { + /* + * Check to see if the only thing in this directory are + * vetoed files/directories. If so then delete them and + * retry. If we fail to delete any of them (and we *don't* + * do a recursive delete) then fail the rmdir. + */ + SMB_STRUCT_STAT st; + const char *dname = NULL; + char *talloced = NULL; + long dirpos = 0; + struct smb_Dir *dir_hnd = OpenDir(talloc_tos(), conn, + smb_dname->base_name, NULL, + 0); + + if(dir_hnd == NULL) { + errno = ENOTEMPTY; + goto err; + } + + while ((dname = ReadDirName(dir_hnd, &dirpos, &st, + &talloced)) != NULL) { + if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) { + TALLOC_FREE(talloced); + continue; + } + if (!is_visible_file(conn, smb_dname->base_name, dname, + &st, false)) { + TALLOC_FREE(talloced); + continue; + } + if(!IS_VETO_PATH(conn, dname)) { + TALLOC_FREE(dir_hnd); + TALLOC_FREE(talloced); + errno = ENOTEMPTY; + goto err; + } + TALLOC_FREE(talloced); + } + + /* We only have veto files/directories. + * Are we allowed to delete them ? */ + + if(!lp_recursive_veto_delete(SNUM(conn))) { + TALLOC_FREE(dir_hnd); + errno = ENOTEMPTY; + goto err; + } + + /* Do a recursive delete. */ + RewindDir(dir_hnd,&dirpos); + while ((dname = ReadDirName(dir_hnd, &dirpos, &st, + &talloced)) != NULL) { + struct smb_filename *smb_dname_full = NULL; + char *fullname = NULL; + bool do_break = true; + NTSTATUS status; + + if (ISDOT(dname) || ISDOTDOT(dname)) { + TALLOC_FREE(talloced); + continue; + } + if (!is_visible_file(conn, smb_dname->base_name, dname, + &st, false)) { + TALLOC_FREE(talloced); + continue; + } + + fullname = talloc_asprintf(ctx, + "%s/%s", + smb_dname->base_name, + dname); + + if(!fullname) { + errno = ENOMEM; + goto err_break; + } + + status = create_synthetic_smb_fname(talloc_tos(), + fullname, NULL, + NULL, + &smb_dname_full); + if (!NT_STATUS_IS_OK(status)) { + errno = map_errno_from_nt_status(status); + goto err_break; + } + + if(SMB_VFS_LSTAT(conn, smb_dname_full) != 0) { + goto err_break; + } + if(smb_dname_full->st.st_ex_mode & S_IFDIR) { + if(!recursive_rmdir(ctx, conn, + smb_dname_full)) { + goto err_break; + } + if(SMB_VFS_RMDIR(conn, + smb_dname_full->base_name) != 0) { + goto err_break; + } + } else if(SMB_VFS_UNLINK(conn, smb_dname_full) != 0) { + goto err_break; + } + + /* Successful iteration. */ + do_break = false; + + err_break: + TALLOC_FREE(fullname); + TALLOC_FREE(smb_dname_full); + TALLOC_FREE(talloced); + if (do_break) + break; + } + TALLOC_FREE(dir_hnd); + /* Retry the rmdir */ + ret = SMB_VFS_RMDIR(conn, smb_dname->base_name); + } + + err: + + if (ret != 0) { + DEBUG(3,("rmdir_internals: couldn't remove directory %s : " + "%s\n", smb_fname_str_dbg(smb_dname), + strerror(errno))); + return map_nt_error_from_unix(errno); + } + + notify_fname(conn, NOTIFY_ACTION_REMOVED, + FILE_NOTIFY_CHANGE_DIR_NAME, + smb_dname->base_name); + + return NT_STATUS_OK; +} /**************************************************************************** Close a directory opened by an NT SMB call. @@ -742,8 +999,7 @@ static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp, TALLOC_FREE(lck); - status = rmdir_internals(talloc_tos(), fsp->conn, - fsp->fsp_name); + status = rmdir_internals(talloc_tos(), fsp); DEBUG(5,("close_directory: %s. Delete on close was set - " "deleting directory returned %s.\n", |