/** * Return file size * @param conn connection * @param fname file name * @return size in bytes **/ static SMB_OFF_T recycle_get_file_size(vfs_handle_struct *handle, const struct smb_filename *smb_fname) { struct smb_filename *smb_fname_tmp = NULL; NTSTATUS status; SMB_OFF_T size; status = copy_smb_filename(talloc_tos(), smb_fname, &smb_fname_tmp); if (!NT_STATUS_IS_OK(status)) { size = (SMB_OFF_T)0; goto out; } if (SMB_VFS_STAT(handle->conn, smb_fname_tmp) != 0) { DEBUG(0,("recycle: stat for %s returned %s\n", smb_fname_str_dbg(smb_fname_tmp), strerror(errno))); size = (SMB_OFF_T)0; goto out; } size = smb_fname_tmp->st.st_ex_size; out: TALLOC_FREE(smb_fname_tmp); return size; }
NTSTATUS set_create_timespec_ea(connection_struct *conn, const struct smb_filename *psmb_fname, struct timespec create_time) { struct smb_filename *smb_fname; uint32_t dosmode; int ret; if (!lp_store_dos_attributes(SNUM(conn))) { return NT_STATUS_OK; } smb_fname = synthetic_smb_fname(talloc_tos(), psmb_fname->base_name, NULL, &psmb_fname->st); if (smb_fname == NULL) { return NT_STATUS_NO_MEMORY; } dosmode = dos_mode(conn, smb_fname); smb_fname->st.st_ex_btime = create_time; ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false); if (ret == -1) { map_nt_error_from_unix(errno); } DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n", smb_fname_str_dbg(smb_fname))); return NT_STATUS_OK; }
static struct files_struct *log_writeable_file_fn( struct files_struct *fsp, void *private_data) { bool *found = (bool *)private_data; char *path; if (!fsp->can_write) { return NULL; } if (!(*found)) { DEBUG(0, ("Writable files open at exit:\n")); *found = true; } path = talloc_asprintf(talloc_tos(), "%s/%s", fsp->conn->connectpath, smb_fname_str_dbg(fsp->fsp_name)); if (path == NULL) { DEBUGADD(0, ("<NOMEM>\n")); } DEBUGADD(0, ("%s\n", path)); TALLOC_FREE(path); return NULL; }
NTSTATUS file_name_hash(connection_struct *conn, const char *name, uint32_t *p_name_hash) { char tmpbuf[PATH_MAX]; char *fullpath, *to_free; ssize_t len; TDB_DATA key; /* Set the hash of the full pathname. */ len = full_path_tos(conn->connectpath, name, tmpbuf, sizeof(tmpbuf), &fullpath, &to_free); if (len == -1) { return NT_STATUS_NO_MEMORY; } key = (TDB_DATA) { .dptr = (uint8_t *)fullpath, .dsize = len+1 }; *p_name_hash = tdb_jenkins_hash(&key); DEBUG(10,("file_name_hash: %s hash 0x%x\n", fullpath, (unsigned int)*p_name_hash )); TALLOC_FREE(to_free); return NT_STATUS_OK; } /** * The only way that the fsp->fsp_name field should ever be set. */ NTSTATUS fsp_set_smb_fname(struct files_struct *fsp, const struct smb_filename *smb_fname_in) { struct smb_filename *smb_fname_new; smb_fname_new = cp_smb_filename(fsp, smb_fname_in); if (smb_fname_new == NULL) { return NT_STATUS_NO_MEMORY; } TALLOC_FREE(fsp->fsp_name); fsp->fsp_name = smb_fname_new; return file_name_hash(fsp->conn, smb_fname_str_dbg(fsp->fsp_name), &fsp->name_hash); } const struct GUID *fsp_client_guid(const files_struct *fsp) { return &fsp->conn->sconn->client->connections->smb2.client.guid; } uint32_t fsp_lease_type(struct files_struct *fsp) { if (fsp->oplock_type == LEASE_OPLOCK) { return fsp->lease->lease.lease_state; } return map_oplock_to_lease_type(fsp->oplock_type); }
NTSTATUS onefs_stream_prep_smb_fname(TALLOC_CTX *ctx, const struct smb_filename *smb_fname_in, struct smb_filename **smb_fname_out) { char *stream_name = NULL; NTSTATUS status; /* * Only attempt to strip off the trailing :$DATA if there is an actual * stream there. If it is the default stream, the smb_fname_out will * just have a NULL stream so the base file is opened. */ if (smb_fname_in->stream_name && !is_ntfs_default_stream_smb_fname(smb_fname_in)) { char *str_tmp = smb_fname_in->stream_name; /* First strip off the leading ':' */ if (str_tmp[0] == ':') { str_tmp++; } /* Create a new copy of the stream_name. */ stream_name = talloc_strdup(ctx, str_tmp); if (stream_name == NULL) { return NT_STATUS_NO_MEMORY; } /* Strip off the :$DATA if one exists. */ str_tmp = strrchr_m(stream_name, ':'); if (str_tmp) { if (strcasecmp_m(str_tmp, ":$DATA") != 0) { return NT_STATUS_INVALID_PARAMETER; } str_tmp[0] = '\0'; } } /* * If there was a stream that wasn't the default stream the leading * colon and trailing :$DATA has now been stripped off. Create a new * smb_filename to pass back. */ status = create_synthetic_smb_fname(ctx, smb_fname_in->base_name, stream_name, &smb_fname_in->st, smb_fname_out); TALLOC_FREE(stream_name); if (!NT_STATUS_IS_OK(status)) { DEBUG(5, ("Failed to prep stream name for %s: %s\n", *smb_fname_out ? smb_fname_str_dbg(*smb_fname_out) : "NULL", nt_errstr(status))); } return status; }
uint32_t dos_mode_msdfs(connection_struct *conn, const struct smb_filename *smb_fname) { uint32_t result = 0; DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname))); if (!VALID_STAT(smb_fname->st)) { return 0; } /* First do any modifications that depend on the path name. */ /* hide files with a name starting with a . */ if (lp_hide_dot_files(SNUM(conn))) { const char *p = strrchr_m(smb_fname->base_name, '/'); if (p) { p++; } else { p = smb_fname->base_name; } /* Only . and .. are not hidden. */ if (p[0] == '.' && !((p[1] == '\0') || (p[1] == '.' && p[2] == '\0'))) { result |= FILE_ATTRIBUTE_HIDDEN; } } result |= dos_mode_from_sbuf(conn, smb_fname); /* Optimization : Only call is_hidden_path if it's not already hidden. */ if (!(result & FILE_ATTRIBUTE_HIDDEN) && IS_HIDDEN_PATH(conn, smb_fname->base_name)) { result |= FILE_ATTRIBUTE_HIDDEN; } if (result == 0) { result = FILE_ATTRIBUTE_NORMAL; } result = filter_mode_by_protocol(result); /* * Add in that it is a reparse point */ result |= FILE_ATTRIBUTE_REPARSE_POINT; dos_mode_debug_print(__func__, result); return(result); }
static int audit_rename(vfs_handle_struct *handle, const struct smb_filename *smb_fname_src, const struct smb_filename *smb_fname_dst) { int result; result = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst); if (lp_syslog() > 0) { syslog(audit_syslog_priority(handle), "rename %s -> %s %s%s\n", smb_fname_src->base_name, smb_fname_dst->base_name, (result < 0) ? "failed: " : "", (result < 0) ? strerror(errno) : ""); } DEBUG(1, ("vfs_extd_audit: rename old: %s newname: %s %s %s\n", smb_fname_str_dbg(smb_fname_src), smb_fname_str_dbg(smb_fname_dst), (result < 0) ? "failed: " : "", (result < 0) ? strerror(errno) : "")); return result; }
/** * Touch access or modify date **/ static void recycle_do_touch(vfs_handle_struct *handle, const struct smb_filename *smb_fname, bool touch_mtime) { struct smb_filename *smb_fname_tmp = NULL; struct smb_file_time ft; NTSTATUS status; int ret, err; ZERO_STRUCT(ft); status = copy_smb_filename(talloc_tos(), smb_fname, &smb_fname_tmp); if (!NT_STATUS_IS_OK(status)) { return; } if (SMB_VFS_STAT(handle->conn, smb_fname_tmp) != 0) { DEBUG(0,("recycle: stat for %s returned %s\n", smb_fname_str_dbg(smb_fname_tmp), strerror(errno))); goto out; } /* atime */ ft.atime = timespec_current(); /* mtime */ ft.mtime = touch_mtime ? ft.atime : smb_fname_tmp->st.st_ex_mtime; become_root(); ret = SMB_VFS_NEXT_NTIMES(handle, smb_fname_tmp, &ft); err = errno; unbecome_root(); if (ret == -1 ) { DEBUG(0, ("recycle: touching %s failed, reason = %s\n", smb_fname_str_dbg(smb_fname_tmp), strerror(err))); } out: TALLOC_FREE(smb_fname_tmp); }
/** * The only way that the fsp->fsp_name field should ever be set. */ NTSTATUS fsp_set_smb_fname(struct files_struct *fsp, const struct smb_filename *smb_fname_in) { struct smb_filename *smb_fname_new; smb_fname_new = cp_smb_filename(fsp, smb_fname_in); if (smb_fname_new == NULL) { return NT_STATUS_NO_MEMORY; } TALLOC_FREE(fsp->fsp_name); fsp->fsp_name = smb_fname_new; return file_name_hash(fsp->conn, smb_fname_str_dbg(fsp->fsp_name), &fsp->name_hash); }
static int cephwrap_open(struct vfs_handle_struct *handle, struct smb_filename *smb_fname, files_struct *fsp, int flags, mode_t mode) { int result = -ENOENT; DBG_DEBUG("[CEPH] open(%p, %s, %p, %d, %d)\n", handle, smb_fname_str_dbg(smb_fname), fsp, flags, mode); if (smb_fname->stream_name) { goto out; } result = ceph_open(handle->data, smb_fname->base_name, flags, mode); out: DBG_DEBUG("[CEPH] open(...) = %d\n", result); WRAP_RETURN(result); }
static bool find_completed_open(files_struct *fsp, int *p_fd, int *p_errno) { struct aio_open_private_data *opd; opd = find_open_private_data_by_mid(fsp->mid); if (!opd) { return false; } if (opd->in_progress) { DEBUG(0,("find_completed_open: mid %llu " "jobid %d still in progress for " "file %s/%s. PANIC !\n", (unsigned long long)opd->mid, opd->jobid, opd->dname, opd->fname)); /* Disaster ! This is an open timeout. Just panic. */ smb_panic("find_completed_open - in_progress\n"); /* notreached. */ return false; } *p_fd = opd->ret_fd; *p_errno = opd->ret_errno; DEBUG(5,("find_completed_open: mid %llu returning " "fd = %d, errno = %d (%s) " "jobid (%d) for file %s\n", (unsigned long long)opd->mid, opd->ret_fd, opd->ret_errno, strerror(opd->ret_errno), opd->jobid, smb_fname_str_dbg(fsp->fsp_name))); /* Now we can free the opd. */ TALLOC_FREE(opd); return true; }
static int audit_unlink(vfs_handle_struct *handle, const struct smb_filename *smb_fname) { int result; result = SMB_VFS_NEXT_UNLINK(handle, smb_fname); if (lp_syslog() > 0) { syslog(audit_syslog_priority(handle), "unlink %s %s%s\n", smb_fname->base_name, (result < 0) ? "failed: " : "", (result < 0) ? strerror(errno) : ""); } DEBUG(0, ("vfs_extd_audit: unlink %s %s %s\n", smb_fname_str_dbg(smb_fname), (result < 0) ? "failed: " : "", (result < 0) ? strerror(errno) : "")); return result; }
static int audit_open(vfs_handle_struct *handle, struct smb_filename *smb_fname, files_struct *fsp, int flags, mode_t mode) { int result; result = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode); if (lp_syslog() > 0) { syslog(audit_syslog_priority(handle), "open %s (fd %d) %s%s%s\n", smb_fname->base_name, result, ((flags & O_WRONLY) || (flags & O_RDWR)) ? "for writing " : "", (result < 0) ? "failed: " : "", (result < 0) ? strerror(errno) : ""); } DEBUG(2, ("vfs_extd_audit: open %s %s %s\n", smb_fname_str_dbg(smb_fname), (result < 0) ? "failed: " : "", (result < 0) ? strerror(errno) : "")); return result; }
/* * Because there is no good way to guarantee that a new xattr will be * created on file creation there might be no acl xattr on a file when * trying to read the acl. In this case the acl xattr will get * constructed at that time from the parent acl. * If the parent ACL doesn't have an xattr either the call will * recurse to the next parent directory until the share root is * reached. If the share root doesn't contain an ACL xattr either a * default ACL will be used. * Also a default ACL will be set if a non inheriting ACL is encountered. * * Basic algorithm: * read acl xattr blob * if acl xattr blob doesn't exist * stat current directory to know if it's a file or directory * read acl xattr blob from parent dir * acl xattr blob to smb nfs4 acl * calculate inherited smb nfs4 acl * without inheritance use default smb nfs4 acl * smb nfs4 acl to acl xattr blob * set acl xattr blob * return smb nfs4 acl * else * acl xattr blob to smb nfs4 acl * * Todo: Really use mem_ctx after fixing interface of nfs4_acls */ static struct SMB4ACL_T *nfs4acls_inheritacl(vfs_handle_struct *handle, const char *path, TALLOC_CTX *mem_ctx) { char *parent_dir = NULL; struct SMB4ACL_T *pparentacl = NULL; struct SMB4ACL_T *pchildacl = NULL; struct SMB4ACE_T *pace; SMB_ACE4PROP_T ace; bool isdir; struct smb_filename *smb_fname = NULL; NTSTATUS status; int ret; TALLOC_CTX *frame = talloc_stackframe(); DEBUG(10, ("nfs4acls_inheritacl invoked for %s\n", path)); smb_fname = synthetic_smb_fname(frame, path, NULL, NULL); if (smb_fname == NULL) { TALLOC_FREE(frame); errno = ENOMEM; return NULL; } ret = SMB_VFS_STAT(handle->conn, smb_fname); if (ret == -1) { DEBUG(0,("nfs4acls_inheritacl: failed to stat " "directory %s. Error was %s\n", smb_fname_str_dbg(smb_fname), strerror(errno))); TALLOC_FREE(frame); return NULL; } isdir = S_ISDIR(smb_fname->st.st_ex_mode); if (!parent_dirname(talloc_tos(), path, &parent_dir, NULL)) { TALLOC_FREE(frame); errno = ENOMEM; return NULL; } status = nfs4_get_nfs4_acl(handle, frame, parent_dir, &pparentacl); if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND) && strncmp(parent_dir, ".", 2) != 0) { pparentacl = nfs4acls_inheritacl(handle, parent_dir, frame); } else if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { pparentacl = nfs4acls_defaultacl(frame); } else if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(frame); return NULL; } pchildacl = smb_create_smb4acl(mem_ctx); if (pchildacl == NULL) { DEBUG(0, ("talloc failed\n")); TALLOC_FREE(frame); errno = ENOMEM; return NULL; } for (pace = smb_first_ace4(pparentacl); pace != NULL; pace = smb_next_ace4(pace)) { struct SMB4ACE_T *pchildace; ace = *smb_get_ace4(pace); if ((isdir && !(ace.aceFlags & SMB_ACE4_DIRECTORY_INHERIT_ACE)) || (!isdir && !(ace.aceFlags & SMB_ACE4_FILE_INHERIT_ACE))) { DEBUG(10, ("non inheriting ace type: %d, iflags: %x, " "flags: %x, mask: %x, who: %d\n", ace.aceType, ace.flags, ace.aceFlags, ace.aceMask, ace.who.id)); continue; } DEBUG(10, ("inheriting ace type: %d, iflags: %x, " "flags: %x, mask: %x, who: %d\n", ace.aceType, ace.flags, ace.aceFlags, ace.aceMask, ace.who.id)); ace.aceFlags |= SMB_ACE4_INHERITED_ACE; if (ace.aceFlags & SMB_ACE4_INHERIT_ONLY_ACE) { ace.aceFlags &= ~SMB_ACE4_INHERIT_ONLY_ACE; } if (ace.aceFlags & SMB_ACE4_NO_PROPAGATE_INHERIT_ACE) { ace.aceFlags &= ~SMB_ACE4_FILE_INHERIT_ACE; ace.aceFlags &= ~SMB_ACE4_DIRECTORY_INHERIT_ACE; ace.aceFlags &= ~SMB_ACE4_NO_PROPAGATE_INHERIT_ACE; } pchildace = smb_add_ace4(pchildacl, &ace); if (pchildace == NULL) { DEBUG(0, ("talloc failed\n")); TALLOC_FREE(frame); errno = ENOMEM; return NULL; } } /* Set a default ACL if we didn't inherit anything. */ if (smb_first_ace4(pchildacl) == NULL) { TALLOC_FREE(pchildacl); pchildacl = nfs4acls_defaultacl(mem_ctx); } /* store the returned ACL to get it directly in the future and avoid dynamic inheritance behavior. */ nfs4acl_xattr_set_smb4acl(handle, path, pchildacl); TALLOC_FREE(frame); return pchildacl; }
static NTSTATUS build_stream_path(TALLOC_CTX *mem_ctx, connection_struct *conn, const char *orig_path, struct smb_filename *smb_fname) { NTSTATUS status; unsigned int i, num_streams = 0; struct stream_struct *streams = NULL; if (SMB_VFS_STAT(conn, smb_fname) == 0) { DEBUG(10, ("'%s' exists\n", smb_fname_str_dbg(smb_fname))); return NT_STATUS_OK; } if (errno != ENOENT) { DEBUG(10, ("vfs_stat failed: %s\n", strerror(errno))); status = map_nt_error_from_unix(errno); goto fail; } /* Fall back to a case-insensitive scan of all streams on the file. */ status = vfs_streaminfo(conn, NULL, smb_fname->base_name, mem_ctx, &num_streams, &streams); if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { SET_STAT_INVALID(smb_fname->st); return NT_STATUS_OK; } if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("vfs_streaminfo failed: %s\n", nt_errstr(status))); goto fail; } for (i=0; i<num_streams; i++) { DEBUG(10, ("comparing [%s] and [%s]: ", smb_fname->stream_name, streams[i].name)); if (fname_equal(smb_fname->stream_name, streams[i].name, conn->case_sensitive)) { DEBUGADD(10, ("equal\n")); break; } DEBUGADD(10, ("not equal\n")); } /* Couldn't find the stream. */ if (i == num_streams) { SET_STAT_INVALID(smb_fname->st); TALLOC_FREE(streams); return NT_STATUS_OK; } DEBUG(10, ("case insensitive stream. requested: %s, actual: %s\n", smb_fname->stream_name, streams[i].name)); TALLOC_FREE(smb_fname->stream_name); smb_fname->stream_name = talloc_strdup(smb_fname, streams[i].name); if (smb_fname->stream_name == NULL) { status = NT_STATUS_NO_MEMORY; goto fail; } SET_STAT_INVALID(smb_fname->st); if (SMB_VFS_STAT(conn, smb_fname) == 0) { DEBUG(10, ("'%s' exists\n", smb_fname_str_dbg(smb_fname))); } status = NT_STATUS_OK; fail: TALLOC_FREE(streams); return status; }
/** * Return a debug string of the path name of an fsp using the talloc_tos(). */ const char *fsp_str_dbg(const struct files_struct *fsp) { return smb_fname_str_dbg(fsp->fsp_name); }
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; }
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; }
/** * Go through all the steps to validate a filename. * * @param ctx talloc_ctx to allocate memory with. * @param conn connection struct for vfs calls. * @param dfs_path Whether this path requires dfs resolution. * @param name_in The unconverted name. * @param ucf_flags flags to pass through to unix_convert(). * UCF_ALWAYS_ALLOW_WCARD_LCOMP will be OR'd in if * p_cont_wcard != NULL and is true and * UCF_COND_ALLOW_WCARD_LCOMP. * @param p_cont_wcard If not NULL, will be set to true if the dfs path * resolution detects a wildcard. * @param pp_smb_fname The final converted name will be allocated if the * return is NT_STATUS_OK. * * @return NT_STATUS_OK if all operations completed succesfully, appropriate * error otherwise. */ NTSTATUS filename_convert(TALLOC_CTX *ctx, connection_struct *conn, bool dfs_path, const char *name_in, uint32_t ucf_flags, bool *ppath_contains_wcard, struct smb_filename **pp_smb_fname) { NTSTATUS status; bool allow_wcards = (ucf_flags & (UCF_COND_ALLOW_WCARD_LCOMP|UCF_ALWAYS_ALLOW_WCARD_LCOMP)); char *fname = NULL; *pp_smb_fname = NULL; status = resolve_dfspath_wcard(ctx, conn, dfs_path, name_in, allow_wcards, &fname, ppath_contains_wcard); if (!NT_STATUS_IS_OK(status)) { DEBUG(10,("filename_convert: resolve_dfspath failed " "for name %s with %s\n", name_in, nt_errstr(status) )); return status; } if (is_fake_file_path(name_in)) { SMB_STRUCT_STAT st; ZERO_STRUCT(st); st.st_ex_nlink = 1; status = create_synthetic_smb_fname_split(ctx, name_in, &st, pp_smb_fname); return status; } /* * If the caller conditionally allows wildcard lookups, only add the * always allow if the path actually does contain a wildcard. */ if (ucf_flags & UCF_COND_ALLOW_WCARD_LCOMP && ppath_contains_wcard != NULL && *ppath_contains_wcard) { ucf_flags |= UCF_ALWAYS_ALLOW_WCARD_LCOMP; } status = unix_convert(ctx, conn, fname, pp_smb_fname, ucf_flags); if (!NT_STATUS_IS_OK(status)) { DEBUG(10,("filename_convert: unix_convert failed " "for name %s with %s\n", fname, nt_errstr(status) )); return status; } if ((ucf_flags & UCF_UNIX_NAME_LOOKUP) && VALID_STAT((*pp_smb_fname)->st) && S_ISLNK((*pp_smb_fname)->st.st_ex_mode)) { return check_veto_path(conn, (*pp_smb_fname)->base_name); } status = check_name(conn, (*pp_smb_fname)->base_name); if (!NT_STATUS_IS_OK(status)) { DEBUG(3,("filename_convert: check_name failed " "for name %s with %s\n", smb_fname_str_dbg(*pp_smb_fname), nt_errstr(status) )); TALLOC_FREE(*pp_smb_fname); return status; } return status; }
NTSTATUS file_set_sparse(connection_struct *conn, files_struct *fsp, bool sparse) { uint32_t old_dosmode; uint32_t new_dosmode; NTSTATUS status; if (!CAN_WRITE(conn)) { DEBUG(9,("file_set_sparse: fname[%s] set[%u] " "on readonly share[%s]\n", smb_fname_str_dbg(fsp->fsp_name), sparse, lp_servicename(talloc_tos(), SNUM(conn)))); return NT_STATUS_MEDIA_WRITE_PROTECTED; } /* * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the * following access flags are granted. */ if ((fsp->access_mask & (FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | SEC_FILE_APPEND_DATA)) == 0) { DEBUG(9,("file_set_sparse: fname[%s] set[%u] " "access_mask[0x%08X] - access denied\n", smb_fname_str_dbg(fsp->fsp_name), sparse, fsp->access_mask)); return NT_STATUS_ACCESS_DENIED; } if (fsp->is_directory) { DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n", (sparse ? "set" : "clear"), smb_fname_str_dbg(fsp->fsp_name))); return NT_STATUS_INVALID_PARAMETER; } if (IS_IPC(conn) || IS_PRINT(conn)) { DEBUG(9, ("attempt to %s sparse flag over invalid conn\n", (sparse ? "set" : "clear"))); return NT_STATUS_INVALID_PARAMETER; } DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n", sparse, smb_fname_str_dbg(fsp->fsp_name))); if (!lp_store_dos_attributes(SNUM(conn))) { return NT_STATUS_INVALID_DEVICE_REQUEST; } status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(status)) { return status; } old_dosmode = dos_mode(conn, fsp->fsp_name); if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) { new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE; } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) { new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE; } else { return NT_STATUS_OK; } /* Store the DOS attributes in an EA. */ if (!set_ea_dos_attribute(conn, fsp->fsp_name, new_dosmode)) { if (errno == 0) { errno = EIO; } return map_nt_error_from_unix(errno); } notify_fname(conn, NOTIFY_ACTION_MODIFIED, FILE_NOTIFY_CHANGE_ATTRIBUTES, fsp->fsp_name->base_name); fsp->is_sparse = sparse; return NT_STATUS_OK; }
uint32 dos_mode(connection_struct *conn, struct smb_filename *smb_fname) { uint32 result = 0; bool offline; DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname))); if (!VALID_STAT(smb_fname->st)) { return 0; } /* First do any modifications that depend on the path name. */ /* hide files with a name starting with a . */ if (lp_hide_dot_files(SNUM(conn))) { const char *p = strrchr_m(smb_fname->base_name,'/'); if (p) { p++; } else { p = smb_fname->base_name; } /* Only . and .. are not hidden. */ if (p[0] == '.' && !((p[1] == '\0') || (p[1] == '.' && p[2] == '\0'))) { result |= FILE_ATTRIBUTE_HIDDEN; } } /* Get the DOS attributes from an EA by preference. */ if (!get_ea_dos_attribute(conn, smb_fname, &result)) { result |= dos_mode_from_sbuf(conn, smb_fname); } offline = SMB_VFS_IS_OFFLINE(conn, smb_fname, &smb_fname->st); if (S_ISREG(smb_fname->st.st_ex_mode) && offline) { result |= FILE_ATTRIBUTE_OFFLINE; } if (conn->fs_capabilities & FILE_FILE_COMPRESSION) { bool compressed = false; NTSTATUS status = dos_mode_check_compressed(conn, smb_fname, &compressed); if (NT_STATUS_IS_OK(status) && compressed) { result |= FILE_ATTRIBUTE_COMPRESSED; } } /* Optimization : Only call is_hidden_path if it's not already hidden. */ if (!(result & FILE_ATTRIBUTE_HIDDEN) && IS_HIDDEN_PATH(conn, smb_fname->base_name)) { result |= FILE_ATTRIBUTE_HIDDEN; } if (result == 0) { result = FILE_ATTRIBUTE_NORMAL; } result = filter_mode_by_protocol(result); dos_mode_debug_print(result); return result; }
mode_t unix_mode(connection_struct *conn, int dosmode, const struct smb_filename *smb_fname, const char *inherit_from_dir) { mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH); mode_t dir_mode = 0; /* Mode of the inherit_from directory if * inheriting. */ if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) { result &= ~(S_IWUSR | S_IWGRP | S_IWOTH); } if ((inherit_from_dir != NULL) && lp_inherit_permissions(SNUM(conn))) { struct smb_filename *smb_fname_parent; DEBUG(2, ("unix_mode(%s) inheriting from %s\n", smb_fname_str_dbg(smb_fname), inherit_from_dir)); smb_fname_parent = synthetic_smb_fname( talloc_tos(), inherit_from_dir, NULL, NULL); if (smb_fname_parent == NULL) { DEBUG(1,("unix_mode(%s) failed, [dir %s]: No memory\n", smb_fname_str_dbg(smb_fname), inherit_from_dir)); return(0); } if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) { DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n", smb_fname_str_dbg(smb_fname), inherit_from_dir, strerror(errno))); TALLOC_FREE(smb_fname_parent); return(0); /* *** shouldn't happen! *** */ } /* Save for later - but explicitly remove setuid bit for safety. */ dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID; DEBUG(2,("unix_mode(%s) inherit mode %o\n", smb_fname_str_dbg(smb_fname), (int)dir_mode)); /* Clear "result" */ result = 0; TALLOC_FREE(smb_fname_parent); } if (IS_DOS_DIR(dosmode)) { /* We never make directories read only for the owner as under DOS a user can always create a file in a read-only directory. */ result |= (S_IFDIR | S_IWUSR); if (dir_mode) { /* Inherit mode of parent directory. */ result |= dir_mode; } else { /* Provisionally add all 'x' bits */ result |= (S_IXUSR | S_IXGRP | S_IXOTH); /* Apply directory mask */ result &= lp_directory_mask(SNUM(conn)); /* Add in force bits */ result |= lp_force_directory_mode(SNUM(conn)); } } else { if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode)) result |= S_IXUSR; if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode)) result |= S_IXGRP; if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode)) result |= S_IXOTH; if (dir_mode) { /* Inherit 666 component of parent directory mode */ result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH); } else { /* Apply mode mask */ result &= lp_create_mask(SNUM(conn)); /* Add in force bits */ result |= lp_force_create_mode(SNUM(conn)); } } DEBUG(3,("unix_mode(%s) returning 0%o\n", smb_fname_str_dbg(smb_fname), (int)result)); return(result); }
/** * Check if file should be recycled **/ static int recycle_unlink(vfs_handle_struct *handle, const struct smb_filename *smb_fname) { connection_struct *conn = handle->conn; char *path_name = NULL; char *temp_name = NULL; char *final_name = NULL; struct smb_filename *smb_fname_final = NULL; const char *base; char *repository = NULL; int i = 1; SMB_OFF_T maxsize, minsize; SMB_OFF_T file_size; /* space_avail; */ bool exist; NTSTATUS status; int rc = -1; repository = talloc_sub_advanced(NULL, lp_servicename(SNUM(conn)), conn->session_info->unix_name, conn->connectpath, conn->session_info->utok.gid, conn->session_info->sanitized_username, conn->session_info->info3->base.domain.string, recycle_repository(handle)); ALLOC_CHECK(repository, done); /* shouldn't we allow absolute path names here? --metze */ /* Yes :-). JRA. */ trim_char(repository, '\0', '/'); if(!repository || *(repository) == '\0') { DEBUG(3, ("recycle: repository path not set, purging %s...\n", smb_fname_str_dbg(smb_fname))); rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname); goto done; } /* we don't recycle the recycle bin... */ if (strncmp(smb_fname->base_name, repository, strlen(repository)) == 0) { DEBUG(3, ("recycle: File is within recycling bin, unlinking ...\n")); rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname); goto done; } file_size = recycle_get_file_size(handle, smb_fname); /* it is wrong to purge filenames only because they are empty imho * --- simo * if(fsize == 0) { DEBUG(3, ("recycle: File %s is empty, purging...\n", file_name)); rc = SMB_VFS_NEXT_UNLINK(handle,file_name); goto done; } */ /* FIXME: this is wrong, we should check the whole size of the recycle bin is * not greater then maxsize, not the size of the single file, also it is better * to remove older files */ maxsize = recycle_maxsize(handle); if(maxsize > 0 && file_size > maxsize) { DEBUG(3, ("recycle: File %s exceeds maximum recycle size, " "purging... \n", smb_fname_str_dbg(smb_fname))); rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname); goto done; } minsize = recycle_minsize(handle); if(minsize > 0 && file_size < minsize) { DEBUG(3, ("recycle: File %s lowers minimum recycle size, " "purging... \n", smb_fname_str_dbg(smb_fname))); rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname); goto done; } /* FIXME: this is wrong: moving files with rename does not change the disk space * allocation * space_avail = SMB_VFS_NEXT_DISK_FREE(handle, ".", True, &bsize, &dfree, &dsize) * 1024L; DEBUG(5, ("space_avail = %Lu, file_size = %Lu\n", space_avail, file_size)); if(space_avail < file_size) { DEBUG(3, ("recycle: Not enough diskspace, purging file %s\n", file_name)); rc = SMB_VFS_NEXT_UNLINK(handle, file_name); goto done; } */ /* extract filename and path */ base = strrchr(smb_fname->base_name, '/'); if (base == NULL) { base = smb_fname->base_name; path_name = SMB_STRDUP("/"); ALLOC_CHECK(path_name, done); } else { path_name = SMB_STRDUP(smb_fname->base_name); ALLOC_CHECK(path_name, done); path_name[base - smb_fname->base_name] = '\0'; base++; } /* original filename with path */ DEBUG(10, ("recycle: fname = %s\n", smb_fname_str_dbg(smb_fname))); /* original path */ DEBUG(10, ("recycle: fpath = %s\n", path_name)); /* filename without path */ DEBUG(10, ("recycle: base = %s\n", base)); if (matchparam(recycle_exclude(handle), base)) { DEBUG(3, ("recycle: file %s is excluded \n", base)); rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname); goto done; } if (matchdirparam(recycle_exclude_dir(handle), path_name)) { DEBUG(3, ("recycle: directory %s is excluded \n", path_name)); rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname); goto done; } if (recycle_keep_dir_tree(handle) == True) { if (asprintf(&temp_name, "%s/%s", repository, path_name) == -1) { ALLOC_CHECK(temp_name, done); } } else { temp_name = SMB_STRDUP(repository); } ALLOC_CHECK(temp_name, done); exist = recycle_directory_exist(handle, temp_name); if (exist) { DEBUG(10, ("recycle: Directory already exists\n")); } else { DEBUG(10, ("recycle: Creating directory %s\n", temp_name)); if (recycle_create_dir(handle, temp_name) == False) { DEBUG(3, ("recycle: Could not create directory, " "purging %s...\n", smb_fname_str_dbg(smb_fname))); rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname); goto done; } } if (asprintf(&final_name, "%s/%s", temp_name, base) == -1) { ALLOC_CHECK(final_name, done); } /* Create smb_fname with final base name and orig stream name. */ status = create_synthetic_smb_fname(talloc_tos(), final_name, smb_fname->stream_name, NULL, &smb_fname_final); if (!NT_STATUS_IS_OK(status)) { rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname); goto done; } /* new filename with path */ DEBUG(10, ("recycle: recycled file name: %s\n", smb_fname_str_dbg(smb_fname_final))); /* check if we should delete file from recycle bin */ if (recycle_file_exist(handle, smb_fname_final)) { if (recycle_versions(handle) == False || matchparam(recycle_noversions(handle), base) == True) { DEBUG(3, ("recycle: Removing old file %s from recycle " "bin\n", smb_fname_str_dbg(smb_fname_final))); if (SMB_VFS_NEXT_UNLINK(handle, smb_fname_final) != 0) { DEBUG(1, ("recycle: Error deleting old file: %s\n", strerror(errno))); } } } /* rename file we move to recycle bin */ i = 1; while (recycle_file_exist(handle, smb_fname_final)) { SAFE_FREE(final_name); if (asprintf(&final_name, "%s/Copy #%d of %s", temp_name, i++, base) == -1) { ALLOC_CHECK(final_name, done); } TALLOC_FREE(smb_fname_final->base_name); smb_fname_final->base_name = talloc_strdup(smb_fname_final, final_name); if (smb_fname_final->base_name == NULL) { rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname); goto done; } } DEBUG(10, ("recycle: Moving %s to %s\n", smb_fname_str_dbg(smb_fname), smb_fname_str_dbg(smb_fname_final))); rc = SMB_VFS_NEXT_RENAME(handle, smb_fname, smb_fname_final); if (rc != 0) { DEBUG(3, ("recycle: Move error %d (%s), purging file %s " "(%s)\n", errno, strerror(errno), smb_fname_str_dbg(smb_fname), smb_fname_str_dbg(smb_fname_final))); rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname); goto done; } /* touch access date of moved file */ if (recycle_touch(handle) == True || recycle_touch_mtime(handle)) recycle_do_touch(handle, smb_fname_final, recycle_touch_mtime(handle)); done: SAFE_FREE(path_name); SAFE_FREE(temp_name); SAFE_FREE(final_name); TALLOC_FREE(smb_fname_final); TALLOC_FREE(repository); return rc; }
static int prealloc_open(vfs_handle_struct* handle, struct smb_filename *smb_fname, files_struct * fsp, int flags, mode_t mode) { int fd; off64_t size = 0; const char * dot; char fext[10]; if (!(flags & (O_CREAT|O_TRUNC))) { /* Caller is not intending to rewrite the file. Let's not mess * with the allocation in this case. */ goto normal_open; } *fext = '\0'; dot = strrchr(smb_fname->base_name, '.'); if (dot && *++dot) { if (strlen(dot) < sizeof(fext)) { strncpy(fext, dot, sizeof(fext)); strnorm(fext, CASE_LOWER); } } if (*fext == '\0') { goto normal_open; } /* Syntax for specifying preallocation size is: * MODULE: <extension> = <size> * where * <extension> is the file extension in lower case * <size> is a size like 10, 10K, 10M */ size = conv_str_size(lp_parm_const_string(SNUM(handle->conn), MODULE, fext, NULL)); if (size <= 0) { /* No need to preallocate this file. */ goto normal_open; } fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode); if (fd < 0) { return fd; } /* Prellocate only if the file is being created or replaced. Note that * Samba won't ever pass down O_TRUNC, which is why we have to handle * truncate calls specially. */ if ((flags & O_CREAT) || (flags & O_TRUNC)) { SMB_OFF_T * psize; psize = VFS_ADD_FSP_EXTENSION(handle, fsp, SMB_OFF_T, NULL); if (psize == NULL || *psize == -1) { return fd; } DEBUG(module_debug, ("%s: preallocating %s (fd=%d) to %lld bytes\n", MODULE, smb_fname_str_dbg(smb_fname), fd, (long long)size)); *psize = size; if (preallocate_space(fd, *psize) < 0) { VFS_REMOVE_FSP_EXTENSION(handle, fsp); } } return fd; normal_open: /* We are not creating or replacing a file. Skip the * preallocation. */ DEBUG(module_debug, ("%s: skipping preallocation for %s\n", MODULE, smb_fname_str_dbg(smb_fname))); return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode); }
static bool get_ea_dos_attribute(connection_struct *conn, struct smb_filename *smb_fname, uint32 *pattr) { struct xattr_DOSATTRIB dosattrib; enum ndr_err_code ndr_err; DATA_BLOB blob; ssize_t sizeret; fstring attrstr; uint32_t dosattr; if (!lp_store_dos_attributes(SNUM(conn))) { return False; } /* Don't reset pattr to zero as we may already have filename-based attributes we need to preserve. */ sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name, SAMBA_XATTR_DOS_ATTRIB, attrstr, sizeof(attrstr)); if (sizeret == -1) { if (errno == ENOSYS #if defined(ENOTSUP) || errno == ENOTSUP) { #else ) { #endif DEBUG(1,("get_ea_dos_attribute: Cannot get attribute " "from EA on file %s: Error = %s\n", smb_fname_str_dbg(smb_fname), strerror(errno))); set_store_dos_attributes(SNUM(conn), False); } return False; } blob.data = (uint8_t *)attrstr; blob.length = sizeret; ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib, (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { DEBUG(1,("get_ea_dos_attribute: bad ndr decode " "from EA on file %s: Error = %s\n", smb_fname_str_dbg(smb_fname), ndr_errstr(ndr_err))); return false; } DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n", smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex)); switch (dosattrib.version) { case 0xFFFF: dosattr = dosattrib.info.compatinfoFFFF.attrib; break; case 1: dosattr = dosattrib.info.info1.attrib; if (!null_nttime(dosattrib.info.info1.create_time)) { struct timespec create_time = nt_time_to_unix_timespec( dosattrib.info.info1.create_time); update_stat_ex_create_time(&smb_fname->st, create_time); DEBUG(10,("get_ea_dos_attribute: file %s case 1 " "set btime %s\n", smb_fname_str_dbg(smb_fname), time_to_asc(convert_timespec_to_time_t( create_time)) )); } break; case 2: dosattr = dosattrib.info.oldinfo2.attrib; /* Don't know what flags to check for this case. */ break; case 3: dosattr = dosattrib.info.info3.attrib; if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) && !null_nttime(dosattrib.info.info3.create_time)) { struct timespec create_time = nt_time_to_unix_timespec( dosattrib.info.info3.create_time); update_stat_ex_create_time(&smb_fname->st, create_time); DEBUG(10,("get_ea_dos_attribute: file %s case 3 " "set btime %s\n", smb_fname_str_dbg(smb_fname), time_to_asc(convert_timespec_to_time_t( create_time)) )); } break; default: DEBUG(1,("get_ea_dos_attribute: Badly formed DOSATTRIB on " "file %s - %s\n", smb_fname_str_dbg(smb_fname), attrstr)); return false; } if (S_ISDIR(smb_fname->st.st_ex_mode)) { dosattr |= FILE_ATTRIBUTE_DIRECTORY; } /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */ *pattr = (uint32)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE)); DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr)); if (dosattr & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h")); if (dosattr & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r")); if (dosattr & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s")); if (dosattr & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d")); if (dosattr & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a")); DEBUG(8,("\n")); return True; } /**************************************************************************** Set DOS attributes in an EA. Also sets the create time. ****************************************************************************/ static bool set_ea_dos_attribute(connection_struct *conn, struct smb_filename *smb_fname, uint32 dosmode) { struct xattr_DOSATTRIB dosattrib; enum ndr_err_code ndr_err; DATA_BLOB blob; ZERO_STRUCT(dosattrib); ZERO_STRUCT(blob); dosattrib.version = 3; dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB| XATTR_DOSINFO_CREATE_TIME; dosattrib.info.info3.attrib = dosmode; dosattrib.info.info3.create_time = unix_timespec_to_nt_time( smb_fname->st.st_ex_btime); DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n", (unsigned int)dosmode, time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)), smb_fname_str_dbg(smb_fname) )); ndr_err = ndr_push_struct_blob( &blob, talloc_tos(), &dosattrib, (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n", ndr_errstr(ndr_err))); return false; } if (blob.data == NULL || blob.length == 0) { return false; } if (SMB_VFS_SETXATTR(conn, smb_fname->base_name, SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length, 0) == -1) { bool ret = false; bool need_close = false; files_struct *fsp = NULL; if((errno != EPERM) && (errno != EACCES)) { if (errno == ENOSYS #if defined(ENOTSUP) || errno == ENOTSUP) { #else ) { #endif DEBUG(1,("set_ea_dos_attributes: Cannot set " "attribute EA on file %s: Error = %s\n", smb_fname_str_dbg(smb_fname), strerror(errno) )); set_store_dos_attributes(SNUM(conn), False); } return false; } /* We want DOS semantics, ie allow non owner with write permission to change the bits on a file. Just like file_ntimes below. */ /* Check if we have write access. */ if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn))) return false; if (!can_write_to_file(conn, smb_fname)) { return false; } /* * We need to get an open file handle to do the * metadata operation under root. */ if (!NT_STATUS_IS_OK(get_file_handle_for_metadata(conn, smb_fname, &fsp, &need_close))) { return false; } become_root(); if (SMB_VFS_FSETXATTR(fsp, SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length, 0) == 0) { ret = true; } unbecome_root(); if (need_close) { close_file(NULL, fsp, NORMAL_CLOSE); } return ret; } DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n", (unsigned int)dosmode, smb_fname_str_dbg(smb_fname))); return true; } /**************************************************************************** Change a unix mode to a dos mode for an ms dfs link. ****************************************************************************/ uint32 dos_mode_msdfs(connection_struct *conn, const struct smb_filename *smb_fname) { uint32 result = 0; DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname))); if (!VALID_STAT(smb_fname->st)) { return 0; } /* First do any modifications that depend on the path name. */ /* hide files with a name starting with a . */ if (lp_hide_dot_files(SNUM(conn))) { const char *p = strrchr_m(smb_fname->base_name, '/'); if (p) { p++; } else { p = smb_fname->base_name; } /* Only . and .. are not hidden. */ if (p[0] == '.' && !((p[1] == '\0') || (p[1] == '.' && p[2] == '\0'))) { result |= FILE_ATTRIBUTE_HIDDEN; } } result |= dos_mode_from_sbuf(conn, smb_fname); /* Optimization : Only call is_hidden_path if it's not already hidden. */ if (!(result & FILE_ATTRIBUTE_HIDDEN) && IS_HIDDEN_PATH(conn, smb_fname->base_name)) { result |= FILE_ATTRIBUTE_HIDDEN; } if (result == 0) { result = FILE_ATTRIBUTE_NORMAL; } result = filter_mode_by_protocol(result); /* * Add in that it is a reparse point */ result |= FILE_ATTRIBUTE_REPARSE_POINT; DEBUG(8,("dos_mode_msdfs returning ")); if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h")); if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r")); if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s")); if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d")); if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a")); if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]")); DEBUG(8,("\n")); return(result); }
bool can_delete_file_in_directory(connection_struct *conn, const struct smb_filename *smb_fname) { TALLOC_CTX *ctx = talloc_tos(); char *dname = NULL; struct smb_filename *smb_fname_parent = NULL; NTSTATUS status; bool ret; if (!CAN_WRITE(conn)) { return False; } if (!lp_acl_check_permissions(SNUM(conn))) { /* This option means don't check. */ return true; } /* Get the parent directory permission mask and owners. */ if (!parent_dirname(ctx, smb_fname->base_name, &dname, NULL)) { return False; } status = create_synthetic_smb_fname(ctx, dname, NULL, NULL, &smb_fname_parent); if (!NT_STATUS_IS_OK(status)) { ret = false; goto out; } if(SMB_VFS_STAT(conn, smb_fname_parent) != 0) { ret = false; goto out; } /* fast paths first */ if (!S_ISDIR(smb_fname_parent->st.st_ex_mode)) { ret = false; goto out; } if (get_current_uid(conn) == (uid_t)0) { /* I'm sorry sir, I didn't know you were root... */ ret = true; goto out; } #ifdef S_ISVTX /* sticky bit means delete only by owner of file or by root or * by owner of directory. */ if (smb_fname_parent->st.st_ex_mode & S_ISVTX) { if (!VALID_STAT(smb_fname->st)) { /* If the file doesn't already exist then * yes we'll be able to delete it. */ ret = true; goto out; } /* * Patch from SATOH Fumiyasu <*****@*****.**> * for bug #3348. Don't assume owning sticky bit * directory means write access allowed. * Fail to delete if we're not the owner of the file, * or the owner of the directory as we have no possible * chance of deleting. Otherwise, go on and check the ACL. */ if ((get_current_uid(conn) != smb_fname_parent->st.st_ex_uid) && (get_current_uid(conn) != smb_fname->st.st_ex_uid)) { DEBUG(10,("can_delete_file_in_directory: not " "owner of file %s or directory %s", smb_fname_str_dbg(smb_fname), smb_fname_str_dbg(smb_fname_parent))); ret = false; goto out; } } #endif /* now for ACL checks */ /* * There's two ways to get the permission to delete a file: First by * having the DELETE bit on the file itself and second if that does * not help, by the DELETE_CHILD bit on the containing directory. * * Here we only check the directory permissions, we will * check the file DELETE permission separately. */ ret = NT_STATUS_IS_OK(smbd_check_access_rights(conn, smb_fname_parent, FILE_DELETE_CHILD)); out: TALLOC_FREE(dname); TALLOC_FREE(smb_fname_parent); return ret; }
int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname, uint32 dosmode, const char *parent_dir, bool newfile) { int mask=0; mode_t tmp; mode_t unixmode; int ret = -1, lret = -1; uint32_t old_mode; struct timespec new_create_timespec; files_struct *fsp = NULL; bool need_close = false; NTSTATUS status; if (!CAN_WRITE(conn)) { errno = EROFS; return -1; } /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */ dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE); DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, smb_fname_str_dbg(smb_fname))); unixmode = smb_fname->st.st_ex_mode; get_acl_group_bits(conn, smb_fname->base_name, &smb_fname->st.st_ex_mode); if (S_ISDIR(smb_fname->st.st_ex_mode)) dosmode |= FILE_ATTRIBUTE_DIRECTORY; else dosmode &= ~FILE_ATTRIBUTE_DIRECTORY; new_create_timespec = smb_fname->st.st_ex_btime; old_mode = dos_mode(conn, smb_fname); if ((dosmode & FILE_ATTRIBUTE_OFFLINE) && !(old_mode & FILE_ATTRIBUTE_OFFLINE)) { lret = SMB_VFS_SET_OFFLINE(conn, smb_fname); if (lret == -1) { if (errno == ENOTSUP) { DEBUG(10, ("Setting FILE_ATTRIBUTE_OFFLINE for " "%s/%s is not supported.\n", parent_dir, smb_fname_str_dbg(smb_fname))); } else { DEBUG(0, ("An error occurred while setting " "FILE_ATTRIBUTE_OFFLINE for " "%s/%s: %s", parent_dir, smb_fname_str_dbg(smb_fname), strerror(errno))); } } } dosmode &= ~FILE_ATTRIBUTE_OFFLINE; old_mode &= ~FILE_ATTRIBUTE_OFFLINE; smb_fname->st.st_ex_btime = new_create_timespec; /* Store the DOS attributes in an EA by preference. */ if (lp_store_dos_attributes(SNUM(conn))) { /* * Don't fall back to using UNIX modes. Finally * follow the smb.conf manpage. */ if (!set_ea_dos_attribute(conn, smb_fname, dosmode)) { return -1; } if (!newfile) { notify_fname(conn, NOTIFY_ACTION_MODIFIED, FILE_NOTIFY_CHANGE_ATTRIBUTES, smb_fname->base_name); } smb_fname->st.st_ex_mode = unixmode; return 0; } unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir); /* preserve the file type bits */ mask |= S_IFMT; /* preserve the s bits */ mask |= (S_ISUID | S_ISGID); /* preserve the t bit */ #ifdef S_ISVTX mask |= S_ISVTX; #endif /* possibly preserve the x bits */ if (!MAP_ARCHIVE(conn)) mask |= S_IXUSR; if (!MAP_SYSTEM(conn)) mask |= S_IXGRP; if (!MAP_HIDDEN(conn)) mask |= S_IXOTH; unixmode |= (smb_fname->st.st_ex_mode & mask); /* if we previously had any r bits set then leave them alone */ if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) { unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH); unixmode |= tmp; } /* if we previously had any w bits set then leave them alone whilst adding in the new w bits, if the new mode is not rdonly */ if (!IS_DOS_READONLY(dosmode)) { unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH)); } /* * From the chmod 2 man page: * * "If the calling process is not privileged, and the group of the file * does not match the effective group ID of the process or one of its * supplementary group IDs, the S_ISGID bit will be turned off, but * this will not cause an error to be returned." * * Simply refuse to do the chmod in this case. */ if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) && geteuid() != sec_initial_uid() && !current_user_in_group(conn, smb_fname->st.st_ex_gid)) { DEBUG(3,("file_set_dosmode: setgid bit cannot be " "set for directory %s\n", smb_fname_str_dbg(smb_fname))); errno = EPERM; return -1; } ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode); if (ret == 0) { if(!newfile || (lret != -1)) { notify_fname(conn, NOTIFY_ACTION_MODIFIED, FILE_NOTIFY_CHANGE_ATTRIBUTES, smb_fname->base_name); } smb_fname->st.st_ex_mode = unixmode; return 0; } if((errno != EPERM) && (errno != EACCES)) return -1; if(!lp_dos_filemode(SNUM(conn))) return -1; /* We want DOS semantics, ie allow non owner with write permission to change the bits on a file. Just like file_ntimes below. */ if (!can_write_to_file(conn, smb_fname)) { errno = EACCES; return -1; } /* * We need to get an open file handle to do the * metadata operation under root. */ status = get_file_handle_for_metadata(conn, smb_fname, &fsp, &need_close); if (!NT_STATUS_IS_OK(status)) { errno = map_errno_from_nt_status(status); return -1; } become_root(); ret = SMB_VFS_FCHMOD(fsp, unixmode); unbecome_root(); if (need_close) { close_file(NULL, fsp, NORMAL_CLOSE); } if (!newfile) { notify_fname(conn, NOTIFY_ACTION_MODIFIED, FILE_NOTIFY_CHANGE_ATTRIBUTES, smb_fname->base_name); } if (ret == 0) { smb_fname->st.st_ex_mode = unixmode; } 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; }
NTSTATUS delete_all_streams(connection_struct *conn, const char *fname) { struct stream_struct *stream_info = NULL; int i; unsigned int num_streams = 0; TALLOC_CTX *frame = talloc_stackframe(); NTSTATUS status; status = vfs_streaminfo(conn, NULL, fname, talloc_tos(), &num_streams, &stream_info); if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) { DEBUG(10, ("no streams around\n")); TALLOC_FREE(frame); return NT_STATUS_OK; } if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("vfs_streaminfo failed: %s\n", nt_errstr(status))); goto fail; } DEBUG(10, ("delete_all_streams found %d streams\n", num_streams)); if (num_streams == 0) { TALLOC_FREE(frame); return NT_STATUS_OK; } for (i=0; i<num_streams; i++) { int res; struct smb_filename *smb_fname_stream; if (strequal(stream_info[i].name, "::$DATA")) { continue; } smb_fname_stream = synthetic_smb_fname( talloc_tos(), fname, stream_info[i].name, NULL); if (smb_fname_stream == NULL) { DEBUG(0, ("talloc_aprintf failed\n")); status = NT_STATUS_NO_MEMORY; goto fail; } res = SMB_VFS_UNLINK(conn, smb_fname_stream); if (res == -1) { status = map_nt_error_from_unix(errno); DEBUG(10, ("Could not delete stream %s: %s\n", smb_fname_str_dbg(smb_fname_stream), strerror(errno))); TALLOC_FREE(smb_fname_stream); break; } TALLOC_FREE(smb_fname_stream); } fail: TALLOC_FREE(frame); return status; }
NTSTATUS unix_convert(TALLOC_CTX *ctx, connection_struct *conn, const char *orig_path, struct smb_filename **smb_fname_out, uint32_t ucf_flags) { struct smb_filename *smb_fname = NULL; char *start, *end; char *dirpath = NULL; char *stream = NULL; bool component_was_mangled = False; bool name_has_wildcard = False; bool posix_pathnames = false; bool allow_wcard_last_component = (ucf_flags & UCF_ALWAYS_ALLOW_WCARD_LCOMP); bool save_last_component = ucf_flags & UCF_SAVE_LCOMP; NTSTATUS status; int ret = -1; *smb_fname_out = NULL; smb_fname = talloc_zero(ctx, struct smb_filename); if (smb_fname == NULL) { return NT_STATUS_NO_MEMORY; } if (conn->printer) { /* we don't ever use the filenames on a printer share as a filename - so don't convert them */ if (!(smb_fname->base_name = talloc_strdup(smb_fname, orig_path))) { status = NT_STATUS_NO_MEMORY; goto err; } goto done; } DEBUG(5, ("unix_convert called on file \"%s\"\n", orig_path)); /* * Conversion to basic unix format is already done in * check_path_syntax(). */ /* * Names must be relative to the root of the service - any leading /. * and trailing /'s should have been trimmed by check_path_syntax(). */ #ifdef DEVELOPER SMB_ASSERT(*orig_path != '/'); #endif /* * If we trimmed down to a single '\0' character * then we should use the "." directory to avoid * searching the cache, but not if we are in a * printing share. * As we know this is valid we can return true here. */ if (!*orig_path) { if (!(smb_fname->base_name = talloc_strdup(smb_fname, "."))) { status = NT_STATUS_NO_MEMORY; goto err; } if (SMB_VFS_STAT(conn, smb_fname) != 0) { status = map_nt_error_from_unix(errno); goto err; } DEBUG(5, ("conversion finished \"\" -> %s\n", smb_fname->base_name)); goto done; } if (orig_path[0] == '.' && (orig_path[1] == '/' || orig_path[1] == '\0')) { /* Start of pathname can't be "." only. */ if (orig_path[1] == '\0' || orig_path[2] == '\0') { status = NT_STATUS_OBJECT_NAME_INVALID; } else { status =determine_path_error(&orig_path[2], allow_wcard_last_component); } goto err; } /* Start with the full orig_path as given by the caller. */ if (!(smb_fname->base_name = talloc_strdup(smb_fname, orig_path))) { DEBUG(0, ("talloc_strdup failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } /* * Large directory fix normalization. If we're case sensitive, and * the case preserving parameters are set to "no", normalize the case of * the incoming filename from the client WHETHER IT EXISTS OR NOT ! * This is in conflict with the current (3.0.20) man page, but is * what people expect from the "large directory howto". I'll update * the man page. Thanks to [email protected] for finding this. JRA. */ if (conn->case_sensitive && !conn->case_preserve && !conn->short_case_preserve) { strnorm(smb_fname->base_name, lp_defaultcase(SNUM(conn))); } /* * Ensure saved_last_component is valid even if file exists. */ if(save_last_component) { end = strrchr_m(smb_fname->base_name, '/'); if (end) { smb_fname->original_lcomp = talloc_strdup(smb_fname, end + 1); } else { smb_fname->original_lcomp = talloc_strdup(smb_fname, smb_fname->base_name); } if (smb_fname->original_lcomp == NULL) { status = NT_STATUS_NO_MEMORY; goto err; } } posix_pathnames = (lp_posix_pathnames() || (ucf_flags & UCF_POSIX_PATHNAMES)); /* * Strip off the stream, and add it back when we're done with the * base_name. */ if (!posix_pathnames) { stream = strchr_m(smb_fname->base_name, ':'); if (stream != NULL) { char *tmp = talloc_strdup(smb_fname, stream); if (tmp == NULL) { status = NT_STATUS_NO_MEMORY; goto err; } /* * Since this is actually pointing into * smb_fname->base_name this truncates base_name. */ *stream = '\0'; stream = tmp; } } start = smb_fname->base_name; /* * If we're providing case insensitive semantics or * the underlying filesystem is case insensitive, * then a case-normalized hit in the stat-cache is * authoratitive. JRA. * * Note: We're only checking base_name. The stream_name will be * added and verified in build_stream_path(). */ if((!conn->case_sensitive || !(conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) && stat_cache_lookup(conn, posix_pathnames, &smb_fname->base_name, &dirpath, &start, &smb_fname->st)) { goto done; } /* * Make sure "dirpath" is an allocated string, we use this for * building the directories with talloc_asprintf and free it. */ if ((dirpath == NULL) && (!(dirpath = talloc_strdup(ctx,"")))) { DEBUG(0, ("talloc_strdup failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } /* * If we have a wildcard we must walk the path to * find where the error is, even if case sensitive * is true. */ name_has_wildcard = ms_has_wild(smb_fname->base_name); if (name_has_wildcard && !allow_wcard_last_component) { /* Wildcard not valid anywhere. */ status = NT_STATUS_OBJECT_NAME_INVALID; goto fail; } DEBUG(5,("unix_convert begin: name = %s, dirpath = %s, start = %s\n", smb_fname->base_name, dirpath, start)); if (!name_has_wildcard) { /* * stat the name - if it exists then we can add the stream back (if * there was one) and be done! */ if (posix_pathnames) { ret = SMB_VFS_LSTAT(conn, smb_fname); } else { ret = SMB_VFS_STAT(conn, smb_fname); } if (ret == 0) { status = check_for_dot_component(smb_fname); if (!NT_STATUS_IS_OK(status)) { goto fail; } /* Add the path (not including the stream) to the cache. */ stat_cache_add(orig_path, smb_fname->base_name, conn->case_sensitive); DEBUG(5,("conversion of base_name finished %s -> %s\n", orig_path, smb_fname->base_name)); goto done; } /* Stat failed - ensure we don't use it. */ SET_STAT_INVALID(smb_fname->st); if (errno == ENOENT) { /* Optimization when creating a new file - only the last component doesn't exist. */ status = check_parent_exists(ctx, conn, posix_pathnames, smb_fname, &dirpath, &start); if (!NT_STATUS_IS_OK(status)) { goto fail; } } /* * A special case - if we don't have any wildcards or mangling chars and are case * sensitive or the underlying filesystem is case insensitive then searching * won't help. */ if ((conn->case_sensitive || !(conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) && !mangle_is_mangled(smb_fname->base_name, conn->params)) { status = check_for_dot_component(smb_fname); if (!NT_STATUS_IS_OK(status)) { goto fail; } /* * The stat failed. Could be ok as it could be * a new file. */ if (errno == ENOTDIR || errno == ELOOP) { status = NT_STATUS_OBJECT_PATH_NOT_FOUND; goto fail; } else if (errno == ENOENT) { /* * Was it a missing last component ? * or a missing intermediate component ? */ struct smb_filename parent_fname; const char *last_component = NULL; ZERO_STRUCT(parent_fname); if (!parent_dirname(ctx, smb_fname->base_name, &parent_fname.base_name, &last_component)) { status = NT_STATUS_NO_MEMORY; goto fail; } if (posix_pathnames) { ret = SMB_VFS_LSTAT(conn, &parent_fname); } else { ret = SMB_VFS_STAT(conn, &parent_fname); } if (ret == -1) { if (errno == ENOTDIR || errno == ENOENT || errno == ELOOP) { status = NT_STATUS_OBJECT_PATH_NOT_FOUND; goto fail; } } /* * Missing last component is ok - new file. * Also deal with permission denied elsewhere. * Just drop out to done. */ goto done; } } } else { /* * We have a wildcard in the pathname. * * Optimization for common case where the wildcard * is in the last component and the client already * sent the correct case. */ status = check_parent_exists(ctx, conn, posix_pathnames, smb_fname, &dirpath, &start); if (!NT_STATUS_IS_OK(status)) { goto fail; } } /* * is_mangled() was changed to look at an entire pathname, not * just a component. JRA. */ if (mangle_is_mangled(start, conn->params)) { component_was_mangled = True; } /* * Now we need to recursively match the name against the real * directory structure. */ /* * Match each part of the path name separately, trying the names * as is first, then trying to scan the directory for matching names. */ for (; start ; start = (end?end+1:(char *)NULL)) { /* * Pinpoint the end of this section of the filename. */ /* mb safe. '/' can't be in any encoded char. */ end = strchr(start, '/'); /* * Chop the name at this point. */ if (end) { *end = 0; } if (save_last_component) { TALLOC_FREE(smb_fname->original_lcomp); smb_fname->original_lcomp = talloc_strdup(smb_fname, end ? end + 1 : start); if (!smb_fname->original_lcomp) { DEBUG(0, ("talloc failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } } /* The name cannot have a component of "." */ if (ISDOT(start)) { if (!end) { /* Error code at the end of a pathname. */ status = NT_STATUS_OBJECT_NAME_INVALID; } else { status = determine_path_error(end+1, allow_wcard_last_component); } goto fail; } /* The name cannot have a wildcard if it's not the last component. */ name_has_wildcard = ms_has_wild(start); /* Wildcards never valid within a pathname. */ if (name_has_wildcard && end) { status = NT_STATUS_OBJECT_NAME_INVALID; goto fail; } /* Skip the stat call if it's a wildcard end. */ if (name_has_wildcard) { DEBUG(5,("Wildcard %s\n",start)); goto done; } /* * Check if the name exists up to this point. */ if (posix_pathnames) { ret = SMB_VFS_LSTAT(conn, smb_fname); } else { ret = SMB_VFS_STAT(conn, smb_fname); } if (ret == 0) { /* * It exists. it must either be a directory or this must * be the last part of the path for it to be OK. */ if (end && !S_ISDIR(smb_fname->st.st_ex_mode)) { /* * An intermediate part of the name isn't * a directory. */ DEBUG(5,("Not a dir %s\n",start)); *end = '/'; /* * We need to return the fact that the * intermediate name resolution failed. This * is used to return an error of ERRbadpath * rather than ERRbadfile. Some Windows * applications depend on the difference between * these two errors. */ status = NT_STATUS_OBJECT_PATH_NOT_FOUND; goto fail; } } else { char *found_name = NULL; /* Stat failed - ensure we don't use it. */ SET_STAT_INVALID(smb_fname->st); /* * Reset errno so we can detect * directory open errors. */ errno = 0; /* * Try to find this part of the path in the directory. */ if (name_has_wildcard || (get_real_filename(conn, dirpath, start, talloc_tos(), &found_name) == -1)) { char *unmangled; if (end) { /* * An intermediate part of the name * can't be found. */ DEBUG(5,("Intermediate not found %s\n", start)); *end = '/'; /* * We need to return the fact that the * intermediate name resolution failed. * This is used to return an error of * ERRbadpath rather than ERRbadfile. * Some Windows applications depend on * the difference between these two * errors. */ /* * ENOENT, ENOTDIR and ELOOP all map * to NT_STATUS_OBJECT_PATH_NOT_FOUND * in the filename walk. */ if (errno == ENOENT || errno == ENOTDIR || errno == ELOOP) { status = NT_STATUS_OBJECT_PATH_NOT_FOUND; } else { status = map_nt_error_from_unix(errno); } goto fail; } /* * ENOENT/EACCESS are the only valid errors * here. EACCESS needs handling here for * "dropboxes", i.e. directories where users * can only put stuff with permission -wx. */ if ((errno != 0) && (errno != ENOENT) && (errno != EACCES)) { /* * ENOTDIR and ELOOP both map to * NT_STATUS_OBJECT_PATH_NOT_FOUND * in the filename walk. */ if (errno == ENOTDIR || errno == ELOOP) { status = NT_STATUS_OBJECT_PATH_NOT_FOUND; } else { status = map_nt_error_from_unix(errno); } goto fail; } /* * Just the last part of the name doesn't exist. * We need to strupper() or strlower() it as * this conversion may be used for file creation * purposes. Fix inspired by * Thomas Neumann <*****@*****.**>. */ if (!conn->case_preserve || (mangle_is_8_3(start, False, conn->params) && !conn->short_case_preserve)) { strnorm(start, lp_defaultcase(SNUM(conn))); } /* * check on the mangled stack to see if we can * recover the base of the filename. */ if (mangle_is_mangled(start, conn->params) && mangle_lookup_name_from_8_3(ctx, start, &unmangled, conn->params)) { char *tmp; size_t start_ofs = start - smb_fname->base_name; if (*dirpath != '\0') { tmp = talloc_asprintf( smb_fname, "%s/%s", dirpath, unmangled); TALLOC_FREE(unmangled); } else { tmp = unmangled; } if (tmp == NULL) { DEBUG(0, ("talloc failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } TALLOC_FREE(smb_fname->base_name); smb_fname->base_name = tmp; start = smb_fname->base_name + start_ofs; end = start + strlen(start); } DEBUG(5,("New file %s\n",start)); goto done; } /* * Restore the rest of the string. If the string was * mangled the size may have changed. */ if (end) { char *tmp; size_t start_ofs = start - smb_fname->base_name; if (*dirpath != '\0') { tmp = talloc_asprintf(smb_fname, "%s/%s/%s", dirpath, found_name, end+1); } else { tmp = talloc_asprintf(smb_fname, "%s/%s", found_name, end+1); } if (tmp == NULL) { DEBUG(0, ("talloc_asprintf failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } TALLOC_FREE(smb_fname->base_name); smb_fname->base_name = tmp; start = smb_fname->base_name + start_ofs; end = start + strlen(found_name); *end = '\0'; } else { char *tmp; size_t start_ofs = start - smb_fname->base_name; if (*dirpath != '\0') { tmp = talloc_asprintf(smb_fname, "%s/%s", dirpath, found_name); } else { tmp = talloc_strdup(smb_fname, found_name); } if (tmp == NULL) { DEBUG(0, ("talloc failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } TALLOC_FREE(smb_fname->base_name); smb_fname->base_name = tmp; start = smb_fname->base_name + start_ofs; /* * We just scanned for, and found the end of * the path. We must return a valid stat struct * if it exists. JRA. */ if (posix_pathnames) { ret = SMB_VFS_LSTAT(conn, smb_fname); } else { ret = SMB_VFS_STAT(conn, smb_fname); } if (ret != 0) { SET_STAT_INVALID(smb_fname->st); } } TALLOC_FREE(found_name); } /* end else */ #ifdef DEVELOPER /* * This sucks! * We should never provide different behaviors * depending on DEVELOPER!!! */ if (VALID_STAT(smb_fname->st)) { bool delete_pending; uint32_t name_hash; status = file_name_hash(conn, smb_fname_str_dbg(smb_fname), &name_hash); if (!NT_STATUS_IS_OK(status)) { goto fail; } get_file_infos(vfs_file_id_from_sbuf(conn, &smb_fname->st), name_hash, &delete_pending, NULL); if (delete_pending) { status = NT_STATUS_DELETE_PENDING; goto fail; } } #endif /* * Add to the dirpath that we have resolved so far. */ if (*dirpath != '\0') { char *tmp = talloc_asprintf(ctx, "%s/%s", dirpath, start); if (!tmp) { DEBUG(0, ("talloc_asprintf failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } TALLOC_FREE(dirpath); dirpath = tmp; } else { TALLOC_FREE(dirpath); if (!(dirpath = talloc_strdup(ctx,start))) { DEBUG(0, ("talloc_strdup failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } } /* * Cache the dirpath thus far. Don't cache a name with mangled * or wildcard components as this can change the size. */ if(!component_was_mangled && !name_has_wildcard) { stat_cache_add(orig_path, dirpath, conn->case_sensitive); } /* * Restore the / that we wiped out earlier. */ if (end) { *end = '/'; } } /* * Cache the full path. Don't cache a name with mangled or wildcard * components as this can change the size. */ if(!component_was_mangled && !name_has_wildcard) { stat_cache_add(orig_path, smb_fname->base_name, conn->case_sensitive); } /* * The name has been resolved. */ DEBUG(5,("conversion finished %s -> %s\n", orig_path, smb_fname->base_name)); done: /* Add back the stream if one was stripped off originally. */ if (stream != NULL) { smb_fname->stream_name = stream; /* Check path now that the base_name has been converted. */ status = build_stream_path(ctx, conn, orig_path, smb_fname); if (!NT_STATUS_IS_OK(status)) { goto fail; } } TALLOC_FREE(dirpath); *smb_fname_out = smb_fname; return NT_STATUS_OK; fail: DEBUG(10, ("dirpath = [%s] start = [%s]\n", dirpath, start)); if (*dirpath != '\0') { smb_fname->base_name = talloc_asprintf(smb_fname, "%s/%s", dirpath, start); } else { smb_fname->base_name = talloc_strdup(smb_fname, start); } if (!smb_fname->base_name) { DEBUG(0, ("talloc_asprintf failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } *smb_fname_out = smb_fname; TALLOC_FREE(dirpath); return status; err: TALLOC_FREE(smb_fname); return status; }