Ejemplo n.º 1
0
/*
  form the lock context used for byte range locking. This is separate
  from the locking key used for opendb locking as it needs to take
  account of file streams (each stream is a separate byte range
  locking space)
*/
static NTSTATUS pvfs_brl_locking_handle(TALLOC_CTX *mem_ctx,
					struct pvfs_filename *name,
					struct ntvfs_handle *ntvfs,
					struct brl_handle **_h)
{
	DATA_BLOB odb_key, key;
	NTSTATUS status;
	struct brl_handle *h;

	status = pvfs_locking_key(name, mem_ctx, &odb_key);
	NT_STATUS_NOT_OK_RETURN(status);

	if (name->stream_name == NULL) {
		key = odb_key;
	} else {
		key = data_blob_talloc(mem_ctx, NULL, 
				       odb_key.length + strlen(name->stream_name) + 1);
		NT_STATUS_HAVE_NO_MEMORY(key.data);
		memcpy(key.data, odb_key.data, odb_key.length);
		memcpy(key.data + odb_key.length, 
		       name->stream_name, strlen(name->stream_name) + 1);
		data_blob_free(&odb_key);
	}

	h = brl_create_handle(mem_ctx, ntvfs, &key);
	NT_STATUS_HAVE_NO_MEMORY(h);

	*_h = h;
	return NT_STATUS_OK;
}
Ejemplo n.º 2
0
/*
  fill in the dos file attributes for a file
*/
NTSTATUS pvfs_fill_dos_info(struct pvfs_state *pvfs, struct pvfs_filename *name,
			    unsigned int flags, int fd)
{
	NTSTATUS status;
	DATA_BLOB lkey;
	NTTIME write_time;

	/* make directories appear as size 0 with 1 link */
	if (S_ISDIR(name->st.st_mode)) {
		name->st.st_size = 0;
		name->st.st_nlink = 1;
	} else if (name->stream_id == 0) {
		name->stream_name = NULL;
	}

	/* for now just use the simple samba mapping */
	unix_to_nt_time(&name->dos.create_time, name->st.st_ctime);
	unix_to_nt_time(&name->dos.access_time, name->st.st_atime);
	unix_to_nt_time(&name->dos.write_time,  name->st.st_mtime);
	unix_to_nt_time(&name->dos.change_time, name->st.st_ctime);
#ifdef HAVE_STAT_TV_NSEC
	name->dos.create_time += name->st.st_ctim.tv_nsec / 100;
	name->dos.access_time += name->st.st_atim.tv_nsec / 100;
	name->dos.write_time  += name->st.st_mtim.tv_nsec / 100;
	name->dos.change_time += name->st.st_ctim.tv_nsec / 100;
#endif
	name->dos.attrib = dos_mode_from_stat(pvfs, &name->st);
	name->dos.alloc_size = pvfs_round_alloc_size(pvfs, name->st.st_size);
	name->dos.nlink = name->st.st_nlink;
	name->dos.ea_size = 4;  /* TODO: Fill this in without hitting the stream bad in pvfs_doseas_load() */
	if (pvfs->ntvfs->ctx->protocol >= PROTOCOL_SMB2_02) {
		/* SMB2 represents a null EA with zero bytes */
		name->dos.ea_size = 0;
	}
	
	name->dos.file_id = (((uint64_t)name->st.st_dev)<<32) | name->st.st_ino;
	name->dos.flags = 0;

	status = pvfs_dosattrib_load(pvfs, name, fd);
	NT_STATUS_NOT_OK_RETURN(status);

	if (flags & PVFS_RESOLVE_NO_OPENDB) {
		return NT_STATUS_OK;
	}

	status = pvfs_locking_key(name, name, &lkey);
	NT_STATUS_NOT_OK_RETURN(status);

	status = odb_get_file_infos(pvfs->odb_context, &lkey,
				    NULL, &write_time);
	data_blob_free(&lkey);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(1,("WARNING: odb_get_file_infos: %s\n", nt_errstr(status)));
		return status;
	}

	if (!null_time(write_time)) {
		name->dos.write_time = write_time;
	}

	return NT_STATUS_OK;
}
Ejemplo n.º 3
0
/*
  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;
}
Ejemplo n.º 4
0
/*
  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;
}