NTSTATUS dup_file_fsp(struct smb_request *req, files_struct *from, uint32 access_mask, uint32 share_access, uint32 create_options, files_struct *to) { TALLOC_FREE(to->fh); to->fh = from->fh; to->fh->ref_count++; to->file_id = from->file_id; to->initial_allocation_size = from->initial_allocation_size; to->file_pid = from->file_pid; to->vuid = from->vuid; to->open_time = from->open_time; to->access_mask = access_mask; to->share_access = share_access; to->oplock_type = from->oplock_type; to->can_lock = from->can_lock; to->can_read = (access_mask & (FILE_READ_DATA)) ? True : False; if (!CAN_WRITE(from->conn)) { to->can_write = False; } else { to->can_write = (access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) ? True : False; } to->modified = from->modified; to->is_directory = from->is_directory; to->aio_write_behind = from->aio_write_behind; if (from->print_file) { to->print_file = talloc(to, struct print_file_data); if (!to->print_file) return NT_STATUS_NO_MEMORY; to->print_file->rap_jobid = from->print_file->rap_jobid; } else {
NTSTATUS dup_file_fsp(struct smb_request *req, files_struct *from, uint32 access_mask, uint32 share_access, uint32 create_options, files_struct *to) { /* this can never happen for print files */ SMB_ASSERT(from->print_file == NULL); TALLOC_FREE(to->fh); to->fh = from->fh; to->fh->ref_count++; to->file_id = from->file_id; to->initial_allocation_size = from->initial_allocation_size; to->file_pid = from->file_pid; to->vuid = from->vuid; to->open_time = from->open_time; to->access_mask = access_mask; to->share_access = share_access; to->oplock_type = from->oplock_type; to->can_lock = from->can_lock; to->can_read = ((access_mask & FILE_READ_DATA) != 0); to->can_write = CAN_WRITE(from->conn) && ((access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) != 0); to->modified = from->modified; to->is_directory = from->is_directory; to->aio_write_behind = from->aio_write_behind; return fsp_set_smb_fname(to, from->fsp_name); }
int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname, struct smb_file_time *ft) { int ret = -1; errno = 0; DEBUG(6, ("file_ntime: actime: %s", time_to_asc(convert_timespec_to_time_t(ft->atime)))); DEBUG(6, ("file_ntime: modtime: %s", time_to_asc(convert_timespec_to_time_t(ft->mtime)))); DEBUG(6, ("file_ntime: ctime: %s", time_to_asc(convert_timespec_to_time_t(ft->ctime)))); DEBUG(6, ("file_ntime: createtime: %s", time_to_asc(convert_timespec_to_time_t(ft->create_time)))); /* Don't update the time on read-only shares */ /* We need this as set_filetime (which can be called on close and other paths) can end up calling this function without the NEED_WRITE protection. Found by : Leo Weppelman <*****@*****.**> */ if (!CAN_WRITE(conn)) { return 0; } if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) { return 0; } if((errno != EPERM) && (errno != EACCES)) { return -1; } if(!lp_dos_filetimes(SNUM(conn))) { return -1; } /* We have permission (given by the Samba admin) to break POSIX semantics and allow a user to change the time on a file they don't own but can write to (as DOS does). */ /* Check if we have write access. */ if (can_write_to_file(conn, smb_fname)) { /* We are allowed to become root and change the filetime. */ become_root(); ret = SMB_VFS_NTIMES(conn, smb_fname, ft); unbecome_root(); } return ret; }
apdb_record_t apdb_add_end(apdb_t* db, const void* content) { index_t index, *indexp; int content_len; assert(NULL != db && ADD == db->action && CAN_WRITE(db) && db->next_length == db->next_written); assert(db->index_file_len <= db->index_mmap_len); db->action = NONE; if (db->error) return -1; ERROR_IF(db->next_length != db->next_written, "write less data than expected"); pad_data_file(db); if (db->error) return -1; index.guard = GUARD; index.flags = WRITING; index.id = db->next_id; index.length = db->next_length + sizeof(data_t); index.offset = db->next_offset; ERRORP_IF(sizeof(index_t) != writen(db->index_fd, &index, sizeof(index_t)), "failed to write"); content_len = db->index_len - sizeof(index_t); ERRORP_IF(content_len != writen(db->index_fd, content, content_len), "failed to write index content"); db->index_file_len += db->index_len; remap_index_file(db, SIZE_MAX); if (db->error) return -1; indexp = RECORD_TO_INDEX(db, db->index_file_len - db->index_len); assert(GUARD == indexp->guard && WRITING == indexp->flags && index.id == indexp->id && index.length == indexp->length && index.offset == indexp->offset); indexp->flags = 0; return INDEX_TO_RECORD(db, indexp); L_error: db->error = -1; return -1; }
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; }
void apdb_delete(apdb_t* db, apdb_record_t record) { index_t* index; assert(NULL != db && record >= 0 && CAN_WRITE(db)); assert(db->index_file_len <= db->index_mmap_len); index = RECORD_TO_INDEX(db, record); assert(GUARD == index->guard); index->flags |= DELETED; }
/** * make sure range [0, min(to, st_size)) of index file is mapped. * * @note Writer process always knows the current real size of index * file, but reader processes don't, so we need update @c index_file_len * when map window can't fully cover [0, to), the caller should * check @c index_file_len against @c to after calling this function to * make sure not access data out of file ending. */ static void remap_index_file(apdb_t* db, size_t to) { struct stat st; assert(NULL != db && 0 == db->error); if (! CAN_WRITE(db)) { /* * can't be to <= db->index_mmap_len, because st_size * can be changed by writer process and reader processes * use index_file_len to calculate count of records in * apdb_first() and apdb_next(). */ if (to <= db->index_file_len) return; ERRORP_IF(-1 == fstat(db->index_fd, &st), "failed to fstat"); db->index_file_len = st.st_size; } else if (to <= db->data_mmap_len) { /* * @note NEVER return if @c to <= db->index_file_len for writer * process because writer process increases index_file_len when * adding record but doesn't update map window before finishing. */ return; } if (db->index_mmap_len < db->index_file_len) { ERRORP_IF(-1 == munmap(db->index_mmap, db->index_mmap_len), "failed to munmap"); db->index_mmap_len = db->index_file_len + INDEX_EXPAND_SIZE; /* mmap PROT_READ for client and PROT_READ | PROT_WRITE for server */ db->index_mmap = mmap(NULL, db->index_mmap_len, db->prot, MAP_SHARED, db->index_fd, 0); ERRORP_IF(MAP_FAILED == db->index_mmap, "failed to mmap"); } return; L_error: db->error = -1; }
int file_ntimes(connection_struct *conn, const char *fname, const struct timespec ts[2]) { SMB_STRUCT_STAT sbuf; int ret = -1; errno = 0; ZERO_STRUCT(sbuf); /* Don't update the time on read-only shares */ /* We need this as set_filetime (which can be called on close and other paths) can end up calling this function without the NEED_WRITE protection. Found by : Leo Weppelman <*****@*****.**> */ if (!CAN_WRITE(conn)) { return 0; } if(SMB_VFS_NTIMES(conn, fname, ts) == 0) { return 0; } if((errno != EPERM) && (errno != EACCES)) { return -1; } if(!lp_dos_filetimes(SNUM(conn))) { return -1; } /* We have permission (given by the Samba admin) to break POSIX semantics and allow a user to change the time on a file they don't own but can write to (as DOS does). */ /* Check if we have write access. */ if (can_write_to_file(conn, fname, &sbuf)) { /* We are allowed to become root and change the filetime. */ become_root(); ret = SMB_VFS_NTIMES(conn, fname, ts); unbecome_root(); } return ret; }
unsigned apdb_add_begin(apdb_t* db, size_t length) { data_t data; assert(NULL != db && NONE == db->action && CAN_WRITE(db)); assert(db->index_file_len <= db->index_mmap_len); if (db->error) return -1; if (SSIZE_MAX - db->index_len <= db->index_file_len) { DEBUG("index file too big"); return -1; } if (SSIZE_MAX - ALIGN_UP(sizeof(data_t) + length, ALIGN_SIZE) <= db->data_file_len) { DEBUG("data file too big"); return -1; } db->next_id = get_next_id(db); if (db->error || UINT_MAX == db->next_id /* avoid overflow */) return -1; db->next_length = length; db->next_offset = db->data_file_len; db->next_written = 0; data.guard = GUARD; data.flags = 0; data.id = db->next_id; data.length = length; ERRORP_IF(sizeof(data_t) != writen(db->data_fd, &data, sizeof(data_t)), "failed to write"); db->data_file_len += sizeof(data_t); db->action = ADD; return data.id; L_error: db->error = -1; return -1; }
void apdb_record_unlock(apdb_t* db, apdb_record_t record) { index_t* index; assert(NULL != db && record >= 0 && NONE == db->action && CAN_WRITE(db)); assert(db->index_file_len <= db->index_mmap_len); if (db->error) return; index = RECORD_TO_INDEX(db, record); assert(GUARD == index->guard && (WRITING & index->flags)); index->flags &= ~WRITING; }
/** * make sure range [0, min(to, st_size)) of data file is mapped. * * @note Writer process always knows the current real size of data * file, but reader processes don't, so we need update @c data_file_len * when map window can't fully cover [0, to), the caller should * check @c data_file_len against @c to after calling this function to * make sure not access data out of file ending. */ static void remap_data_file(apdb_t* db, size_t to) { struct stat st; assert(NULL != db && 0 == db->error); /* * don't have to be to <= db->data_file_len, because * we don't use @c data_file_len to calculate count of records. * * @note db->data_file_len is always less or equal to st_size. * * @note NEVER return if @c to <= db->data_file_len for writer * process because writer process increases data_file_len when * adding record but doesn't update map window before finishing. */ if (to <= db->data_mmap_len) { return; } else if (! CAN_WRITE(db)) { ERRORP_IF(-1 == fstat(db->data_fd, &st), "failed to fstat"); db->data_file_len = st.st_size; } if (db->data_mmap_len < db->data_file_len) { ERRORP_IF(MAP_FAILED != db->data_mmap && -1 == munmap(db->data_mmap, db->data_mmap_len), "failed to munmap"); db->data_mmap_len = db->data_file_len + DATA_EXPAND_SIZE; /* always mmap to read */ db->data_mmap = mmap(NULL, db->data_mmap_len, PROT_READ, MAP_SHARED, db->data_fd, 0); ERRORP_IF(MAP_FAILED == db->data_mmap, "failed to mmap"); } return; L_error: db->error = -1; }
int apdb_record_set_flags(apdb_t* db, apdb_record_t record, unsigned flags) { index_t* index; assert(NULL != db && record >= 0 && NONE == db->action && CAN_WRITE(db)); assert(db->index_file_len <= db->index_mmap_len); if (db->error) return -1; index = RECORD_TO_INDEX(db, record); assert(GUARD == index->guard); index->flags = flags & ~(WRITING | DELETED); return 0; }
/******************************************************************* Wrapper around dos_utime that possibly allows DOS semantics rather than POSIX. *******************************************************************/ int file_utime(connection_struct *conn, char *fname, struct utimbuf *times) { extern struct current_user current_user; SMB_STRUCT_STAT sb; int ret = -1; errno = 0; if(dos_utime(fname, times) == 0) return 0; if((errno != EPERM) && (errno != EACCES)) return -1; if(!lp_dos_filetimes(SNUM(conn))) return -1; /* We have permission (given by the Samba admin) to break POSIX semantics and allow a user to change the time on a file they don't own but can write to (as DOS does). */ if(dos_stat(fname,&sb) != 0) return -1; /* Check if we have write access. */ if (CAN_WRITE(conn)) { if (((sb.st_mode & S_IWOTH) || conn->admin_user || ((sb.st_mode & S_IWUSR) && current_user.uid==sb.st_uid) || ((sb.st_mode & S_IWGRP) && in_group(sb.st_gid,current_user.gid, current_user.ngroups,current_user.groups)))) { /* We are allowed to become root and change the filetime. */ become_root(False); ret = dos_utime(fname, times); unbecome_root(False); } } return ret; }
int apdb_update_begin(apdb_t* db, apdb_record_t record, size_t length) { index_t* index; data_t data; assert(NULL != db && record >= 0 && NONE == db->action && CAN_WRITE(db)); assert(db->index_file_len <= db->index_mmap_len); if (db->error) return -1; if (SSIZE_MAX - ALIGN_UP(sizeof(data_t) + length, ALIGN_SIZE) <= db->data_file_len) { DEBUG("data file too big"); return -1; } index = RECORD_TO_INDEX(db, record); assert(GUARD == index->guard); db->next_record = record; db->next_length = length; db->next_offset = db->data_file_len; db->next_written = 0; data.guard = GUARD; data.flags = index->flags; data.id = index->id; data.length = length; ERRORP_IF(sizeof(data_t) != writen(db->data_fd, &data, sizeof(data_t)), "failed to write"); db->data_file_len += sizeof(data_t); db->action = UPDATE; return 0; L_error: db->error = -1; return -1; }
/** * get next available id, 0 <= id < UINT_MAX * * @param db pointer to a database instance * * @return UINT_MAX on error, next id on success * * @note This function can only be called by writer process * because only it knows the real last record. */ static unsigned get_next_id(apdb_t* db) { unsigned count; index_t* index; assert(NULL != db && CAN_WRITE(db)); assert(db->index_file_len <= db->index_mmap_len); if (0 == db->index_file_len) return 0; count = db->index_file_len / db->index_len; index = RECORD_TO_INDEX(db, (count - 1) * db->index_len); ERROR_IF(GUARD != index->guard || UINT_MAX == index->id, "corrupted index"); return index->id + 1; L_error: db->error = -1; return UINT_MAX; }
int apdb_update_end(apdb_t* db, void* content) { index_t* index; assert(NULL != db && UPDATE == db->action && CAN_WRITE(db)); assert(db->index_file_len <= db->index_mmap_len); db->action = NONE; if (db->error) return -1; ERROR_IF(db->next_length != db->next_written, "write less data than expected"); pad_data_file(db); if (db->error) return -1; index = RECORD_TO_INDEX(db, db->next_record); assert(GUARD == index->guard); index->flags |= WRITING; index->length = sizeof(data_t); /* make reader processes read nothing */ index->offset = db->next_offset; if (NULL != content) memcpy(index->content, content, db->index_len - sizeof(index_t)); index->length += db->next_length; index->flags &= ~WRITING; return 0; L_error: db->error = -1; return -1; }
NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn, struct smb_request *smb1req, struct smbXsrv_open *op, const DATA_BLOB old_cookie, TALLOC_CTX *mem_ctx, files_struct **result, DATA_BLOB *new_cookie) { struct share_mode_lock *lck; struct share_mode_entry *e; struct files_struct *fsp = NULL; NTSTATUS status; bool ok; int ret; int flags = 0; struct file_id file_id; struct smb_filename *smb_fname = NULL; enum ndr_err_code ndr_err; struct vfs_default_durable_cookie cookie; DATA_BLOB new_cookie_blob = data_blob_null; *result = NULL; *new_cookie = data_blob_null; if (!lp_durable_handles(SNUM(conn))) { return NT_STATUS_NOT_SUPPORTED; } /* * the checks for kernel oplocks * and similar things are done * in the vfs_default_durable_cookie() * call below. */ ZERO_STRUCT(cookie); ndr_err = ndr_pull_struct_blob(&old_cookie, talloc_tos(), &cookie, (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { status = ndr_map_error2ntstatus(ndr_err); return status; } if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) { return NT_STATUS_INVALID_PARAMETER; } if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) { return NT_STATUS_INVALID_PARAMETER; } if (!cookie.allow_reconnect) { return NT_STATUS_OBJECT_NAME_NOT_FOUND; } if (strcmp(cookie.servicepath, conn->connectpath) != 0) { return NT_STATUS_OBJECT_NAME_NOT_FOUND; } /* Create an smb_filename with stream_name == NULL. */ smb_fname = synthetic_smb_fname(talloc_tos(), cookie.base_name, NULL, NULL); if (smb_fname == NULL) { return NT_STATUS_NO_MEMORY; } ret = SMB_VFS_LSTAT(conn, smb_fname); if (ret == -1) { status = map_nt_error_from_unix_common(errno); DEBUG(1, ("Unable to lstat stream: %s => %s\n", smb_fname_str_dbg(smb_fname), nt_errstr(status))); return status; } if (!S_ISREG(smb_fname->st.st_ex_mode)) { return NT_STATUS_OBJECT_NAME_NOT_FOUND; } file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st); if (!file_id_equal(&cookie.id, &file_id)) { return NT_STATUS_OBJECT_NAME_NOT_FOUND; } /* * 1. check entry in locking.tdb */ lck = get_existing_share_mode_lock(mem_ctx, file_id); if (lck == NULL) { DEBUG(5, ("vfs_default_durable_reconnect: share-mode lock " "not obtained from db\n")); return NT_STATUS_OBJECT_NAME_NOT_FOUND; } if (lck->data->num_share_modes == 0) { DEBUG(1, ("vfs_default_durable_reconnect: Error: no share-mode " "entry in existing share mode lock\n")); TALLOC_FREE(lck); return NT_STATUS_INTERNAL_DB_ERROR; } if (lck->data->num_share_modes > 1) { /* * It can't be durable if there is more than one handle * on the file. */ DEBUG(5, ("vfs_default_durable_reconnect: more than one " "share-mode entry - can not be durable\n")); TALLOC_FREE(lck); return NT_STATUS_OBJECT_NAME_NOT_FOUND; } e = &lck->data->share_modes[0]; if (!server_id_is_disconnected(&e->pid)) { DEBUG(5, ("vfs_default_durable_reconnect: denying durable " "reconnect for handle that was not marked " "disconnected (e.g. smbd or cluster node died)\n")); TALLOC_FREE(lck); return NT_STATUS_OBJECT_NAME_NOT_FOUND; } if (e->share_file_id != op->global->open_persistent_id) { DEBUG(5, ("vfs_default_durable_reconnect: denying durable " "share_file_id changed %llu != %llu" "(e.g. another client had opened the file)\n", (unsigned long long)e->share_file_id, (unsigned long long)op->global->open_persistent_id)); TALLOC_FREE(lck); return NT_STATUS_OBJECT_NAME_NOT_FOUND; } if ((e->access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) && !CAN_WRITE(conn)) { DEBUG(5, ("vfs_default_durable_reconnect: denying durable " "share[%s] is not writeable anymore\n", lp_servicename(talloc_tos(), SNUM(conn)))); TALLOC_FREE(lck); return NT_STATUS_OBJECT_NAME_NOT_FOUND; } /* * 2. proceed with opening file */ status = fsp_new(conn, conn, &fsp); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("vfs_default_durable_reconnect: failed to create " "new fsp: %s\n", nt_errstr(status))); TALLOC_FREE(lck); return status; } fsp->fh->private_options = e->private_options; fsp->fh->gen_id = smbXsrv_open_hash(op); fsp->file_id = file_id; fsp->file_pid = smb1req->smbpid; fsp->vuid = smb1req->vuid; fsp->open_time = e->time; fsp->access_mask = e->access_mask; fsp->share_access = e->share_access; fsp->can_read = ((fsp->access_mask & (FILE_READ_DATA)) != 0); fsp->can_write = ((fsp->access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) != 0); fsp->fnum = op->local_id; /* * TODO: * Do we need to store the modified flag in the DB? */ fsp->modified = false; /* * no durables for directories */ fsp->is_directory = false; /* * For normal files, can_lock == !is_directory */ fsp->can_lock = true; /* * We do not support aio write behind for smb2 */ fsp->aio_write_behind = false; fsp->oplock_type = e->op_type; fsp->initial_allocation_size = cookie.initial_allocation_size; fsp->fh->position_information = cookie.position_information; fsp->update_write_time_triggered = cookie.update_write_time_triggered; fsp->update_write_time_on_close = cookie.update_write_time_on_close; fsp->write_time_forced = cookie.write_time_forced; fsp->close_write_time = cookie.close_write_time; status = fsp_set_smb_fname(fsp, smb_fname); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(lck); fsp_free(fsp); DEBUG(0, ("vfs_default_durable_reconnect: " "fsp_set_smb_fname failed: %s\n", nt_errstr(status))); return status; } op->compat = fsp; fsp->op = op; e->pid = messaging_server_id(conn->sconn->msg_ctx); e->op_mid = smb1req->mid; e->share_file_id = fsp->fh->gen_id; ok = brl_reconnect_disconnected(fsp); if (!ok) { status = NT_STATUS_INTERNAL_ERROR; DEBUG(1, ("vfs_default_durable_reconnect: " "failed to reopen brlocks: %s\n", nt_errstr(status))); TALLOC_FREE(lck); op->compat = NULL; fsp_free(fsp); return status; } /* * TODO: properly calculate open flags */ if (fsp->can_write && fsp->can_read) { flags = O_RDWR; } else if (fsp->can_write) { flags = O_WRONLY; } else if (fsp->can_read) { flags = O_RDONLY; } status = fd_open(conn, fsp, flags, 0 /* mode */); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(lck); DEBUG(1, ("vfs_default_durable_reconnect: failed to open " "file: %s\n", nt_errstr(status))); op->compat = NULL; fsp_free(fsp); return status; } /* * We now check the stat info stored in the cookie against * the current stat data from the file we just opened. * If any detail differs, we deny the durable reconnect, * because in that case it is very likely that someone * opened the file while the handle was disconnected, * which has to be interpreted as an oplock break. */ ret = SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st); if (ret == -1) { status = map_nt_error_from_unix_common(errno); DEBUG(1, ("Unable to fstat stream: %s => %s\n", smb_fname_str_dbg(smb_fname), nt_errstr(status))); ret = SMB_VFS_CLOSE(fsp); if (ret == -1) { DEBUG(0, ("vfs_default_durable_reconnect: " "SMB_VFS_CLOSE failed (%s) - leaking file " "descriptor\n", strerror(errno))); } TALLOC_FREE(lck); op->compat = NULL; fsp_free(fsp); return status; } if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) { ret = SMB_VFS_CLOSE(fsp); if (ret == -1) { DEBUG(0, ("vfs_default_durable_reconnect: " "SMB_VFS_CLOSE failed (%s) - leaking file " "descriptor\n", strerror(errno))); } TALLOC_FREE(lck); op->compat = NULL; fsp_free(fsp); return NT_STATUS_OBJECT_NAME_NOT_FOUND; } file_id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st); if (!file_id_equal(&cookie.id, &file_id)) { ret = SMB_VFS_CLOSE(fsp); if (ret == -1) { DEBUG(0, ("vfs_default_durable_reconnect: " "SMB_VFS_CLOSE failed (%s) - leaking file " "descriptor\n", strerror(errno))); } TALLOC_FREE(lck); op->compat = NULL; fsp_free(fsp); return NT_STATUS_OBJECT_NAME_NOT_FOUND; } ok = vfs_default_durable_reconnect_check_stat(&cookie.stat_info, &fsp->fsp_name->st, fsp_str_dbg(fsp)); if (!ok) { ret = SMB_VFS_CLOSE(fsp); if (ret == -1) { DEBUG(0, ("vfs_default_durable_reconnect: " "SMB_VFS_CLOSE failed (%s) - leaking file " "descriptor\n", strerror(errno))); } TALLOC_FREE(lck); op->compat = NULL; fsp_free(fsp); return NT_STATUS_OBJECT_NAME_NOT_FOUND; } status = set_file_oplock(fsp); if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("vfs_default_durable_reconnect failed to set oplock " "after opening file: %s\n", nt_errstr(status))); ret = SMB_VFS_CLOSE(fsp); if (ret == -1) { DEBUG(0, ("vfs_default_durable_reconnect: " "SMB_VFS_CLOSE failed (%s) - leaking file " "descriptor\n", strerror(errno))); } TALLOC_FREE(lck); op->compat = NULL; fsp_free(fsp); return status; } status = vfs_default_durable_cookie(fsp, mem_ctx, &new_cookie_blob); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(lck); DEBUG(1, ("vfs_default_durable_reconnect: " "vfs_default_durable_cookie - %s\n", nt_errstr(status))); op->compat = NULL; fsp_free(fsp); return status; } smb1req->chain_fsp = fsp; smb1req->smb2req->compat_chain_fsp = fsp; DEBUG(10, ("vfs_default_durable_reconnect: opened file '%s'\n", fsp_str_dbg(fsp))); /* * release the sharemode lock: this writes the changes */ lck->data->modified = true; TALLOC_FREE(lck); *result = fsp; *new_cookie = new_cookie_blob; return NT_STATUS_OK; }
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 ); }
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; }
static bool set_ea_dos_attribute(connection_struct *conn, struct smb_filename *smb_fname, uint32_t 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)) { DBG_INFO("Cannot set " "attribute EA on file %s: Error = %s\n", smb_fname_str_dbg(smb_fname), strerror(errno)); 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; }
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); }
int file_set_dosmode(connection_struct *conn, const char *fname, uint32 dosmode, SMB_STRUCT_STAT *st, const char *parent_dir) { SMB_STRUCT_STAT st1; int mask=0; mode_t tmp; mode_t unixmode; int ret = -1; /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */ dosmode &= SAMBA_ATTRIBUTES_MASK; DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname)); if (st == NULL) { SET_STAT_INVALID(st1); st = &st1; } if (!VALID_STAT(*st)) { if (SMB_VFS_STAT(conn,fname,st)) return(-1); } unixmode = st->st_mode; get_acl_group_bits(conn, fname, &st->st_mode); if (S_ISDIR(st->st_mode)) dosmode |= aDIR; else dosmode &= ~aDIR; if (dos_mode(conn,fname,st) == dosmode) { st->st_mode = unixmode; return(0); } /* Store the DOS attributes in an EA by preference. */ if (set_ea_dos_attribute(conn, fname, st, dosmode)) { st->st_mode = unixmode; return 0; } unixmode = unix_mode(conn,dosmode,fname, parent_dir); /* 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 |= (st->st_mode & mask); /* if we previously had any r bits set then leave them alone */ if ((tmp = st->st_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 |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)); } if ((ret = SMB_VFS_CHMOD(conn,fname,unixmode)) == 0) { notify_fname(conn, NOTIFY_ACTION_MODIFIED, FILE_NOTIFY_CHANGE_ATTRIBUTES, fname); st->st_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. */ /* Check if we have write access. */ if (CAN_WRITE(conn)) { /* * We need to open the file with write access whilst * still in our current user context. This ensures we * are not violating security in doing the fchmod. * This file open does *not* break any oplocks we are * holding. We need to review this.... may need to * break batch oplocks open by others. JRA. */ files_struct *fsp; if (!NT_STATUS_IS_OK(open_file_fchmod(conn,fname,st,&fsp))) return -1; become_root(); ret = SMB_VFS_FCHMOD(fsp, fsp->fh->fd, unixmode); unbecome_root(); close_file_fchmod(fsp); notify_fname(conn, NOTIFY_ACTION_MODIFIED, FILE_NOTIFY_CHANGE_ATTRIBUTES, fname); if (ret == 0) { st->st_mode = unixmode; } } return( ret ); }
static BOOL set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode) { fstring attrstr; files_struct *fsp = NULL; BOOL ret = False; if (!lp_store_dos_attributes(SNUM(conn))) { return False; } snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK); if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) { if((errno != EPERM) && (errno != EACCES)) { if (errno == ENOSYS #if defined(ENOTSUP) || errno == ENOTSUP) { #else ) { #endif 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; /* * We need to open the file with write access whilst * still in our current user context. This ensures we * are not violating security in doing the setxattr. */ if (!NT_STATUS_IS_OK(open_file_fchmod(conn,path,sbuf,&fsp))) return ret; become_root(); if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) { ret = True; } unbecome_root(); close_file_fchmod(fsp); return ret; } DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path)); return True; } /**************************************************************************** Change a unix mode to a dos mode for an ms dfs link. ****************************************************************************/ uint32 dos_mode_msdfs(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf) { uint32 result = 0; DEBUG(8,("dos_mode_msdfs: %s\n", path)); if (!VALID_STAT(*sbuf)) { 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(path,'/'); if (p) { p++; } else { p = path; } if (p[0] == '.' && p[1] != '.' && p[1] != 0) { result |= aHIDDEN; } } result |= dos_mode_from_sbuf(conn, path, sbuf); /* Optimization : Only call is_hidden_path if it's not already hidden. */ if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) { result |= aHIDDEN; } DEBUG(8,("dos_mode_msdfs returning ")); if (result & aHIDDEN) DEBUG(8, ("h")); if (result & aRONLY ) DEBUG(8, ("r")); if (result & aSYSTEM) DEBUG(8, ("s")); if (result & aDIR ) DEBUG(8, ("d")); if (result & aARCH ) 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; }
bool can_delete_file_in_directory(connection_struct *conn, const char *fname) { SMB_STRUCT_STAT sbuf; TALLOC_CTX *ctx = talloc_tos(); char *dname = NULL; if (!CAN_WRITE(conn)) { return False; } /* Get the parent directory permission mask and owners. */ if (!parent_dirname(ctx, fname, &dname, NULL)) { return False; } if(SMB_VFS_STAT(conn, dname, &sbuf) != 0) { return False; } /* fast paths first */ if (!S_ISDIR(sbuf.st_mode)) { return False; } if (conn->server_info->utok.uid == 0 || conn->admin_user) { /* I'm sorry sir, I didn't know you were root... */ return True; } #ifdef S_ISVTX /* sticky bit means delete only by owner or root. */ if (sbuf.st_mode & S_ISVTX) { SMB_STRUCT_STAT sbuf_file; if(SMB_VFS_STAT(conn, fname, &sbuf_file) != 0) { if (errno == ENOENT) { /* If the file doesn't already exist then * yes we'll be able to delete it. */ return True; } return False; } /* * Patch from SATOH Fumiyasu <*****@*****.**> * for bug #3348. Don't assume owning sticky bit * directory means write access allowed. */ if (conn->server_info->utok.uid != sbuf_file.st_uid) { return False; } } #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. */ return can_access_file_acl(conn, dname, FILE_DELETE_CHILD); }
static BOOL open_file(files_struct *fsp,connection_struct *conn, const char *fname1,SMB_STRUCT_STAT *psbuf,int flags,mode_t mode, uint32 desired_access) { extern struct current_user current_user; pstring fname; int accmode = (flags & O_ACCMODE); int local_flags = flags; fsp->fd = -1; fsp->oplock_type = NO_OPLOCK; errno = EPERM; pstrcpy(fname,fname1); /* Check permissions */ /* * This code was changed after seeing a client open request * containing the open mode of (DENY_WRITE/read-only) with * the 'create if not exist' bit set. The previous code * would fail to open the file read only on a read-only share * as it was checking the flags parameter directly against O_RDONLY, * this was failing as the flags parameter was set to O_RDONLY|O_CREAT. * JRA. */ if (!CAN_WRITE(conn)) { /* It's a read-only share - fail if we wanted to write. */ if(accmode != O_RDONLY) { DEBUG(3,("Permission denied opening %s\n",fname)); check_for_pipe(fname); return False; } else if(flags & O_CREAT) { /* We don't want to write - but we must make sure that O_CREAT doesn't create the file if we have write access into the directory. */ flags &= ~O_CREAT; } } /* * This little piece of insanity is inspired by the * fact that an NT client can open a file for O_RDONLY, * but set the create disposition to FILE_EXISTS_TRUNCATE. * If the client *can* write to the file, then it expects to * truncate the file, even though it is opening for readonly. * Quicken uses this stupid trick in backup file creation... * Thanks *greatly* to "David W. Chapman Jr." <*****@*****.**> * for helping track this one down. It didn't bite us in 2.0.x * as we always opened files read-write in that release. JRA. */ if ((accmode == O_RDONLY) && ((flags & O_TRUNC) == O_TRUNC)) { DEBUG(10,("open_file: truncate requested on read-only open for file %s\n",fname )); local_flags = (flags & ~O_ACCMODE)|O_RDWR; } /* actually do the open */ if ((desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) || (local_flags & O_CREAT) || ((local_flags & O_TRUNC) == O_TRUNC) ) { /* * We can't actually truncate here as the file may be locked. * open_file_shared will take care of the truncate later. JRA. */ local_flags &= ~O_TRUNC; #if defined(O_NONBLOCK) && defined(S_ISFIFO) /* * We would block on opening a FIFO with no one else on the * other end. Do what we used to do and add O_NONBLOCK to the * open flags. JRA. */ if (VALID_STAT(*psbuf) && S_ISFIFO(psbuf->st_mode)) local_flags |= O_NONBLOCK; #endif fsp->fd = fd_open(conn, fname, local_flags, mode); if (fsp->fd == -1) { DEBUG(3,("Error opening file %s (%s) (local_flags=%d) (flags=%d)\n", fname,strerror(errno),local_flags,flags)); check_for_pipe(fname); return False; } } else fsp->fd = -1; /* What we used to call a stat open. */ if (!VALID_STAT(*psbuf)) { int ret; if (fsp->fd == -1) ret = vfs_stat(conn, fname, psbuf); else { ret = vfs_fstat(fsp,fsp->fd,psbuf); /* If we have an fd, this stat should succeed. */ if (ret == -1) DEBUG(0,("Error doing fstat on open file %s (%s)\n", fname,strerror(errno) )); } /* For a non-io open, this stat failing means file not found. JRA */ if (ret == -1) { fd_close(conn, fsp); return False; } } /* * POSIX allows read-only opens of directories. We don't * want to do this (we use a different code path for this) * so catch a directory open and return an EISDIR. JRA. */ if(S_ISDIR(psbuf->st_mode)) { fd_close(conn, fsp); errno = EISDIR; return False; } fsp->mode = psbuf->st_mode; fsp->inode = psbuf->st_ino; fsp->dev = psbuf->st_dev; fsp->vuid = current_user.vuid; fsp->size = psbuf->st_size; fsp->pos = -1; fsp->can_lock = True; fsp->can_read = ((flags & O_WRONLY)==0); fsp->can_write = ((flags & (O_WRONLY|O_RDWR))!=0); fsp->share_mode = 0; fsp->desired_access = desired_access; fsp->print_file = False; fsp->modified = False; fsp->oplock_type = NO_OPLOCK; fsp->sent_oplock_break = NO_BREAK_SENT; fsp->is_directory = False; fsp->directory_delete_on_close = False; fsp->conn = conn; /* * Note that the file name here is the *untranslated* name * ie. it is still in the DOS codepage sent from the client. * All use of this filename will pass though the sys_xxxx * functions which will do the dos_to_unix translation before * mapping into a UNIX filename. JRA. */ string_set(&fsp->fsp_name,fname); fsp->wbmpx_ptr = NULL; fsp->wcp = NULL; /* Write cache pointer. */ DEBUG(2,("%s opened file %s read=%s write=%s (numopen=%d)\n", *current_user_info.smb_name ? current_user_info.smb_name : conn->user,fsp->fsp_name, BOOLSTR(fsp->can_read), BOOLSTR(fsp->can_write), conn->num_files_open + 1)); return True; }