/* unlink a file */ static NTSTATUS pvfs_unlink_file(struct pvfs_state *pvfs, struct pvfs_filename *name) { NTSTATUS status = NT_STATUS_OK; if (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) { return NT_STATUS_FILE_IS_A_DIRECTORY; } if (name->st.st_nlink == 1) { status = pvfs_xattr_unlink_hook(pvfs, name->full_name); if (!NT_STATUS_IS_OK(status)) { return status; } } /* finally try the actual unlink */ if (pvfs_sys_unlink(pvfs, name->full_name) == -1) { status = pvfs_map_errno(pvfs, errno); } if (NT_STATUS_IS_OK(status)) { notify_trigger(pvfs->notify_context, NOTIFY_ACTION_REMOVED, FILE_NOTIFY_CHANGE_FILE_NAME, name->full_name); } return status; }
/* start to read a directory if the pattern matches no files then we return NT_STATUS_OK, with dir->count = 0 */ NTSTATUS pvfs_list_start(struct pvfs_state *pvfs, struct pvfs_filename *name, TALLOC_CTX *mem_ctx, struct pvfs_dir **dirp) { char *pattern; struct pvfs_dir *dir; (*dirp) = talloc_zero(mem_ctx, struct pvfs_dir); if (*dirp == NULL) { return NT_STATUS_NO_MEMORY; } dir = *dirp; /* split the unix path into a directory + pattern */ pattern = strrchr(name->full_name, '/'); if (!pattern) { /* this should not happen, as pvfs_unix_path is supposed to return an absolute path */ return NT_STATUS_UNSUCCESSFUL; } *pattern++ = 0; if (!name->has_wildcard) { return pvfs_list_no_wildcard(pvfs, name, pattern, dir); } dir->unix_path = talloc_strdup(dir, name->full_name); if (!dir->unix_path) { return NT_STATUS_NO_MEMORY; } dir->pattern = talloc_strdup(dir, pattern); if (dir->pattern == NULL) { return NT_STATUS_NO_MEMORY; } dir->dir = opendir(name->full_name); if (!dir->dir) { return pvfs_map_errno(pvfs, errno); } dir->pvfs = pvfs; dir->no_wildcard = False; dir->end_of_search = False; dir->offset = DIR_OFFSET_DOT; dir->name_cache = talloc_zero_array(dir, struct name_cache_entry, NAME_CACHE_SIZE); if (dir->name_cache == NULL) { talloc_free(dir); return NT_STATUS_NO_MEMORY; } talloc_set_destructor(dir, pvfs_dirlist_destructor); return NT_STATUS_OK; }
/* create a directory */ NTSTATUS pvfs_mkdir(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req, union smb_mkdir *md) { struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data, struct pvfs_state); NTSTATUS status; struct pvfs_filename *name; mode_t mode; if (md->generic.level == RAW_MKDIR_T2MKDIR) { return pvfs_t2mkdir(pvfs, req, md); } if (md->generic.level != RAW_MKDIR_MKDIR) { return NT_STATUS_INVALID_LEVEL; } /* resolve the cifs name to a posix name */ status = pvfs_resolve_name(pvfs, req, md->mkdir.in.path, 0, &name); if (!NT_STATUS_IS_OK(status)) { return status; } if (name->exists) { return NT_STATUS_OBJECT_NAME_COLLISION; } status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_ADD_FILE); if (!NT_STATUS_IS_OK(status)) { return status; } mode = pvfs_fileperms(pvfs, FILE_ATTRIBUTE_DIRECTORY); if (pvfs_sys_mkdir(pvfs, name->full_name, mode, name->allow_override) == -1) { return pvfs_map_errno(pvfs, errno); } pvfs_xattr_unlink_hook(pvfs, name->full_name); /* setup an inherited acl from the parent */ status = pvfs_acl_inherit(pvfs, req, name, -1); if (!NT_STATUS_IS_OK(status)) { pvfs_sys_rmdir(pvfs, name->full_name, name->allow_override); return status; } notify_trigger(pvfs->notify_context, NOTIFY_ACTION_ADDED, FILE_NOTIFY_CHANGE_DIR_NAME, name->full_name); return NT_STATUS_OK; }
/* delete a xattr */ NTSTATUS delete_xattr_system(struct pvfs_state *pvfs, const char *attr_name, const char *fname, int fd) { int ret; if (fd != -1) { ret = wrap_fremovexattr(fd, attr_name); } else { ret = wrap_removexattr(fname, attr_name); } if (ret == -1) { return pvfs_map_errno(pvfs, errno); } return NT_STATUS_OK; }
/* remove a directory */ NTSTATUS pvfs_rmdir(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req, struct smb_rmdir *rd) { struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data, struct pvfs_state); NTSTATUS status; struct pvfs_filename *name; /* resolve the cifs name to a posix name */ status = pvfs_resolve_name(pvfs, req, rd->in.path, 0, &name); if (!NT_STATUS_IS_OK(status)) { return status; } if (!name->exists) { return NT_STATUS_OBJECT_NAME_NOT_FOUND; } status = pvfs_access_check_simple(pvfs, req, name, SEC_STD_DELETE); if (!NT_STATUS_IS_OK(status)) { return status; } status = pvfs_xattr_unlink_hook(pvfs, name->full_name); if (!NT_STATUS_IS_OK(status)) { return status; } if (pvfs_sys_rmdir(pvfs, name->full_name, name->allow_override) == -1) { /* some olders systems don't return ENOTEMPTY to rmdir() */ if (errno == EEXIST) { return NT_STATUS_DIRECTORY_NOT_EMPTY; } return pvfs_map_errno(pvfs, errno); } notify_trigger(pvfs->notify_context, NOTIFY_ACTION_REMOVED, FILE_NOTIFY_CHANGE_DIR_NAME, name->full_name); return NT_STATUS_OK; }
/* push a xattr as a blob, from either a file or a file descriptor */ NTSTATUS push_xattr_blob_system(struct pvfs_state *pvfs, const char *attr_name, const char *fname, int fd, const DATA_BLOB *blob) { int ret; if (fd != -1) { ret = wrap_fsetxattr(fd, attr_name, blob->data, blob->length, 0); } else { ret = wrap_setxattr(fname, attr_name, blob->data, blob->length, 0); } if (ret == -1) { return pvfs_map_errno(pvfs, errno); } return NT_STATUS_OK; }
/* create a directory with EAs */ static NTSTATUS pvfs_t2mkdir(struct pvfs_state *pvfs, struct ntvfs_request *req, union smb_mkdir *md) { NTSTATUS status; struct pvfs_filename *name; mode_t mode; /* resolve the cifs name to a posix name */ status = pvfs_resolve_name(pvfs, req, md->t2mkdir.in.path, 0, &name); if (!NT_STATUS_IS_OK(status)) { return status; } if (name->exists) { return NT_STATUS_OBJECT_NAME_COLLISION; } status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_ADD_FILE); if (!NT_STATUS_IS_OK(status)) { return status; } mode = pvfs_fileperms(pvfs, FILE_ATTRIBUTE_DIRECTORY); if (pvfs_sys_mkdir(pvfs, name->full_name, mode, name->allow_override) == -1) { return pvfs_map_errno(pvfs, errno); } pvfs_xattr_unlink_hook(pvfs, name->full_name); status = pvfs_resolve_name(pvfs, req, md->t2mkdir.in.path, 0, &name); if (!NT_STATUS_IS_OK(status)) { return status; } if (!name->exists || !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) { return NT_STATUS_INTERNAL_ERROR; } /* setup an inherited acl from the parent */ status = pvfs_acl_inherit(pvfs, req, name, -1); if (!NT_STATUS_IS_OK(status)) { pvfs_sys_rmdir(pvfs, name->full_name, name->allow_override); return status; } /* setup any EAs that were asked for */ status = pvfs_setfileinfo_ea_set(pvfs, name, -1, md->t2mkdir.in.num_eas, md->t2mkdir.in.eas); if (!NT_STATUS_IS_OK(status)) { pvfs_sys_rmdir(pvfs, name->full_name, name->allow_override); return status; } notify_trigger(pvfs->notify_context, NOTIFY_ACTION_ADDED, FILE_NOTIFY_CHANGE_DIR_NAME, name->full_name); return NT_STATUS_OK; }
/* read from a file */ NTSTATUS pvfs_read(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req, union smb_read *rd) { struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data, struct pvfs_state); ssize_t ret; struct pvfs_file *f; NTSTATUS status; uint32_t maxcnt; uint32_t mask; if (rd->generic.level != RAW_READ_READX) { return ntvfs_map_read(ntvfs, req, rd); } f = pvfs_find_fd(pvfs, req, rd->readx.in.file.ntvfs); if (!f) { return NT_STATUS_INVALID_HANDLE; } if (f->handle->fd == -1) { return NT_STATUS_INVALID_DEVICE_REQUEST; } mask = SEC_FILE_READ_DATA; if (rd->readx.in.read_for_execute) { mask |= SEC_FILE_EXECUTE; } if (!(f->access_mask & mask)) { return NT_STATUS_ACCESS_DENIED; } maxcnt = rd->readx.in.maxcnt; if (maxcnt > 2*UINT16_MAX && req->ctx->protocol < PROTOCOL_SMB2) { DEBUG(3,(__location__ ": Invalid SMB maxcnt 0x%x\n", maxcnt)); return NT_STATUS_INVALID_PARAMETER; } status = pvfs_check_lock(pvfs, f, req->smbpid, rd->readx.in.offset, maxcnt, READ_LOCK); if (!NT_STATUS_IS_OK(status)) { return status; } if (f->handle->name->stream_name) { ret = pvfs_stream_read(pvfs, f->handle, rd->readx.out.data, maxcnt, rd->readx.in.offset); } else { #if HAVE_LINUX_AIO /* possibly try an aio read */ if ((req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC) && (pvfs->flags & PVFS_FLAG_LINUX_AIO)) { status = pvfs_aio_pread(req, rd, f, maxcnt); if (NT_STATUS_IS_OK(status)) { return NT_STATUS_OK; } } #endif ret = pread(f->handle->fd, rd->readx.out.data, maxcnt, rd->readx.in.offset); } if (ret == -1) { return pvfs_map_errno(pvfs, errno); } /* only SMB2 honors mincnt */ if (req->ctx->protocol == PROTOCOL_SMB2) { if (rd->readx.in.mincnt > ret || (ret == 0 && maxcnt > 0)) { return NT_STATUS_END_OF_FILE; } } f->handle->position = f->handle->seek_offset = rd->readx.in.offset + ret; rd->readx.out.nread = ret; rd->readx.out.remaining = 0xFFFF; rd->readx.out.compaction_mode = 0; return NT_STATUS_OK; }
/* copy a file. Caller is supposed to have already ensured that the operation is allowed. The destination file must not exist. */ NTSTATUS pvfs_copy_file(struct pvfs_state *pvfs, struct pvfs_filename *name1, struct pvfs_filename *name2) { int fd1, fd2; mode_t mode; NTSTATUS status; size_t buf_size = 0x10000; uint8_t *buf = talloc_array(name2, uint8_t, buf_size); if (buf == NULL) { return NT_STATUS_NO_MEMORY; } fd1 = pvfs_sys_open(pvfs, name1->full_name, O_RDONLY, 0); if (fd1 == -1) { talloc_free(buf); return pvfs_map_errno(pvfs, errno); } fd2 = pvfs_sys_open(pvfs, name2->full_name, O_CREAT|O_EXCL|O_WRONLY, 0); if (fd2 == -1) { close(fd1); talloc_free(buf); return pvfs_map_errno(pvfs, errno); } while (1) { ssize_t ret2, ret = read(fd1, buf, buf_size); if (ret == -1 && (errno == EINTR || errno == EAGAIN)) { continue; } if (ret <= 0) break; ret2 = write(fd2, buf, ret); if (ret2 == -1 && (errno == EINTR || errno == EAGAIN)) { continue; } if (ret2 != ret) { close(fd1); close(fd2); talloc_free(buf); pvfs_sys_unlink(pvfs, name2->full_name); if (ret2 == -1) { return pvfs_map_errno(pvfs, errno); } return NT_STATUS_DISK_FULL; } } talloc_free(buf); close(fd1); mode = pvfs_fileperms(pvfs, name1->dos.attrib); if (pvfs_sys_fchmod(pvfs, fd2, mode) == -1) { status = pvfs_map_errno(pvfs, errno); close(fd2); pvfs_sys_unlink(pvfs, name2->full_name); return status; } name2->st.st_mode = mode; name2->dos = name1->dos; status = pvfs_dosattrib_save(pvfs, name2, fd2); if (!NT_STATUS_IS_OK(status)) { close(fd2); pvfs_sys_unlink(pvfs, name2->full_name); return status; } close(fd2); return NT_STATUS_OK; }
/* create a new file */ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, struct ntvfs_request *req, struct pvfs_filename *name, union smb_open *io) { struct pvfs_file *f; NTSTATUS status; struct ntvfs_handle *h; int flags, fd; struct odb_lock *lck; uint32_t create_options = io->generic.in.create_options; uint32_t share_access = io->generic.in.share_access; uint32_t access_mask = io->generic.in.access_mask; mode_t mode; uint32_t attrib; BOOL del_on_close; struct pvfs_filename *parent; uint32_t oplock_level = OPLOCK_NONE, oplock_granted; if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) && (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) { return NT_STATUS_CANNOT_DELETE; } status = pvfs_access_check_create(pvfs, req, name, &access_mask); NT_STATUS_NOT_OK_RETURN(status); /* check that the parent isn't opened with delete on close set */ status = pvfs_resolve_parent(pvfs, req, name, &parent); if (NT_STATUS_IS_OK(status)) { DATA_BLOB locking_key; status = pvfs_locking_key(parent, req, &locking_key); NT_STATUS_NOT_OK_RETURN(status); status = odb_get_delete_on_close(pvfs->odb_context, &locking_key, &del_on_close, NULL, NULL); NT_STATUS_NOT_OK_RETURN(status); if (del_on_close) { return NT_STATUS_DELETE_PENDING; } } if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) { flags = O_RDWR; } else { flags = O_RDONLY; } status = ntvfs_handle_new(pvfs->ntvfs, req, &h); NT_STATUS_NOT_OK_RETURN(status); f = talloc(h, struct pvfs_file); NT_STATUS_HAVE_NO_MEMORY(f); f->handle = talloc(f, struct pvfs_file_handle); NT_STATUS_HAVE_NO_MEMORY(f->handle); attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE; mode = pvfs_fileperms(pvfs, attrib); /* create the file */ fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode); if (fd == -1) { return pvfs_map_errno(pvfs, errno); } pvfs_xattr_unlink_hook(pvfs, name->full_name); /* if this was a stream create then create the stream as well */ if (name->stream_name) { status = pvfs_stream_create(pvfs, name, fd); if (!NT_STATUS_IS_OK(status)) { close(fd); return status; } } /* re-resolve the open fd */ status = pvfs_resolve_name_fd(pvfs, fd, name); if (!NT_STATUS_IS_OK(status)) { close(fd); return status; } name->dos.attrib = attrib; status = pvfs_dosattrib_save(pvfs, name, fd); if (!NT_STATUS_IS_OK(status)) { goto cleanup_delete; } status = pvfs_open_setup_eas_acl(pvfs, req, name, fd, f, io); if (!NT_STATUS_IS_OK(status)) { goto cleanup_delete; } /* form the lock context used for byte range locking and opendb locking */ status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key); if (!NT_STATUS_IS_OK(status)) { goto cleanup_delete; } status = pvfs_brl_locking_handle(f, name, h, &f->brl_handle); if (!NT_STATUS_IS_OK(status)) { goto cleanup_delete; } /* grab a lock on the open file record */ lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key); if (lck == NULL) { DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n", name->full_name)); /* we were supposed to do a blocking lock, so something is badly wrong! */ status = NT_STATUS_INTERNAL_DB_CORRUPTION; goto cleanup_delete; } if (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) { del_on_close = True; } else { del_on_close = False; } if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) { oplock_level = OPLOCK_NONE; } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK) { oplock_level = OPLOCK_BATCH; } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_OPLOCK) { oplock_level = OPLOCK_EXCLUSIVE; } status = odb_open_file(lck, f->handle, name->stream_id, share_access, access_mask, del_on_close, name->full_name, oplock_level, &oplock_granted); talloc_free(lck); if (!NT_STATUS_IS_OK(status)) { /* bad news, we must have hit a race - we don't delete the file here as the most likely scenario is that someone else created the file at the same time */ close(fd); return status; } if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) { oplock_granted = OPLOCK_BATCH; } f->ntvfs = h; f->pvfs = pvfs; f->pending_list = NULL; f->lock_count = 0; f->share_access = io->generic.in.share_access; f->access_mask = access_mask; f->impersonation = io->generic.in.impersonation; f->notify_buffer = NULL; f->search = NULL; f->handle->pvfs = pvfs; f->handle->name = talloc_steal(f->handle, name); f->handle->fd = fd; f->handle->create_options = io->generic.in.create_options; f->handle->seek_offset = 0; f->handle->position = 0; f->handle->mode = 0; f->handle->have_opendb_entry = True; f->handle->sticky_write_time = False; DLIST_ADD(pvfs->files.list, f); /* setup a destructor to avoid file descriptor leaks on abnormal termination */ talloc_set_destructor(f, pvfs_fnum_destructor); talloc_set_destructor(f->handle, pvfs_handle_destructor); io->generic.out.oplock_level = oplock_granted; io->generic.out.file.ntvfs = f->ntvfs; io->generic.out.create_action = NTCREATEX_ACTION_CREATED; io->generic.out.create_time = name->dos.create_time; io->generic.out.access_time = name->dos.access_time; io->generic.out.write_time = name->dos.write_time; io->generic.out.change_time = name->dos.change_time; io->generic.out.attrib = name->dos.attrib; io->generic.out.alloc_size = name->dos.alloc_size; io->generic.out.size = name->st.st_size; io->generic.out.file_type = FILE_TYPE_DISK; io->generic.out.ipc_state = 0; io->generic.out.is_directory = 0; /* success - keep the file handle */ status = ntvfs_handle_set_backend_data(h, pvfs->ntvfs, f); if (!NT_STATUS_IS_OK(status)) { goto cleanup_delete; } notify_trigger(pvfs->notify_context, NOTIFY_ACTION_ADDED, FILE_NOTIFY_CHANGE_FILE_NAME, name->full_name); return NT_STATUS_OK; cleanup_delete: close(fd); unlink(name->full_name); return status; }
/* open a directory */ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, struct ntvfs_request *req, struct pvfs_filename *name, union smb_open *io) { struct pvfs_file *f; struct ntvfs_handle *h; NTSTATUS status; uint32_t create_action; uint32_t access_mask = io->generic.in.access_mask; struct odb_lock *lck; BOOL del_on_close; uint32_t create_options; uint32_t share_access; create_options = io->generic.in.create_options; share_access = io->generic.in.share_access; if (name->stream_name) { return NT_STATUS_NOT_A_DIRECTORY; } /* if the client says it must be a directory, and it isn't, then fail */ if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) { return NT_STATUS_NOT_A_DIRECTORY; } switch (io->generic.in.open_disposition) { case NTCREATEX_DISP_OPEN_IF: break; case NTCREATEX_DISP_OPEN: if (!name->exists) { return NT_STATUS_OBJECT_NAME_NOT_FOUND; } break; case NTCREATEX_DISP_CREATE: if (name->exists) { return NT_STATUS_OBJECT_NAME_COLLISION; } break; case NTCREATEX_DISP_OVERWRITE_IF: case NTCREATEX_DISP_OVERWRITE: case NTCREATEX_DISP_SUPERSEDE: default: return NT_STATUS_INVALID_PARAMETER; } status = ntvfs_handle_new(pvfs->ntvfs, req, &h); NT_STATUS_NOT_OK_RETURN(status); f = talloc(h, struct pvfs_file); if (f == NULL) { return NT_STATUS_NO_MEMORY; } f->handle = talloc(f, struct pvfs_file_handle); if (f->handle == NULL) { return NT_STATUS_NO_MEMORY; } if (name->exists) { /* check the security descriptor */ status = pvfs_access_check(pvfs, req, name, &access_mask); } else { status = pvfs_access_check_create(pvfs, req, name, &access_mask); } if (!NT_STATUS_IS_OK(status)) { return status; } f->ntvfs = h; f->pvfs = pvfs; f->pending_list = NULL; f->lock_count = 0; f->share_access = io->generic.in.share_access; f->impersonation = io->generic.in.impersonation; f->access_mask = access_mask; f->brl_handle = NULL; f->notify_buffer = NULL; f->search = NULL; f->handle->pvfs = pvfs; f->handle->name = talloc_steal(f->handle, name); f->handle->fd = -1; f->handle->odb_locking_key = data_blob(NULL, 0); f->handle->create_options = io->generic.in.create_options; f->handle->seek_offset = 0; f->handle->position = 0; f->handle->mode = 0; f->handle->sticky_write_time = False; if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) && pvfs_directory_empty(pvfs, f->handle->name)) { del_on_close = True; } else { del_on_close = False; } if (name->exists) { /* form the lock context used for opendb locking */ status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key); if (!NT_STATUS_IS_OK(status)) { return status; } /* get a lock on this file before the actual open */ lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key); if (lck == NULL) { DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n", name->full_name)); /* we were supposed to do a blocking lock, so something is badly wrong! */ return NT_STATUS_INTERNAL_DB_CORRUPTION; } /* see if we are allowed to open at the same time as existing opens */ status = odb_open_file(lck, f->handle, f->handle->name->stream_id, share_access, access_mask, del_on_close, name->full_name, OPLOCK_NONE, NULL); if (!NT_STATUS_IS_OK(status)) { talloc_free(lck); return status; } f->handle->have_opendb_entry = True; } DLIST_ADD(pvfs->files.list, f); /* setup destructors to avoid leaks on abnormal termination */ talloc_set_destructor(f->handle, pvfs_dir_handle_destructor); talloc_set_destructor(f, pvfs_dir_fnum_destructor); if (!name->exists) { uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY; mode_t mode = pvfs_fileperms(pvfs, attrib); if (mkdir(name->full_name, mode) == -1) { return pvfs_map_errno(pvfs,errno); } pvfs_xattr_unlink_hook(pvfs, name->full_name); status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name); if (!NT_STATUS_IS_OK(status)) { goto cleanup_delete; } status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, f, io); if (!NT_STATUS_IS_OK(status)) { goto cleanup_delete; } /* form the lock context used for opendb locking */ status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key); if (!NT_STATUS_IS_OK(status)) { return status; } lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key); if (lck == NULL) { DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n", name->full_name)); /* we were supposed to do a blocking lock, so something is badly wrong! */ return NT_STATUS_INTERNAL_DB_CORRUPTION; } status = odb_open_file(lck, f->handle, f->handle->name->stream_id, share_access, access_mask, del_on_close, name->full_name, OPLOCK_NONE, NULL); if (!NT_STATUS_IS_OK(status)) { goto cleanup_delete; } f->handle->have_opendb_entry = True; create_action = NTCREATEX_ACTION_CREATED; notify_trigger(pvfs->notify_context, NOTIFY_ACTION_ADDED, FILE_NOTIFY_CHANGE_DIR_NAME, name->full_name); } else { create_action = NTCREATEX_ACTION_EXISTED; } if (!name->exists) { return NT_STATUS_OBJECT_NAME_NOT_FOUND; } /* the open succeeded, keep this handle permanently */ status = ntvfs_handle_set_backend_data(h, pvfs->ntvfs, f); if (!NT_STATUS_IS_OK(status)) { goto cleanup_delete; } io->generic.out.oplock_level = OPLOCK_NONE; io->generic.out.file.ntvfs = h; io->generic.out.create_action = create_action; io->generic.out.create_time = name->dos.create_time; io->generic.out.access_time = name->dos.access_time; io->generic.out.write_time = name->dos.write_time; io->generic.out.change_time = name->dos.change_time; io->generic.out.attrib = name->dos.attrib; io->generic.out.alloc_size = name->dos.alloc_size; io->generic.out.size = name->st.st_size; io->generic.out.file_type = FILE_TYPE_DISK; io->generic.out.ipc_state = 0; io->generic.out.is_directory = 1; return NT_STATUS_OK; cleanup_delete: rmdir(name->full_name); return status; }
/* pull a xattr as a blob, from either a file or a file descriptor */ NTSTATUS pull_xattr_blob_system(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, const char *attr_name, const char *fname, int fd, size_t estimated_size, DATA_BLOB *blob) { int ret; *blob = data_blob_talloc(mem_ctx, NULL, estimated_size+16); if (blob->data == NULL) { return NT_STATUS_NO_MEMORY; } again: if (fd != -1) { ret = wrap_fgetxattr(fd, attr_name, blob->data, estimated_size); } else { ret = wrap_getxattr(fname, attr_name, blob->data, estimated_size); } if (ret == -1 && errno == ERANGE) { estimated_size *= 2; blob->data = talloc_realloc(mem_ctx, blob->data, uint8_t, estimated_size); if (blob->data == NULL) { return NT_STATUS_NO_MEMORY; } blob->length = estimated_size; goto again; } if (ret == -1 && errno == EPERM) { struct stat statbuf; if (fd != -1) { ret = fstat(fd, &statbuf); } else { ret = stat(fname, &statbuf); } if (ret == 0) { /* check if this is a directory and the sticky bit is set */ if (S_ISDIR(statbuf.st_mode) && (statbuf.st_mode & S_ISVTX)) { /* pretend we could not find the xattr */ data_blob_free(blob); return NT_STATUS_NOT_FOUND; } else { /* if not this was probably a legitimate error * reset ret and errno to the correct values */ errno = EPERM; ret = -1; } } } if (ret == -1) { data_blob_free(blob); return pvfs_map_errno(pvfs, errno); } blob->length = ret; return NT_STATUS_OK; }