int onefs_vtimes_streams(vfs_handle_struct *handle, const struct smb_filename *smb_fname, int flags, struct timespec times[3]) { struct smb_filename *smb_fname_onefs = NULL; int ret; int dirfd; int saved_errno; NTSTATUS status; START_PROFILE(syscall_ntimes); if (!is_ntfs_stream_smb_fname(smb_fname)) { ret = vtimes(smb_fname->base_name, times, flags); return ret; } status = onefs_stream_prep_smb_fname(talloc_tos(), smb_fname, &smb_fname_onefs); if (!NT_STATUS_IS_OK(status)) { errno = map_errno_from_nt_status(status); return -1; } /* Default stream (the ::$DATA was just stripped off). */ if (!is_ntfs_stream_smb_fname(smb_fname_onefs)) { ret = vtimes(smb_fname_onefs->base_name, times, flags); goto out; } dirfd = get_stream_dir_fd(handle->conn, smb_fname->base_name, NULL); if (dirfd < -1) { ret = -1; goto out; } ret = enc_vtimesat(dirfd, smb_fname_onefs->stream_name, ENC_DEFAULT, times, flags); saved_errno = errno; close(dirfd); errno = saved_errno; out: END_PROFILE(syscall_ntimes); TALLOC_FREE(smb_fname_onefs); return ret; }
int onefs_lstat(vfs_handle_struct *handle, struct smb_filename *smb_fname) { struct smb_filename *smb_fname_onefs = NULL; NTSTATUS status; int ret; status = onefs_stream_prep_smb_fname(talloc_tos(), smb_fname, &smb_fname_onefs); if (!NT_STATUS_IS_OK(status)) { errno = map_errno_from_nt_status(status); return -1; } /* * If the smb_fname has no stream or is :$DATA, then just stat the * base stream. Otherwise stat the stream. */ if (!is_ntfs_stream_smb_fname(smb_fname_onefs)) { ret = onefs_sys_lstat(smb_fname_onefs->base_name, &smb_fname->st); } else { ret = stat_stream(handle->conn, smb_fname_onefs->base_name, smb_fname_onefs->stream_name, &smb_fname->st, AT_SYMLINK_NOFOLLOW); } onefs_adjust_stat_time(handle->conn, smb_fname->base_name, &smb_fname->st); TALLOC_FREE(smb_fname_onefs); return ret; }
/**************************************************************************** Returns true if the filename's stream == "::$DATA" ***************************************************************************/ bool is_ntfs_default_stream_smb_fname(const struct smb_filename *smb_fname) { if (!is_ntfs_stream_smb_fname(smb_fname)) { return false; } return strcasecmp_m(smb_fname->stream_name, "::$DATA") == 0; }
static int streams_xattr_stat(vfs_handle_struct *handle, struct smb_filename *smb_fname) { NTSTATUS status; int result = -1; char *xattr_name = NULL; if (!is_ntfs_stream_smb_fname(smb_fname)) { return SMB_VFS_NEXT_STAT(handle, smb_fname); } /* Note if lp_posix_paths() is true, we can never * get here as is_ntfs_stream_smb_fname() is * always false. So we never need worry about * not following links here. */ /* If the default stream is requested, just stat the base file. */ if (is_ntfs_default_stream_smb_fname(smb_fname)) { return streams_xattr_stat_base(handle, smb_fname, true); } /* Populate the stat struct with info from the base file. */ if (streams_xattr_stat_base(handle, smb_fname, true) == -1) { return -1; } /* Derive the xattr name to lookup. */ status = streams_xattr_get_name(handle, talloc_tos(), smb_fname->stream_name, &xattr_name); if (!NT_STATUS_IS_OK(status)) { errno = map_errno_from_nt_status(status); return -1; } /* Augment the base file's stat information before returning. */ smb_fname->st.st_ex_size = get_xattr_size(handle->conn, smb_fname, xattr_name); if (smb_fname->st.st_ex_size == -1) { SET_STAT_INVALID(smb_fname->st); errno = ENOENT; result = -1; goto fail; } smb_fname->st.st_ex_ino = stream_inode(&smb_fname->st, xattr_name); smb_fname->st.st_ex_mode &= ~S_IFMT; smb_fname->st.st_ex_mode &= ~S_IFDIR; smb_fname->st.st_ex_mode |= S_IFREG; smb_fname->st.st_ex_blocks = smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1; result = 0; fail: TALLOC_FREE(xattr_name); return result; }
int onefs_unlink(vfs_handle_struct *handle, const struct smb_filename *smb_fname) { struct smb_filename *smb_fname_onefs = NULL; int ret; int dir_fd, saved_errno; NTSTATUS status; /* Not a stream. */ if (!is_ntfs_stream_smb_fname(smb_fname)) { return SMB_VFS_NEXT_UNLINK(handle, smb_fname); } status = onefs_stream_prep_smb_fname(talloc_tos(), smb_fname, &smb_fname_onefs); if (!NT_STATUS_IS_OK(status)) { errno = map_errno_from_nt_status(status); return -1; } /* Default stream (the ::$DATA was just stripped off). */ if (!is_ntfs_stream_smb_fname(smb_fname_onefs)) { ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname_onefs); goto out; } dir_fd = get_stream_dir_fd(handle->conn, smb_fname_onefs->base_name, NULL); if (dir_fd < 0) { ret = -1; goto out; } ret = enc_unlinkat(dir_fd, smb_fname_onefs->stream_name, ENC_DEFAULT, 0); saved_errno = errno; close(dir_fd); errno = saved_errno; out: TALLOC_FREE(smb_fname_onefs); return ret; }
NTSTATUS can_set_delete_on_close(files_struct *fsp, uint32 dosmode) { /* * Only allow delete on close for writable files. */ if ((dosmode & FILE_ATTRIBUTE_READONLY) && !lp_delete_readonly(SNUM(fsp->conn))) { DEBUG(10,("can_set_delete_on_close: file %s delete on close " "flag set but file attribute is readonly.\n", fsp_str_dbg(fsp))); return NT_STATUS_CANNOT_DELETE; } /* * Only allow delete on close for writable shares. */ if (!CAN_WRITE(fsp->conn)) { DEBUG(10,("can_set_delete_on_close: file %s delete on " "close flag set but write access denied on share.\n", fsp_str_dbg(fsp))); return NT_STATUS_ACCESS_DENIED; } /* * Only allow delete on close for files/directories opened with delete * intent. */ if (!(fsp->access_mask & DELETE_ACCESS)) { DEBUG(10,("can_set_delete_on_close: file %s delete on " "close flag set but delete access denied.\n", fsp_str_dbg(fsp))); return NT_STATUS_ACCESS_DENIED; } /* Don't allow delete on close for non-empty directories. */ if (fsp->is_directory) { SMB_ASSERT(!is_ntfs_stream_smb_fname(fsp->fsp_name)); /* Or the root of a share. */ if (ISDOT(fsp->fsp_name->base_name)) { DEBUG(10,("can_set_delete_on_close: can't set delete on " "close for the root of a share.\n")); return NT_STATUS_ACCESS_DENIED; } return can_delete_directory(fsp->conn, fsp->fsp_name->base_name); } return NT_STATUS_OK; }
static int streams_xattr_unlink(vfs_handle_struct *handle, const struct smb_filename *smb_fname) { NTSTATUS status; int ret = -1; char *xattr_name = NULL; if (!is_ntfs_stream_smb_fname(smb_fname)) { return SMB_VFS_NEXT_UNLINK(handle, smb_fname); } /* If the default stream is requested, just open the base file. */ if (is_ntfs_default_stream_smb_fname(smb_fname)) { struct smb_filename *smb_fname_base = NULL; smb_fname_base = cp_smb_filename(talloc_tos(), smb_fname); if (smb_fname_base == NULL) { errno = ENOMEM; return -1; } ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname_base); TALLOC_FREE(smb_fname_base); return ret; } status = streams_xattr_get_name(handle, talloc_tos(), smb_fname->stream_name, &xattr_name); if (!NT_STATUS_IS_OK(status)) { errno = map_errno_from_nt_status(status); goto fail; } ret = SMB_VFS_REMOVEXATTR(handle->conn, smb_fname, xattr_name); if ((ret == -1) && (errno == ENOATTR)) { errno = ENOENT; goto fail; } ret = 0; fail: TALLOC_FREE(xattr_name); return ret; }
static int streams_xattr_lstat(vfs_handle_struct *handle, struct smb_filename *smb_fname) { NTSTATUS status; int result = -1; char *xattr_name = NULL; if (!is_ntfs_stream_smb_fname(smb_fname)) { return SMB_VFS_NEXT_LSTAT(handle, smb_fname); } /* If the default stream is requested, just stat the base file. */ if (is_ntfs_default_stream_smb_fname(smb_fname)) { return streams_xattr_stat_base(handle, smb_fname, false); } /* Populate the stat struct with info from the base file. */ if (streams_xattr_stat_base(handle, sm
bool set_delete_on_close(files_struct *fsp, bool delete_on_close, const struct security_token *nt_tok, const struct security_unix_token *tok) { struct share_mode_lock *lck; DEBUG(10,("set_delete_on_close: %s delete on close flag for " "%s, file %s\n", delete_on_close ? "Adding" : "Removing", fsp_fnum_dbg(fsp), fsp_str_dbg(fsp))); lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id); if (lck == NULL) { return False; } if (delete_on_close) { set_delete_on_close_lck(fsp, lck, true, nt_tok, tok); } else { set_delete_on_close_lck(fsp, lck, false, NULL, NULL); } if (fsp->is_directory) { SMB_ASSERT(!is_ntfs_stream_smb_fname(fsp->fsp_name)); send_stat_cache_delete_message(fsp->conn->sconn->msg_ctx, fsp->fsp_name->base_name); } TALLOC_FREE(lck); fsp->delete_on_close = delete_on_close; return True; }
NTSTATUS vfs_default_durable_cookie(struct files_struct *fsp, TALLOC_CTX *mem_ctx, DATA_BLOB *cookie_blob) { struct connection_struct *conn = fsp->conn; enum ndr_err_code ndr_err; struct vfs_default_durable_cookie cookie; if (!lp_durable_handles(SNUM(conn))) { return NT_STATUS_NOT_SUPPORTED; } if (lp_kernel_share_modes(SNUM(conn))) { /* * We do not support durable handles * if kernel share modes (flocks) are used */ return NT_STATUS_NOT_SUPPORTED; } if (lp_kernel_oplocks(SNUM(conn))) { /* * We do not support durable handles * if kernel oplocks are used */ return NT_STATUS_NOT_SUPPORTED; } if ((fsp->current_lock_count > 0) && lp_posix_locking(fsp->conn->params)) { /* * We do not support durable handles * if the handle has posix locks. */ return NT_STATUS_NOT_SUPPORTED; } if (fsp->is_directory) { return NT_STATUS_NOT_SUPPORTED; } if (fsp->fh->fd == -1) { return NT_STATUS_NOT_SUPPORTED; } if (is_ntfs_stream_smb_fname(fsp->fsp_name)) { /* * We do not support durable handles * on streams for now. */ return NT_STATUS_NOT_SUPPORTED; } if (is_fake_file(fsp->fsp_name)) { /* * We do not support durable handles * on fake files. */ return NT_STATUS_NOT_SUPPORTED; } ZERO_STRUCT(cookie); cookie.allow_reconnect = false; cookie.id = fsp->file_id; cookie.servicepath = conn->connectpath; cookie.base_name = fsp->fsp_name->base_name; cookie.initial_allocation_size = fsp->initial_allocation_size; cookie.position_information = fsp->fh->position_information; cookie.update_write_time_triggered = fsp->update_write_time_triggered; cookie.update_write_time_on_close = fsp->update_write_time_on_close; cookie.write_time_forced = fsp->write_time_forced; cookie.close_write_time = fsp->close_write_time; cookie.stat_info.st_ex_dev = fsp->fsp_name->st.st_ex_dev; cookie.stat_info.st_ex_ino = fsp->fsp_name->st.st_ex_ino; cookie.stat_info.st_ex_mode = fsp->fsp_name->st.st_ex_mode; cookie.stat_info.st_ex_nlink = fsp->fsp_name->st.st_ex_nlink; cookie.stat_info.st_ex_uid = fsp->fsp_name->st.st_ex_uid; cookie.stat_info.st_ex_gid = fsp->fsp_name->st.st_ex_gid; cookie.stat_info.st_ex_rdev = fsp->fsp_name->st.st_ex_rdev; cookie.stat_info.st_ex_size = fsp->fsp_name->st.st_ex_size; cookie.stat_info.st_ex_atime = fsp->fsp_name->st.st_ex_atime; cookie.stat_info.st_ex_mtime = fsp->fsp_name->st.st_ex_mtime; cookie.stat_info.st_ex_ctime = fsp->fsp_name->st.st_ex_ctime; cookie.stat_info.st_ex_btime = fsp->fsp_name->st.st_ex_btime; cookie.stat_info.st_ex_calculated_birthtime = fsp->fsp_name->st.st_ex_calculated_birthtime; cookie.stat_info.st_ex_blksize = fsp->fsp_name->st.st_ex_blksize; cookie.stat_info.st_ex_blocks = fsp->fsp_name->st.st_ex_blocks; cookie.stat_info.st_ex_flags = fsp->fsp_name->st.st_ex_flags; cookie.stat_info.st_ex_mask = fsp->fsp_name->st.st_ex_mask; ndr_err = ndr_push_struct_blob(cookie_blob, mem_ctx, &cookie, (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { NTSTATUS status = ndr_map_error2ntstatus(ndr_err); return status; } return NT_STATUS_OK; }
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(talloc_tos(), 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_delete_veto_files(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; 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; } smb_dname_full = synthetic_smb_fname( talloc_tos(), fullname, NULL, NULL); if (smb_dname_full == NULL) { errno = ENOMEM; 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; }
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; 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; } smb_dname_full = synthetic_smb_fname(talloc_tos(), fullname, NULL, NULL); if (smb_dname_full == NULL) { errno = ENOMEM; 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; }
static NTSTATUS close_remove_share_mode(files_struct *fsp, enum file_close_type close_type) { connection_struct *conn = fsp->conn; struct server_id self = messaging_server_id(conn->sconn->msg_ctx); bool delete_file = false; bool changed_user = false; struct share_mode_lock *lck = NULL; NTSTATUS status = NT_STATUS_OK; NTSTATUS tmp_status; struct file_id id; const struct security_unix_token *del_token = NULL; const struct security_token *del_nt_token = NULL; bool got_tokens = false; bool normal_close; int ret_flock, retries = 1; /* Ensure any pending write time updates are done. */ if (fsp->update_write_time_event) { update_write_time_handler(fsp->conn->sconn->ev_ctx, fsp->update_write_time_event, timeval_current(), (void *)fsp); } /* * Lock the share entries, and determine if we should delete * on close. If so delete whilst the lock is still in effect. * This prevents race conditions with the file being created. JRA. */ lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id); if (lck == NULL) { DEBUG(0, ("close_remove_share_mode: Could not get share mode " "lock for file %s\n", fsp_str_dbg(fsp))); return NT_STATUS_INVALID_PARAMETER; } if (fsp->write_time_forced) { DEBUG(10,("close_remove_share_mode: write time forced " "for file %s\n", fsp_str_dbg(fsp))); set_close_write_time(fsp, lck->data->changed_write_time); } else if (fsp->update_write_time_on_close) { /* Someone had a pending write. */ if (null_timespec(fsp->close_write_time)) { DEBUG(10,("close_remove_share_mode: update to current time " "for file %s\n", fsp_str_dbg(fsp))); /* Update to current time due to "normal" write. */ set_close_write_time(fsp, timespec_current()); } else { DEBUG(10,("close_remove_share_mode: write time pending " "for file %s\n", fsp_str_dbg(fsp))); /* Update to time set on close call. */ set_close_write_time(fsp, fsp->close_write_time); } } if (fsp->initial_delete_on_close && !is_delete_on_close_set(lck, fsp->name_hash)) { bool became_user = False; /* Initial delete on close was set and no one else * wrote a real delete on close. */ if (get_current_vuid(conn) != fsp->vuid) { become_user(conn, fsp->vuid); became_user = True; } fsp->delete_on_close = true; set_delete_on_close_lck(fsp, lck, get_current_nttok(conn), get_current_utok(conn)); if (became_user) { unbecome_user(); } } delete_file = is_delete_on_close_set(lck, fsp->name_hash); if (delete_file) { int i; /* See if others still have the file open via this pathname. If this is the case, then don't delete. If all opens are POSIX delete now. */ for (i=0; i<lck->data->num_share_modes; i++) { struct share_mode_entry *e = &lck->data->share_modes[i]; if (!is_valid_share_mode_entry(e)) { continue; } if (e->name_hash != fsp->name_hash) { continue; } if ((fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) { continue; } if (serverid_equal(&self, &e->pid) && (e->share_file_id == fsp->fh->gen_id)) { continue; } if (share_mode_stale_pid(lck->data, i)) { continue; } delete_file = False; break; } } /* * NT can set delete_on_close of the last open * reference to a file. */ normal_close = (close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE); if (!normal_close || !delete_file) { status = NT_STATUS_OK; goto done; } /* * Ok, we have to delete the file */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close was set " "- deleting file.\n", fsp_str_dbg(fsp))); /* * Don't try to update the write time when we delete the file */ fsp->update_write_time_on_close = false; got_tokens = get_delete_on_close_token(lck, fsp->name_hash, &del_nt_token, &del_token); SMB_ASSERT(got_tokens); if (!unix_token_equal(del_token, get_current_utok(conn))) { /* Become the user who requested the delete. */ DEBUG(5,("close_remove_share_mode: file %s. " "Change user to uid %u\n", fsp_str_dbg(fsp), (unsigned int)del_token->uid)); if (!push_sec_ctx()) { smb_panic("close_remove_share_mode: file %s. failed to push " "sec_ctx.\n"); } set_sec_ctx(del_token->uid, del_token->gid, del_token->ngroups, del_token->groups, del_nt_token); changed_user = true; } /* We can only delete the file if the name we have is still valid and hasn't been renamed. */ tmp_status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(tmp_status)) { DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and stat failed with error %s\n", fsp_str_dbg(fsp), nt_errstr(tmp_status))); /* * Don't save the errno here, we ignore this error */ goto done; } id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st); if (!file_id_equal(&fsp->file_id, &id)) { DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and dev and/or inode does not match\n", fsp_str_dbg(fsp))); DEBUG(5,("close_remove_share_mode: file %s. stored file_id %s, " "stat file_id %s\n", fsp_str_dbg(fsp), file_id_string_tos(&fsp->file_id), file_id_string_tos(&id))); /* * Don't save the errno here, we ignore this error */ goto done; } if ((conn->fs_capabilities & FILE_NAMED_STREAMS) && !is_ntfs_stream_smb_fname(fsp->fsp_name)) { status = delete_all_streams(conn, fsp->fsp_name->base_name); if (!NT_STATUS_IS_OK(status)) { DEBUG(5, ("delete_all_streams failed: %s\n", nt_errstr(status))); goto done; } } retry_delete: /* temporary files with delete on close set will not be deleted on a * cifs share using a netapp backend since they are opened with * read + write access mask. * close the file to allow the delete. */ if (fsp->can_write && !S_ISDIR(fsp->fsp_name->st.st_ex_mode) && fsp->fh->ref_count == 1 && retries) { status = fd_close(fsp); if (!NT_STATUS_IS_OK(status)) { DEBUG(3, ("close_remove_share_mode: Error %s closing %s\n", nt_errstr(status), smb_fname_str_dbg(fsp->fsp_name))); goto skip_retry; } if (SMB_VFS_UNLINK(conn, fsp->fsp_name) != 0) { /* * This call can potentially fail as another smbd may * have had the file open with delete on close set and * deleted it when its last reference to this file * went away. Hence we log this but not at debug level * zero. */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and unlink failed with error %s\n", fsp_str_dbg(fsp), strerror(errno))); status = map_nt_error_from_unix(errno); retries = 0; goto retry_delete; } } else { if (SMB_VFS_UNLINK(conn, fsp->fsp_name) != 0) { /* * This call can potentially fail as another smbd may * have had the file open with delete on close set and * deleted it when its last reference to this file * went away. Hence we log this but not at debug level * zero. */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and unlink failed with error %s\n", fsp_str_dbg(fsp), strerror(errno))); status = map_nt_error_from_unix(errno); } } /* As we now have POSIX opens which can unlink * with other open files we may have taken * this code path with more than one share mode * entry - ensure we only delete once by resetting * the delete on close flag. JRA. */ skip_retry: fsp->delete_on_close = false; reset_delete_on_close_lck(fsp, lck); done: if (changed_user) { /* unbecome user. */ pop_sec_ctx(); } /* remove filesystem sharemodes */ ret_flock = SMB_VFS_KERNEL_FLOCK(fsp, 0, 0); if (ret_flock == -1) { DEBUG(2, ("close_remove_share_mode: removing kernel flock for " "%s failed: %s\n", fsp_str_dbg(fsp), strerror(errno))); } if (!del_share_mode(lck, fsp)) { DEBUG(0, ("close_remove_share_mode: Could not delete share " "entry for file %s\n", fsp_str_dbg(fsp))); } TALLOC_FREE(lck); if (delete_file) { /* * Do the notification after we released the share * mode lock. Inside notify_fname we take out another * tdb lock. With ctdb also accessing our databases, * this can lead to deadlocks. Putting this notify * after the TALLOC_FREE(lck) above we avoid locking * two records simultaneously. Notifies are async and * informational only, so calling the notify_fname * without holding the share mode lock should not do * any harm. */ notify_fname(conn, NOTIFY_ACTION_REMOVED, FILE_NOTIFY_CHANGE_FILE_NAME, fsp->fsp_name->base_name); } return status; }
static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp, enum file_close_type close_type) { struct server_id self = messaging_server_id(fsp->conn->sconn->msg_ctx); struct share_mode_lock *lck = NULL; bool delete_dir = False; NTSTATUS status = NT_STATUS_OK; NTSTATUS status1 = NT_STATUS_OK; const struct security_token *del_nt_token = NULL; const struct security_unix_token *del_token = NULL; NTSTATUS notify_status; if (fsp->conn->sconn->using_smb2) { notify_status = STATUS_NOTIFY_CLEANUP; } else { notify_status = NT_STATUS_OK; } /* * NT can set delete_on_close of the last open * reference to a directory also. */ lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id); if (lck == NULL) { DEBUG(0, ("close_directory: Could not get share mode lock for " "%s\n", fsp_str_dbg(fsp))); return NT_STATUS_INVALID_PARAMETER; } if (fsp->initial_delete_on_close) { bool became_user = False; /* Initial delete on close was set - for * directories we don't care if anyone else * wrote a real delete on close. */ if (get_current_vuid(fsp->conn) != fsp->vuid) { become_user(fsp->conn, fsp->vuid); became_user = True; } send_stat_cache_delete_message(fsp->conn->sconn->msg_ctx, fsp->fsp_name->base_name); set_delete_on_close_lck(fsp, lck, get_current_nttok(fsp->conn), get_current_utok(fsp->conn)); fsp->delete_on_close = true; if (became_user) { unbecome_user(); } } delete_dir = get_delete_on_close_token(lck, fsp->name_hash, &del_nt_token, &del_token); if (delete_dir) { int i; /* See if others still have the dir open. If this is the * case, then don't delete. If all opens are POSIX delete now. */ for (i=0; i<lck->data->num_share_modes; i++) { struct share_mode_entry *e = &lck->data->share_modes[i]; if (is_valid_share_mode_entry(e) && e->name_hash == fsp->name_hash) { if ((fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) { continue; } if (serverid_equal(&self, &e->pid) && (e->share_file_id == fsp->fh->gen_id)) { continue; } if (share_mode_stale_pid(lck->data, i)) { continue; } delete_dir = False; break; } } } if ((close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) && delete_dir) { /* Become the user who requested the delete. */ if (!push_sec_ctx()) { smb_panic("close_directory: failed to push sec_ctx.\n"); } set_sec_ctx(del_token->uid, del_token->gid, del_token->ngroups, del_token->groups, del_nt_token); if (!del_share_mode(lck, fsp)) { DEBUG(0, ("close_directory: Could not delete share entry for " "%s\n", fsp_str_dbg(fsp))); } TALLOC_FREE(lck); if ((fsp->conn->fs_capabilities & FILE_NAMED_STREAMS) && !is_ntfs_stream_smb_fname(fsp->fsp_name)) { status = delete_all_streams(fsp->conn, fsp->fsp_name->base_name); if (!NT_STATUS_IS_OK(status)) { DEBUG(5, ("delete_all_streams failed: %s\n", nt_errstr(status))); return status; } } status = rmdir_internals(talloc_tos(), fsp); DEBUG(5,("close_directory: %s. Delete on close was set - " "deleting directory returned %s.\n", fsp_str_dbg(fsp), nt_errstr(status))); /* unbecome user. */ pop_sec_ctx(); /* * Ensure we remove any change notify requests that would * now fail as the directory has been deleted. */ if (NT_STATUS_IS_OK(status)) { notify_status = NT_STATUS_DELETE_PENDING; } } else { if (!del_share_mode(lck, fsp)) { DEBUG(0, ("close_directory: Could not delete share entry for " "%s\n", fsp_str_dbg(fsp))); } TALLOC_FREE(lck); } remove_pending_change_notify_requests_by_fid(fsp, notify_status); status1 = fd_close(fsp); if (!NT_STATUS_IS_OK(status1)) { DEBUG(0, ("Could not close dir! fname=%s, fd=%d, err=%d=%s\n", fsp_str_dbg(fsp), fsp->fh->fd, errno, strerror(errno))); } /* * Do the code common to files and directories. */ close_filestruct(fsp); file_free(req, fsp); if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(status1)) { status = status1; } return status; }
static int streams_xattr_rename(vfs_handle_struct *handle, const struct smb_filename *smb_fname_src, const struct smb_filename *smb_fname_dst) { NTSTATUS status; int ret = -1; char *src_xattr_name = NULL; char *dst_xattr_name = NULL; bool src_is_stream, dst_is_stream; ssize_t oret; ssize_t nret; struct ea_struct ea; src_is_stream = is_ntfs_stream_smb_fname(smb_fname_src); dst_is_stream = is_ntfs_stream_smb_fname(smb_fname_dst); if (!src_is_stream && !dst_is_stream) { return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst); } /* For now don't allow renames from or to the default stream. */ if (is_ntfs_default_stream_smb_fname(smb_fname_src) || is_ntfs_default_stream_smb_fname(smb_fname_dst)) { errno = ENOSYS; goto done; } /* Don't rename if the streams are identical. */ if (strcasecmp_m(smb_fname_src->stream_name, smb_fname_dst->stream_name) == 0) { goto done; } /* Get the xattr names. */ status = streams_xattr_get_name(handle, talloc_tos(), smb_fname_src->stream_name, &src_xattr_name); if (!NT_STATUS_IS_OK(status)) { errno = map_errno_from_nt_status(status); goto fail; } status = streams_xattr_get_name(handle, talloc_tos(), smb_fname_dst->stream_name, &dst_xattr_name); if (!NT_STATUS_IS_OK(status)) { errno = map_errno_from_nt_status(status); goto fail; } /* read the old stream */ status = get_ea_value(talloc_tos(), handle->conn, NULL, smb_fname_src, src_xattr_name, &ea); if (!NT_STATUS_IS_OK(status)) { errno = ENOENT; goto fail; } /* (over)write the new stream */ nret = SMB_VFS_SETXATTR(handle->conn, smb_fname_src, dst_xattr_name, ea.value.data, ea.value.length, 0); if (nret < 0) { if (errno == ENOATTR) { errno = ENOENT; } goto fail; } /* remove the old stream */ oret = SMB_VFS_REMOVEXATTR(handle->conn, smb_fname_src, src_xattr_name); if (oret < 0) { if (errno == ENOATTR) { errno = ENOENT; } goto fail; } done: errno = 0; ret = 0; fail: TALLOC_FREE(src_xattr_name); TALLOC_FREE(dst_xattr_name); return ret; }
static int streams_xattr_open(vfs_handle_struct *handle, struct smb_filename *smb_fname, files_struct *fsp, int flags, mode_t mode) { NTSTATUS status; struct streams_xattr_config *config = NULL; struct stream_io *sio = NULL; struct ea_struct ea; char *xattr_name = NULL; int pipe_fds[2]; int fakefd = -1; int ret; SMB_VFS_HANDLE_GET_DATA(handle, config, struct streams_xattr_config, return -1); DEBUG(10, ("streams_xattr_open called for %s with flags 0x%x\n", smb_fname_str_dbg(smb_fname), flags)); if (!is_ntfs_stream_smb_fname(smb_fname)) { return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode); } /* If the default stream is requested, just open the base file. */ if (is_ntfs_default_stream_smb_fname(smb_fname)) { char *tmp_stream_name; tmp_stream_name = smb_fname->stream_name; smb_fname->stream_name = NULL; ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode); smb_fname->stream_name = tmp_stream_name; return ret; } status = streams_xattr_get_name(handle, talloc_tos(), smb_fname->stream_name, &xattr_name); if (!NT_STATUS_IS_OK(status)) { errno = map_errno_from_nt_status(status); goto fail; } /* * Return a valid fd, but ensure any attempt to use it returns an error * (EPIPE). */ ret = pipe(pipe_fds); if (ret != 0) { goto fail; } close(pipe_fds[1]); pipe_fds[1] = -1; fakefd = pipe_fds[0]; status = get_ea_value(talloc_tos(), handle->conn, NULL, smb_fname, xattr_name, &ea); DEBUG(10, ("get_ea_value returned %s\n", nt_errstr(status))); if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { /* * The base file is not there. This is an error even if we got * O_CREAT, the higher levels should have created the base * file for us. */ DEBUG(10, ("streams_xattr_open: base file %s not around, " "returning ENOENT\n", smb_fname->base_name)); errno = ENOENT; goto fail; } if ((!NT_STATUS_IS_OK(status) && (flags & O_CREAT)) || (flags & O_TRUNC)) { /* * The attribute does not exist or needs to be truncated */ /* * Darn, xattrs need at least 1 byte */ char null = '\0'; DEBUG(10, ("creating or truncating attribute %s on file %s\n", xattr_name, smb_fname->base_name)); ret = SMB_VFS_SETXATTR(fsp->conn, smb_fname, xattr_name, &null, sizeof(null), flags & O_EXCL ? XATTR_CREATE : 0); if (ret != 0) { goto fail; } } sio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct stream_io, NULL); if (sio == NULL) { errno = ENOMEM; goto fail; } sio->xattr_name = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(handle, fsp), xattr_name); /* * so->base needs to be a copy of fsp->fsp_name->base_name, * making it identical to streams_xattr_recheck(). If the * open is changing directories, fsp->fsp_name->base_name * will be the full path from the share root, whilst * smb_fname will be relative to the $cwd. */ sio->base = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(handle, fsp), fsp->fsp_name->base_name); sio->fsp_name_ptr = fsp->fsp_name; sio->handle = handle; sio->fsp = fsp; if ((sio->xattr_name == NULL) || (sio->base == NULL)) { errno = ENOMEM; goto fail; } return fakefd; fail: if (fakefd >= 0) { close(fakefd); fakefd = -1; } return -1; }
int onefs_rename(vfs_handle_struct *handle, const struct smb_filename *smb_fname_src, const struct smb_filename *smb_fname_dst) { struct smb_filename *smb_fname_src_onefs = NULL; struct smb_filename *smb_fname_dst_onefs = NULL; NTSTATUS status; int saved_errno; int dir_fd = -1; int ret = -1; START_PROFILE(syscall_rename_at); if (!is_ntfs_stream_smb_fname(smb_fname_src) && !is_ntfs_stream_smb_fname(smb_fname_dst)) { ret = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst); goto done; } /* For now don't allow renames from or to the default stream. */ if (is_ntfs_default_stream_smb_fname(smb_fname_src) || is_ntfs_default_stream_smb_fname(smb_fname_dst)) { DEBUG(3, ("Unable to rename to/from a default stream: %s -> " "%s\n", smb_fname_str_dbg(smb_fname_src), smb_fname_str_dbg(smb_fname_dst))); errno = ENOSYS; goto done; } /* prep stream smb_filename structs. */ status = onefs_stream_prep_smb_fname(talloc_tos(), smb_fname_src, &smb_fname_src_onefs); if (!NT_STATUS_IS_OK(status)) { errno = map_errno_from_nt_status(status); goto done; } status = onefs_stream_prep_smb_fname(talloc_tos(), smb_fname_dst, &smb_fname_dst_onefs); if (!NT_STATUS_IS_OK(status)) { errno = map_errno_from_nt_status(status); goto done; } dir_fd = get_stream_dir_fd(handle->conn, smb_fname_src->base_name, NULL); if (dir_fd < -1) { goto done; } DEBUG(8, ("onefs_rename called for %s => %s\n", smb_fname_str_dbg(smb_fname_src_onefs), smb_fname_str_dbg(smb_fname_dst_onefs))); /* Handle rename of stream to default stream specially. */ if (smb_fname_dst_onefs->stream_name == NULL) { ret = enc_renameat(dir_fd, smb_fname_src_onefs->stream_name, ENC_DEFAULT, AT_FDCWD, smb_fname_dst_onefs->base_name, ENC_DEFAULT); } else { ret = enc_renameat(dir_fd, smb_fname_src_onefs->stream_name, ENC_DEFAULT, dir_fd, smb_fname_dst_onefs->stream_name, ENC_DEFAULT); } done: END_PROFILE(syscall_rename_at); TALLOC_FREE(smb_fname_src_onefs); TALLOC_FREE(smb_fname_dst_onefs); saved_errno = errno; if (dir_fd >= 0) { close(dir_fd); } errno = saved_errno; return ret; }
static NTSTATUS close_remove_share_mode(files_struct *fsp, enum file_close_type close_type) { connection_struct *conn = fsp->conn; bool delete_file = false; bool changed_user = false; struct share_mode_lock *lck = NULL; NTSTATUS status = NT_STATUS_OK; NTSTATUS tmp_status; struct file_id id; /* Ensure any pending write time updates are done. */ if (fsp->update_write_time_event) { update_write_time_handler(smbd_event_context(), fsp->update_write_time_event, timeval_current(), (void *)fsp); } /* * Lock the share entries, and determine if we should delete * on close. If so delete whilst the lock is still in effect. * This prevents race conditions with the file being created. JRA. */ lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL, NULL); if (lck == NULL) { DEBUG(0, ("close_remove_share_mode: Could not get share mode " "lock for file %s\n", fsp_str_dbg(fsp))); status = NT_STATUS_INVALID_PARAMETER; goto done; } if (fsp->write_time_forced) { DEBUG(10,("close_remove_share_mode: write time forced " "for file %s\n", fsp_str_dbg(fsp))); set_close_write_time(fsp, lck->changed_write_time); } else if (fsp->update_write_time_on_close) { /* Someone had a pending write. */ if (null_timespec(fsp->close_write_time)) { DEBUG(10,("close_remove_share_mode: update to current time " "for file %s\n", fsp_str_dbg(fsp))); /* Update to current time due to "normal" write. */ set_close_write_time(fsp, timespec_current()); } else { DEBUG(10,("close_remove_share_mode: write time pending " "for file %s\n", fsp_str_dbg(fsp))); /* Update to time set on close call. */ set_close_write_time(fsp, fsp->close_write_time); } } if (!del_share_mode(lck, fsp)) { DEBUG(0, ("close_remove_share_mode: Could not delete share " "entry for file %s\n", fsp_str_dbg(fsp))); } if (fsp->initial_delete_on_close && (lck->delete_token == NULL)) { bool became_user = False; /* Initial delete on close was set and no one else * wrote a real delete on close. */ if (current_user.vuid != fsp->vuid) { become_user(conn, fsp->vuid); became_user = True; } fsp->delete_on_close = true; set_delete_on_close_lck(lck, True, ¤t_user.ut); if (became_user) { unbecome_user(); } } delete_file = lck->delete_on_close; if (delete_file) { int i; /* See if others still have the file open. If this is the * case, then don't delete. If all opens are POSIX delete now. */ for (i=0; i<lck->num_share_modes; i++) { struct share_mode_entry *e = &lck->share_modes[i]; if (is_valid_share_mode_entry(e)) { if (fsp->posix_open && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) { continue; } delete_file = False; break; } } } /* Notify any deferred opens waiting on this close. */ notify_deferred_opens(lck); reply_to_oplock_break_requests(fsp); /* * NT can set delete_on_close of the last open * reference to a file. */ if (!(close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) || !delete_file || (lck->delete_token == NULL)) { TALLOC_FREE(lck); return NT_STATUS_OK; } /* * Ok, we have to delete the file */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close was set " "- deleting file.\n", fsp_str_dbg(fsp))); /* * Don't try to update the write time when we delete the file */ fsp->update_write_time_on_close = false; if (!unix_token_equal(lck->delete_token, ¤t_user.ut)) { /* Become the user who requested the delete. */ DEBUG(5,("close_remove_share_mode: file %s. " "Change user to uid %u\n", fsp_str_dbg(fsp), (unsigned int)lck->delete_token->uid)); if (!push_sec_ctx()) { smb_panic("close_remove_share_mode: file %s. failed to push " "sec_ctx.\n"); } set_sec_ctx(lck->delete_token->uid, lck->delete_token->gid, lck->delete_token->ngroups, lck->delete_token->groups, NULL); changed_user = true; } /* We can only delete the file if the name we have is still valid and hasn't been renamed. */ tmp_status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(tmp_status)) { DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and stat failed with error %s\n", fsp_str_dbg(fsp), nt_errstr(tmp_status))); /* * Don't save the errno here, we ignore this error */ goto done; } id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st); if (!file_id_equal(&fsp->file_id, &id)) { DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and dev and/or inode does not match\n", fsp_str_dbg(fsp))); DEBUG(5,("close_remove_share_mode: file %s. stored file_id %s, " "stat file_id %s\n", fsp_str_dbg(fsp), file_id_string_tos(&fsp->file_id), file_id_string_tos(&id))); /* * Don't save the errno here, we ignore this error */ goto done; } if ((conn->fs_capabilities & FILE_NAMED_STREAMS) && !is_ntfs_stream_smb_fname(fsp->fsp_name)) { status = delete_all_streams(conn, fsp->fsp_name->base_name); if (!NT_STATUS_IS_OK(status)) { DEBUG(5, ("delete_all_streams failed: %s\n", nt_errstr(status))); goto done; } } if (SMB_VFS_UNLINK(conn, fsp->fsp_name) != 0) { /* * This call can potentially fail as another smbd may * have had the file open with delete on close set and * deleted it when its last reference to this file * went away. Hence we log this but not at debug level * zero. */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and unlink failed with error %s\n", fsp_str_dbg(fsp), strerror(errno))); status = map_nt_error_from_unix(errno); } notify_fname(conn, NOTIFY_ACTION_REMOVED, FILE_NOTIFY_CHANGE_FILE_NAME, fsp->fsp_name->base_name); /* As we now have POSIX opens which can unlink * with other open files we may have taken * this code path with more than one share mode * entry - ensure we only delete once by resetting * the delete on close flag. JRA. */ fsp->delete_on_close = false; set_delete_on_close_lck(lck, False, NULL); done: if (changed_user) { /* unbecome user. */ pop_sec_ctx(); } TALLOC_FREE(lck); return status; }