2 Unix SMB/CIFS implementation.
4 POSIX NTVFS backend - open and close
6 Copyright (C) Andrew Tridgell 2004
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "vfs_posix.h"
24 #include "system/dir.h"
25 #include "system/time.h"
26 #include "lib/util/dlinklist.h"
27 #include "messaging/messaging.h"
28 #include "librpc/gen_ndr/xattr.h"
31 find open file handle given fnum
33 struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
34 struct ntvfs_request *req, struct ntvfs_handle *h)
39 p = ntvfs_handle_get_backend_data(h, pvfs->ntvfs);
42 f = talloc_get_type(p, struct pvfs_file);
49 cleanup a open directory handle
51 static int pvfs_dir_handle_destructor(struct pvfs_file_handle *h)
53 if (h->have_opendb_entry) {
56 const char *delete_path = NULL;
58 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
60 DEBUG(0,("Unable to lock opendb for close\n"));
64 status = odb_close_file(lck, h, &delete_path);
65 if (!NT_STATUS_IS_OK(status)) {
66 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
67 h->name->full_name, nt_errstr(status)));
70 if (h->name->stream_name == NULL && delete_path) {
71 status = pvfs_xattr_unlink_hook(h->pvfs, delete_path);
72 if (!NT_STATUS_IS_OK(status)) {
73 DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
74 delete_path, nt_errstr(status)));
76 if (rmdir(delete_path) != 0) {
77 DEBUG(0,("pvfs_dir_handle_destructor: failed to rmdir '%s' - %s\n",
78 delete_path, strerror(errno)));
89 cleanup a open directory fnum
91 static int pvfs_dir_fnum_destructor(struct pvfs_file *f)
93 DLIST_REMOVE(f->pvfs->files.list, f);
94 ntvfs_handle_remove_backend_data(f->ntvfs, f->pvfs->ntvfs);
100 setup any EAs and the ACL on newly created files/directories
102 static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs,
103 struct ntvfs_request *req,
104 struct pvfs_filename *name,
105 int fd, struct pvfs_file *f,
110 /* setup any EAs that were asked for */
111 if (io->ntcreatex.in.ea_list) {
112 status = pvfs_setfileinfo_ea_set(pvfs, name, fd,
113 io->ntcreatex.in.ea_list->num_eas,
114 io->ntcreatex.in.ea_list->eas);
115 if (!NT_STATUS_IS_OK(status)) {
120 /* setup an initial sec_desc if requested */
121 if (io->ntcreatex.in.sec_desc) {
122 union smb_setfileinfo set;
124 * TODO: set the full ACL!
125 * - vista denies the creation of the file with NT_STATUS_PRIVILEGE_NOT_HELD,
126 * when a SACL is present on the sd,
127 * but the user doesn't have SeSecurityPrivilege
130 set.set_secdesc.in.file.ntvfs = f->ntvfs;
131 set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
132 set.set_secdesc.in.sd = io->ntcreatex.in.sec_desc;
134 status = pvfs_acl_set(pvfs, req, name, fd, SEC_STD_WRITE_DAC, &set);
136 /* otherwise setup an inherited acl from the parent */
137 status = pvfs_acl_inherit(pvfs, req, name, fd);
144 form the lock context used for opendb locking. Note that we must
145 zero here to take account of possible padding on some architectures
147 NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
148 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
154 ZERO_STRUCT(lock_context);
156 lock_context.device = name->st.st_dev;
157 lock_context.inode = name->st.st_ino;
159 *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
160 if (key->data == NULL) {
161 return NT_STATUS_NO_MEMORY;
171 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
172 struct ntvfs_request *req,
173 struct pvfs_filename *name,
177 struct ntvfs_handle *h;
179 uint32_t create_action;
180 uint32_t access_mask = io->generic.in.access_mask;
181 struct odb_lock *lck;
183 uint32_t create_options;
184 uint32_t share_access;
187 create_options = io->generic.in.create_options;
188 share_access = io->generic.in.share_access;
190 forced = (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)?true:false;
192 if (name->stream_name) {
194 return NT_STATUS_NOT_A_DIRECTORY;
196 return NT_STATUS_FILE_IS_A_DIRECTORY;
200 /* if the client says it must be a directory, and it isn't,
202 if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
203 return NT_STATUS_NOT_A_DIRECTORY;
206 /* found with gentest */
207 if (io->ntcreatex.in.access_mask == SEC_FLAG_MAXIMUM_ALLOWED &&
208 (io->ntcreatex.in.create_options & NTCREATEX_OPTIONS_DIRECTORY) &&
209 (io->ntcreatex.in.create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
210 return NT_STATUS_INVALID_PARAMETER;
213 switch (io->generic.in.open_disposition) {
214 case NTCREATEX_DISP_OPEN_IF:
217 case NTCREATEX_DISP_OPEN:
219 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
223 case NTCREATEX_DISP_CREATE:
225 return NT_STATUS_OBJECT_NAME_COLLISION;
229 case NTCREATEX_DISP_OVERWRITE_IF:
230 case NTCREATEX_DISP_OVERWRITE:
231 case NTCREATEX_DISP_SUPERSEDE:
233 return NT_STATUS_INVALID_PARAMETER;
236 status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
237 NT_STATUS_NOT_OK_RETURN(status);
239 f = talloc(h, struct pvfs_file);
241 return NT_STATUS_NO_MEMORY;
244 f->handle = talloc(f, struct pvfs_file_handle);
245 if (f->handle == NULL) {
246 return NT_STATUS_NO_MEMORY;
250 /* check the security descriptor */
251 status = pvfs_access_check(pvfs, req, name, &access_mask);
253 status = pvfs_access_check_create(pvfs, req, name, &access_mask);
255 NT_STATUS_NOT_OK_RETURN(status);
257 if (io->generic.in.query_maximal_access) {
258 status = pvfs_access_maximal_allowed(pvfs, req, name,
259 &io->generic.out.maximal_access);
260 NT_STATUS_NOT_OK_RETURN(status);
265 f->pending_list = NULL;
267 f->share_access = io->generic.in.share_access;
268 f->impersonation = io->generic.in.impersonation;
269 f->access_mask = access_mask;
270 f->brl_handle = NULL;
271 f->notify_buffer = NULL;
274 f->handle->pvfs = pvfs;
275 f->handle->name = talloc_steal(f->handle, name);
277 f->handle->odb_locking_key = data_blob(NULL, 0);
278 f->handle->create_options = io->generic.in.create_options;
279 f->handle->seek_offset = 0;
280 f->handle->position = 0;
282 f->handle->oplock = NULL;
283 ZERO_STRUCT(f->handle->write_time);
284 f->handle->open_completed = false;
286 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
287 pvfs_directory_empty(pvfs, f->handle->name)) {
290 del_on_close = false;
294 /* form the lock context used for opendb locking */
295 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
296 if (!NT_STATUS_IS_OK(status)) {
300 /* get a lock on this file before the actual open */
301 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
303 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
305 /* we were supposed to do a blocking lock, so something
307 return NT_STATUS_INTERNAL_DB_CORRUPTION;
310 /* see if we are allowed to open at the same time as existing opens */
311 status = odb_can_open(lck, name->stream_id,
312 share_access, access_mask, del_on_close,
313 io->generic.in.open_disposition, false);
314 if (!NT_STATUS_IS_OK(status)) {
319 /* now really mark the file as open */
320 status = odb_open_file(lck, f->handle, name->full_name,
321 NULL, name->dos.write_time,
322 false, OPLOCK_NONE, NULL);
324 if (!NT_STATUS_IS_OK(status)) {
329 f->handle->have_opendb_entry = true;
332 DLIST_ADD(pvfs->files.list, f);
334 /* setup destructors to avoid leaks on abnormal termination */
335 talloc_set_destructor(f->handle, pvfs_dir_handle_destructor);
336 talloc_set_destructor(f, pvfs_dir_fnum_destructor);
339 uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
340 mode_t mode = pvfs_fileperms(pvfs, attrib);
342 if (mkdir(name->full_name, mode) == -1) {
343 return pvfs_map_errno(pvfs,errno);
346 pvfs_xattr_unlink_hook(pvfs, name->full_name);
348 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
349 if (!NT_STATUS_IS_OK(status)) {
353 status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, f, io);
354 if (!NT_STATUS_IS_OK(status)) {
358 /* form the lock context used for opendb locking */
359 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
360 if (!NT_STATUS_IS_OK(status)) {
364 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
366 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
368 /* we were supposed to do a blocking lock, so something
370 return NT_STATUS_INTERNAL_DB_CORRUPTION;
373 status = odb_can_open(lck, name->stream_id,
374 share_access, access_mask, del_on_close,
375 io->generic.in.open_disposition, false);
377 if (!NT_STATUS_IS_OK(status)) {
381 status = odb_open_file(lck, f->handle, name->full_name,
382 NULL, name->dos.write_time,
383 false, OPLOCK_NONE, NULL);
385 if (!NT_STATUS_IS_OK(status)) {
389 f->handle->have_opendb_entry = true;
391 create_action = NTCREATEX_ACTION_CREATED;
393 notify_trigger(pvfs->notify_context,
395 FILE_NOTIFY_CHANGE_DIR_NAME,
398 create_action = NTCREATEX_ACTION_EXISTED;
402 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
405 /* the open succeeded, keep this handle permanently */
406 status = ntvfs_handle_set_backend_data(h, pvfs->ntvfs, f);
407 if (!NT_STATUS_IS_OK(status)) {
411 f->handle->open_completed = true;
413 io->generic.out.oplock_level = OPLOCK_NONE;
414 io->generic.out.file.ntvfs = h;
415 io->generic.out.create_action = create_action;
416 io->generic.out.create_time = name->dos.create_time;
417 io->generic.out.access_time = name->dos.access_time;
418 io->generic.out.write_time = name->dos.write_time;
419 io->generic.out.change_time = name->dos.change_time;
420 io->generic.out.attrib = name->dos.attrib;
421 io->generic.out.alloc_size = name->dos.alloc_size;
422 io->generic.out.size = name->st.st_size;
423 io->generic.out.file_type = FILE_TYPE_DISK;
424 io->generic.out.ipc_state = 0;
425 io->generic.out.is_directory = 1;
430 rmdir(name->full_name);
435 destroy a struct pvfs_file_handle
437 static int pvfs_handle_destructor(struct pvfs_file_handle *h)
439 talloc_free(h->write_time.update_event);
440 h->write_time.update_event = NULL;
442 if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
443 h->name->stream_name) {
445 status = pvfs_stream_delete(h->pvfs, h->name, h->fd);
446 if (!NT_STATUS_IS_OK(status)) {
447 DEBUG(0,("Failed to delete stream '%s' on close of '%s'\n",
448 h->name->stream_name, h->name->full_name));
453 if (close(h->fd) != 0) {
454 DEBUG(0,("pvfs_handle_destructor: close(%d) failed for %s - %s\n",
455 h->fd, h->name->full_name, strerror(errno)));
460 if (!h->write_time.update_forced &&
461 h->write_time.update_on_close &&
462 h->write_time.close_time == 0) {
464 tv = timeval_current();
465 h->write_time.close_time = timeval_to_nttime(&tv);
468 if (h->have_opendb_entry) {
469 struct odb_lock *lck;
471 const char *delete_path = NULL;
473 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
475 DEBUG(0,("Unable to lock opendb for close\n"));
479 if (h->write_time.update_forced) {
480 status = odb_get_file_infos(h->pvfs->odb_context,
483 &h->write_time.close_time);
484 if (!NT_STATUS_IS_OK(status)) {
485 DEBUG(0,("Unable get write time for '%s' - %s\n",
486 h->name->full_name, nt_errstr(status)));
489 h->write_time.update_forced = false;
490 h->write_time.update_on_close = true;
491 } else if (h->write_time.update_on_close) {
492 status = odb_set_write_time(lck, h->write_time.close_time, true);
493 if (!NT_STATUS_IS_OK(status)) {
494 DEBUG(0,("Unable set write time for '%s' - %s\n",
495 h->name->full_name, nt_errstr(status)));
499 status = odb_close_file(lck, h, &delete_path);
500 if (!NT_STATUS_IS_OK(status)) {
501 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
502 h->name->full_name, nt_errstr(status)));
505 if (h->name->stream_name == NULL &&
506 h->open_completed && delete_path) {
507 status = pvfs_xattr_unlink_hook(h->pvfs, delete_path);
508 if (!NT_STATUS_IS_OK(status)) {
509 DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
510 delete_path, nt_errstr(status)));
512 if (unlink(delete_path) != 0) {
513 DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n",
514 delete_path, strerror(errno)));
516 notify_trigger(h->pvfs->notify_context,
517 NOTIFY_ACTION_REMOVED,
518 FILE_NOTIFY_CHANGE_FILE_NAME,
521 h->write_time.update_on_close = false;
527 if (h->write_time.update_on_close) {
528 struct timeval tv[2];
530 nttime_to_timeval(&tv[0], h->name->dos.access_time);
531 nttime_to_timeval(&tv[1], h->write_time.close_time);
533 if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) {
534 if (utimes(h->name->full_name, tv) == -1) {
535 DEBUG(0,("pvfs_handle_destructor: utimes() failed '%s' - %s\n",
536 h->name->full_name, strerror(errno)));
546 destroy a struct pvfs_file
548 static int pvfs_fnum_destructor(struct pvfs_file *f)
550 DLIST_REMOVE(f->pvfs->files.list, f);
551 pvfs_lock_close(f->pvfs, f);
552 ntvfs_handle_remove_backend_data(f->ntvfs, f->pvfs->ntvfs);
559 form the lock context used for byte range locking. This is separate
560 from the locking key used for opendb locking as it needs to take
561 account of file streams (each stream is a separate byte range
564 static NTSTATUS pvfs_brl_locking_handle(TALLOC_CTX *mem_ctx,
565 struct pvfs_filename *name,
566 struct ntvfs_handle *ntvfs,
567 struct brl_handle **_h)
569 DATA_BLOB odb_key, key;
571 struct brl_handle *h;
573 status = pvfs_locking_key(name, mem_ctx, &odb_key);
574 NT_STATUS_NOT_OK_RETURN(status);
576 if (name->stream_name == NULL) {
579 key = data_blob_talloc(mem_ctx, NULL,
580 odb_key.length + strlen(name->stream_name) + 1);
581 NT_STATUS_HAVE_NO_MEMORY(key.data);
582 memcpy(key.data, odb_key.data, odb_key.length);
583 memcpy(key.data + odb_key.length,
584 name->stream_name, strlen(name->stream_name) + 1);
585 data_blob_free(&odb_key);
588 h = brl_create_handle(mem_ctx, ntvfs, &key);
589 NT_STATUS_HAVE_NO_MEMORY(h);
598 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
599 struct ntvfs_request *req,
600 struct pvfs_filename *name,
605 struct ntvfs_handle *h;
607 struct odb_lock *lck;
608 uint32_t create_options = io->generic.in.create_options;
609 uint32_t share_access = io->generic.in.share_access;
610 uint32_t access_mask = io->generic.in.access_mask;
614 struct pvfs_filename *parent;
615 uint32_t oplock_level = OPLOCK_NONE, oplock_granted;
616 bool allow_level_II_oplock = false;
618 if (io->ntcreatex.in.file_attr & ~FILE_ATTRIBUTE_ALL_MASK) {
619 return NT_STATUS_INVALID_PARAMETER;
622 if (io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_ENCRYPTED) {
623 return NT_STATUS_ACCESS_DENIED;
626 if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
627 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
628 return NT_STATUS_CANNOT_DELETE;
631 status = pvfs_access_check_create(pvfs, req, name, &access_mask);
632 NT_STATUS_NOT_OK_RETURN(status);
634 /* check that the parent isn't opened with delete on close set */
635 status = pvfs_resolve_parent(pvfs, req, name, &parent);
636 if (NT_STATUS_IS_OK(status)) {
637 DATA_BLOB locking_key;
638 status = pvfs_locking_key(parent, req, &locking_key);
639 NT_STATUS_NOT_OK_RETURN(status);
640 status = odb_get_file_infos(pvfs->odb_context, &locking_key,
641 &del_on_close, NULL);
642 NT_STATUS_NOT_OK_RETURN(status);
644 return NT_STATUS_DELETE_PENDING;
648 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
654 status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
655 NT_STATUS_NOT_OK_RETURN(status);
657 f = talloc(h, struct pvfs_file);
658 NT_STATUS_HAVE_NO_MEMORY(f);
660 f->handle = talloc(f, struct pvfs_file_handle);
661 NT_STATUS_HAVE_NO_MEMORY(f->handle);
663 attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
664 mode = pvfs_fileperms(pvfs, attrib);
666 /* create the file */
667 fd = open(name->full_name, flags | O_CREAT | O_EXCL| O_NONBLOCK, mode);
669 return pvfs_map_errno(pvfs, errno);
672 pvfs_xattr_unlink_hook(pvfs, name->full_name);
674 /* if this was a stream create then create the stream as well */
675 if (name->stream_name) {
676 status = pvfs_stream_create(pvfs, name, fd);
677 if (!NT_STATUS_IS_OK(status)) {
683 /* re-resolve the open fd */
684 status = pvfs_resolve_name_fd(pvfs, fd, name, 0);
685 if (!NT_STATUS_IS_OK(status)) {
690 /* support initial alloc sizes */
691 name->dos.alloc_size = io->ntcreatex.in.alloc_size;
692 name->dos.attrib = attrib;
693 status = pvfs_dosattrib_save(pvfs, name, fd);
694 if (!NT_STATUS_IS_OK(status)) {
699 status = pvfs_open_setup_eas_acl(pvfs, req, name, fd, f, io);
700 if (!NT_STATUS_IS_OK(status)) {
704 if (io->generic.in.query_maximal_access) {
705 status = pvfs_access_maximal_allowed(pvfs, req, name,
706 &io->generic.out.maximal_access);
707 NT_STATUS_NOT_OK_RETURN(status);
710 /* form the lock context used for byte range locking and
712 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
713 if (!NT_STATUS_IS_OK(status)) {
717 status = pvfs_brl_locking_handle(f, name, h, &f->brl_handle);
718 if (!NT_STATUS_IS_OK(status)) {
722 /* grab a lock on the open file record */
723 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
725 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
727 /* we were supposed to do a blocking lock, so something
729 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
733 if (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
736 del_on_close = false;
739 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
740 oplock_level = OPLOCK_NONE;
741 } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK) {
742 oplock_level = OPLOCK_BATCH;
743 } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_OPLOCK) {
744 oplock_level = OPLOCK_EXCLUSIVE;
747 if (req->client_caps & NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS) {
748 allow_level_II_oplock = true;
751 status = odb_can_open(lck, name->stream_id,
752 share_access, access_mask, del_on_close,
753 io->generic.in.open_disposition, false);
754 if (!NT_STATUS_IS_OK(status)) {
756 /* bad news, we must have hit a race - we don't delete the file
757 here as the most likely scenario is that someone else created
758 the file at the same time */
765 f->pending_list = NULL;
767 f->share_access = io->generic.in.share_access;
768 f->access_mask = access_mask;
769 f->impersonation = io->generic.in.impersonation;
770 f->notify_buffer = NULL;
773 f->handle->pvfs = pvfs;
774 f->handle->name = talloc_steal(f->handle, name);
776 f->handle->create_options = io->generic.in.create_options;
777 f->handle->seek_offset = 0;
778 f->handle->position = 0;
780 f->handle->oplock = NULL;
781 f->handle->have_opendb_entry = true;
782 ZERO_STRUCT(f->handle->write_time);
783 f->handle->open_completed = false;
785 status = odb_open_file(lck, f->handle, name->full_name,
786 &f->handle->fd, name->dos.write_time,
787 allow_level_II_oplock,
788 oplock_level, &oplock_granted);
790 if (!NT_STATUS_IS_OK(status)) {
791 /* bad news, we must have hit a race - we don't delete the file
792 here as the most likely scenario is that someone else created
793 the file at the same time */
798 DLIST_ADD(pvfs->files.list, f);
800 /* setup a destructor to avoid file descriptor leaks on
801 abnormal termination */
802 talloc_set_destructor(f, pvfs_fnum_destructor);
803 talloc_set_destructor(f->handle, pvfs_handle_destructor);
805 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
806 oplock_granted = OPLOCK_BATCH;
807 } else if (oplock_granted != OPLOCK_NONE) {
808 status = pvfs_setup_oplock(f, oplock_granted);
809 if (!NT_STATUS_IS_OK(status)) {
814 io->generic.out.oplock_level = oplock_granted;
815 io->generic.out.file.ntvfs = f->ntvfs;
816 io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
817 io->generic.out.create_time = name->dos.create_time;
818 io->generic.out.access_time = name->dos.access_time;
819 io->generic.out.write_time = name->dos.write_time;
820 io->generic.out.change_time = name->dos.change_time;
821 io->generic.out.attrib = name->dos.attrib;
822 io->generic.out.alloc_size = name->dos.alloc_size;
823 io->generic.out.size = name->st.st_size;
824 io->generic.out.file_type = FILE_TYPE_DISK;
825 io->generic.out.ipc_state = 0;
826 io->generic.out.is_directory = 0;
828 /* success - keep the file handle */
829 status = ntvfs_handle_set_backend_data(h, pvfs->ntvfs, f);
830 if (!NT_STATUS_IS_OK(status)) {
834 f->handle->open_completed = true;
836 notify_trigger(pvfs->notify_context,
838 FILE_NOTIFY_CHANGE_FILE_NAME,
845 unlink(name->full_name);
850 state of a pending retry
852 struct pvfs_odb_retry {
853 struct ntvfs_module_context *ntvfs;
854 struct ntvfs_request *req;
855 DATA_BLOB odb_locking_key;
858 void (*callback)(struct pvfs_odb_retry *r,
859 struct ntvfs_module_context *ntvfs,
860 struct ntvfs_request *req,
863 enum pvfs_wait_notice reason);
866 /* destroy a pending request */
867 static int pvfs_odb_retry_destructor(struct pvfs_odb_retry *r)
869 struct pvfs_state *pvfs = r->ntvfs->private_data;
870 if (r->odb_locking_key.data) {
871 struct odb_lock *lck;
872 lck = odb_lock(r->req, pvfs->odb_context, &r->odb_locking_key);
874 odb_remove_pending(lck, r);
881 static void pvfs_odb_retry_callback(void *_r, enum pvfs_wait_notice reason)
883 struct pvfs_odb_retry *r = talloc_get_type(_r, struct pvfs_odb_retry);
885 if (reason == PVFS_WAIT_EVENT) {
887 * The pending odb entry is already removed.
888 * We use a null locking key to indicate this
891 data_blob_free(&r->odb_locking_key);
894 r->callback(r, r->ntvfs, r->req, r->io, r->private_data, reason);
898 setup for a retry of a request that was rejected
901 NTSTATUS pvfs_odb_retry_setup(struct ntvfs_module_context *ntvfs,
902 struct ntvfs_request *req,
903 struct odb_lock *lck,
904 struct timeval end_time,
907 void (*callback)(struct pvfs_odb_retry *r,
908 struct ntvfs_module_context *ntvfs,
909 struct ntvfs_request *req,
912 enum pvfs_wait_notice reason))
914 struct pvfs_state *pvfs = ntvfs->private_data;
915 struct pvfs_odb_retry *r;
916 struct pvfs_wait *wait_handle;
919 r = talloc(req, struct pvfs_odb_retry);
920 NT_STATUS_HAVE_NO_MEMORY(r);
925 r->private_data = private_data;
926 r->callback = callback;
927 r->odb_locking_key = odb_get_key(r, lck);
928 if (r->odb_locking_key.data == NULL) {
929 return NT_STATUS_NO_MEMORY;
932 /* setup a pending lock */
933 status = odb_open_file_pending(lck, r);
934 if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND,status)) {
936 * maybe only a unix application
939 data_blob_free(&r->odb_locking_key);
940 } else if (!NT_STATUS_IS_OK(status)) {
946 talloc_set_destructor(r, pvfs_odb_retry_destructor);
948 wait_handle = pvfs_wait_message(pvfs, req,
949 MSG_PVFS_RETRY_OPEN, end_time,
950 pvfs_odb_retry_callback, r);
951 if (wait_handle == NULL) {
952 return NT_STATUS_NO_MEMORY;
955 talloc_steal(r, wait_handle);
961 retry an open after a sharing violation
963 static void pvfs_retry_open_sharing(struct pvfs_odb_retry *r,
964 struct ntvfs_module_context *ntvfs,
965 struct ntvfs_request *req,
968 enum pvfs_wait_notice reason)
970 union smb_open *io = talloc_get_type(_io, union smb_open);
971 struct timeval *final_timeout = NULL;
975 final_timeout = talloc_get_type(private_data,
979 /* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
980 just a bug in their server, but we better do the same */
981 if (reason == PVFS_WAIT_CANCEL) {
985 if (reason == PVFS_WAIT_TIMEOUT) {
987 !timeval_expired(final_timeout)) {
989 * we need to retry periodictly
990 * after an EAGAIN as there's
991 * no way the kernel tell us
992 * an oplock is released.
996 /* if it timed out, then give the failure
999 req->async_states->status = NT_STATUS_SHARING_VIOLATION;
1000 req->async_states->send_fn(req);
1007 /* try the open again, which could trigger another retry setup
1008 if it wants to, so we have to unmark the async flag so we
1009 will know if it does a second async reply */
1010 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
1012 status = pvfs_open(ntvfs, req, io);
1013 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
1014 /* the 2nd try also replied async, so we don't send
1019 /* re-mark it async, just in case someone up the chain does
1020 paranoid checking */
1021 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
1023 /* send the reply up the chain */
1024 req->async_states->status = status;
1025 req->async_states->send_fn(req);
1030 special handling for openx DENY_DOS semantics
1032 This function attempts a reference open using an existing handle. If its allowed,
1033 then it returns NT_STATUS_OK, otherwise it returns any other code and normal
1034 open processing continues.
1036 static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
1037 struct ntvfs_request *req, union smb_open *io,
1038 struct pvfs_file *f, struct odb_lock *lck)
1040 struct pvfs_state *pvfs = ntvfs->private_data;
1041 struct pvfs_file *f2;
1042 struct pvfs_filename *name;
1045 /* search for an existing open with the right parameters. Note
1046 the magic ntcreatex options flag, which is set in the
1047 generic mapping code. This might look ugly, but its
1048 actually pretty much now w2k does it internally as well.
1050 If you look at the BASE-DENYDOS test you will see that a
1051 DENY_DOS is a very special case, and in the right
1052 circumstances you actually get the _same_ handle back
1053 twice, rather than a new handle.
1055 for (f2=pvfs->files.list;f2;f2=f2->next) {
1057 f2->ntvfs->session_info == req->session_info &&
1058 f2->ntvfs->smbpid == req->smbpid &&
1059 (f2->handle->create_options &
1060 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
1061 NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) &&
1062 (f2->access_mask & SEC_FILE_WRITE_DATA) &&
1063 strcasecmp_m(f2->handle->name->original_name,
1064 io->generic.in.fname)==0) {
1070 return NT_STATUS_SHARING_VIOLATION;
1073 /* quite an insane set of semantics ... */
1074 if (is_exe_filename(io->generic.in.fname) &&
1075 (f2->handle->create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) {
1076 return NT_STATUS_SHARING_VIOLATION;
1080 setup a reference to the existing handle
1082 talloc_free(f->handle);
1083 f->handle = talloc_reference(f, f2->handle);
1087 name = f->handle->name;
1089 io->generic.out.oplock_level = OPLOCK_NONE;
1090 io->generic.out.file.ntvfs = f->ntvfs;
1091 io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
1092 io->generic.out.create_time = name->dos.create_time;
1093 io->generic.out.access_time = name->dos.access_time;
1094 io->generic.out.write_time = name->dos.write_time;
1095 io->generic.out.change_time = name->dos.change_time;
1096 io->generic.out.attrib = name->dos.attrib;
1097 io->generic.out.alloc_size = name->dos.alloc_size;
1098 io->generic.out.size = name->st.st_size;
1099 io->generic.out.file_type = FILE_TYPE_DISK;
1100 io->generic.out.ipc_state = 0;
1101 io->generic.out.is_directory = 0;
1103 status = ntvfs_handle_set_backend_data(f->ntvfs, ntvfs, f);
1104 NT_STATUS_NOT_OK_RETURN(status);
1106 return NT_STATUS_OK;
1112 setup for a open retry after a sharing violation
1114 static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
1115 struct ntvfs_request *req,
1117 struct pvfs_file *f,
1118 struct odb_lock *lck,
1119 NTSTATUS parent_status)
1121 struct pvfs_state *pvfs = ntvfs->private_data;
1123 struct timeval end_time;
1124 struct timeval *final_timeout = NULL;
1126 if (io->generic.in.create_options &
1127 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
1128 /* see if we can satisfy the request using the special DENY_DOS
1130 status = pvfs_open_deny_dos(ntvfs, req, io, f, lck);
1131 if (NT_STATUS_IS_OK(status)) {
1136 /* the retry should allocate a new file handle */
1139 if (NT_STATUS_EQUAL(parent_status, NT_STATUS_SHARING_VIOLATION)) {
1140 end_time = timeval_add(&req->statistics.request_time,
1141 0, pvfs->sharing_violation_delay);
1142 } else if (NT_STATUS_EQUAL(parent_status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
1143 end_time = timeval_add(&req->statistics.request_time,
1144 pvfs->oplock_break_timeout, 0);
1145 } else if (NT_STATUS_EQUAL(parent_status, STATUS_MORE_ENTRIES)) {
1147 * we got EAGAIN which means a unix application
1148 * has an oplock or share mode
1150 * we retry every 4/5 of the sharing violation delay
1151 * to see if the unix application
1152 * has released the oplock or share mode.
1154 final_timeout = talloc(req, struct timeval);
1155 NT_STATUS_HAVE_NO_MEMORY(final_timeout);
1156 *final_timeout = timeval_add(&req->statistics.request_time,
1157 pvfs->oplock_break_timeout,
1159 end_time = timeval_current_ofs(0, (pvfs->sharing_violation_delay*4)/5);
1160 end_time = timeval_min(final_timeout, &end_time);
1162 return NT_STATUS_INTERNAL_ERROR;
1165 return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io,
1166 final_timeout, pvfs_retry_open_sharing);
1172 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
1173 struct ntvfs_request *req, union smb_open *io)
1175 struct pvfs_state *pvfs = ntvfs->private_data;
1177 struct pvfs_filename *name;
1178 struct pvfs_file *f;
1179 struct ntvfs_handle *h;
1182 struct odb_lock *lck;
1183 uint32_t create_options;
1184 uint32_t share_access;
1185 uint32_t access_mask;
1186 uint32_t create_action = NTCREATEX_ACTION_EXISTED;
1188 bool stream_existed, stream_truncate=false;
1189 uint32_t oplock_level = OPLOCK_NONE, oplock_granted;
1190 bool allow_level_II_oplock = false;
1192 /* use the generic mapping code to avoid implementing all the
1193 different open calls. */
1194 if (io->generic.level != RAW_OPEN_GENERIC &&
1195 io->generic.level != RAW_OPEN_NTTRANS_CREATE) {
1196 return ntvfs_map_open(ntvfs, req, io);
1199 ZERO_STRUCT(io->generic.out);
1201 create_options = io->generic.in.create_options;
1202 share_access = io->generic.in.share_access;
1203 access_mask = io->generic.in.access_mask;
1205 if (share_access & ~NTCREATEX_SHARE_ACCESS_MASK) {
1206 return NT_STATUS_INVALID_PARAMETER;
1209 /* These options are ignored */
1210 create_options &= ~NTCREATEX_OPTIONS_MUST_IGNORE_MASK;
1212 if (create_options & NTCREATEX_OPTIONS_NOT_SUPPORTED_MASK) {
1213 return NT_STATUS_NOT_SUPPORTED;
1216 /* TODO: When we implement HSM, add a hook here not to pull
1217 * the actual file off tape, when this option is passed from
1219 if (create_options & NTCREATEX_OPTIONS_NO_RECALL) {
1223 /* TODO: If (unlikely) Linux does a good compressed
1224 * filesystem, we might need an ioctl call for this */
1225 if (create_options & NTCREATEX_OPTIONS_NO_COMPRESSION) {
1229 if (create_options & NTCREATEX_OPTIONS_NO_INTERMEDIATE_BUFFERING) {
1230 create_options |= NTCREATEX_OPTIONS_WRITE_THROUGH;
1233 /* Open the file with sync, if they asked for it, but
1234 'strict sync = no' turns this client request into a no-op */
1235 if (create_options & (NTCREATEX_OPTIONS_WRITE_THROUGH) && !(pvfs->flags | PVFS_FLAG_STRICT_SYNC)) {
1240 /* other create options are not allowed */
1241 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
1242 !(access_mask & SEC_STD_DELETE)) {
1243 return NT_STATUS_INVALID_PARAMETER;
1246 if (access_mask & SEC_MASK_INVALID) {
1247 return NT_STATUS_ACCESS_DENIED;
1250 /* what does this bit really mean?? */
1251 if (req->ctx->protocol == PROTOCOL_SMB2 &&
1252 access_mask == SEC_STD_SYNCHRONIZE) {
1253 return NT_STATUS_ACCESS_DENIED;
1256 if (io->ntcreatex.in.file_attr & (FILE_ATTRIBUTE_DEVICE|
1257 FILE_ATTRIBUTE_VOLUME|
1258 (~FILE_ATTRIBUTE_ALL_MASK))) {
1259 return NT_STATUS_INVALID_PARAMETER;
1262 /* we ignore some file_attr bits */
1263 io->ntcreatex.in.file_attr &= ~(FILE_ATTRIBUTE_NONINDEXED |
1264 FILE_ATTRIBUTE_COMPRESSED |
1265 FILE_ATTRIBUTE_REPARSE_POINT |
1266 FILE_ATTRIBUTE_SPARSE |
1267 FILE_ATTRIBUTE_NORMAL);
1269 /* resolve the cifs name to a posix name */
1270 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
1271 PVFS_RESOLVE_STREAMS, &name);
1272 if (!NT_STATUS_IS_OK(status)) {
1276 /* if the client specified that it must not be a directory then
1277 check that it isn't */
1278 if (name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
1279 (io->generic.in.create_options & NTCREATEX_OPTIONS_NON_DIRECTORY_FILE)) {
1280 return NT_STATUS_FILE_IS_A_DIRECTORY;
1283 /* if the client specified that it must be a directory then
1285 if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
1286 (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
1287 return NT_STATUS_NOT_A_DIRECTORY;
1290 /* directory opens are handled separately */
1291 if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
1292 (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
1293 return pvfs_open_directory(pvfs, req, name, io);
1296 /* FILE_ATTRIBUTE_DIRECTORY is ignored if the above test for directory
1297 open doesn't match */
1298 io->generic.in.file_attr &= ~FILE_ATTRIBUTE_DIRECTORY;
1300 switch (io->generic.in.open_disposition) {
1301 case NTCREATEX_DISP_SUPERSEDE:
1302 case NTCREATEX_DISP_OVERWRITE_IF:
1303 if (name->stream_name == NULL) {
1306 stream_truncate = true;
1308 create_action = NTCREATEX_ACTION_TRUNCATED;
1311 case NTCREATEX_DISP_OPEN:
1312 if (!name->stream_exists) {
1313 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1318 case NTCREATEX_DISP_OVERWRITE:
1319 if (!name->stream_exists) {
1320 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1322 if (name->stream_name == NULL) {
1325 stream_truncate = true;
1327 create_action = NTCREATEX_ACTION_TRUNCATED;
1330 case NTCREATEX_DISP_CREATE:
1331 if (name->stream_exists) {
1332 return NT_STATUS_OBJECT_NAME_COLLISION;
1337 case NTCREATEX_DISP_OPEN_IF:
1342 return NT_STATUS_INVALID_PARAMETER;
1345 /* handle creating a new file separately */
1346 if (!name->exists) {
1347 status = pvfs_create_file(pvfs, req, name, io);
1348 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
1352 /* we've hit a race - the file was created during this call */
1353 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
1357 /* try re-resolving the name */
1358 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
1359 if (!NT_STATUS_IS_OK(status)) {
1362 /* fall through to a normal open */
1365 if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
1366 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
1367 return NT_STATUS_CANNOT_DELETE;
1370 /* check the security descriptor */
1371 status = pvfs_access_check(pvfs, req, name, &access_mask);
1372 NT_STATUS_NOT_OK_RETURN(status);
1374 if (io->generic.in.query_maximal_access) {
1375 status = pvfs_access_maximal_allowed(pvfs, req, name,
1376 &io->generic.out.maximal_access);
1377 NT_STATUS_NOT_OK_RETURN(status);
1380 status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
1381 NT_STATUS_NOT_OK_RETURN(status);
1383 f = talloc(h, struct pvfs_file);
1385 return NT_STATUS_NO_MEMORY;
1388 f->handle = talloc(f, struct pvfs_file_handle);
1389 if (f->handle == NULL) {
1390 return NT_STATUS_NO_MEMORY;
1395 f->pending_list = NULL;
1397 f->share_access = io->generic.in.share_access;
1398 f->access_mask = access_mask;
1399 f->impersonation = io->generic.in.impersonation;
1400 f->notify_buffer = NULL;
1403 f->handle->pvfs = pvfs;
1405 f->handle->name = talloc_steal(f->handle, name);
1406 f->handle->create_options = io->generic.in.create_options;
1407 f->handle->seek_offset = 0;
1408 f->handle->position = 0;
1409 f->handle->mode = 0;
1410 f->handle->oplock = NULL;
1411 f->handle->have_opendb_entry = false;
1412 ZERO_STRUCT(f->handle->write_time);
1413 f->handle->open_completed = false;
1415 /* form the lock context used for byte range locking and
1417 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
1418 if (!NT_STATUS_IS_OK(status)) {
1422 status = pvfs_brl_locking_handle(f, name, h, &f->brl_handle);
1423 if (!NT_STATUS_IS_OK(status)) {
1427 /* get a lock on this file before the actual open */
1428 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1430 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
1432 /* we were supposed to do a blocking lock, so something
1434 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1437 DLIST_ADD(pvfs->files.list, f);
1439 /* setup a destructor to avoid file descriptor leaks on
1440 abnormal termination */
1441 talloc_set_destructor(f, pvfs_fnum_destructor);
1442 talloc_set_destructor(f->handle, pvfs_handle_destructor);
1445 * Only SMB2 takes care of the delete_on_close,
1448 if (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE &&
1449 req->ctx->protocol == PROTOCOL_SMB2) {
1450 del_on_close = true;
1452 del_on_close = false;
1455 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
1456 oplock_level = OPLOCK_NONE;
1457 } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK) {
1458 oplock_level = OPLOCK_BATCH;
1459 } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_OPLOCK) {
1460 oplock_level = OPLOCK_EXCLUSIVE;
1463 if (req->client_caps & NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS) {
1464 allow_level_II_oplock = true;
1467 /* see if we are allowed to open at the same time as existing opens */
1468 status = odb_can_open(lck, name->stream_id,
1469 share_access, access_mask, del_on_close,
1470 io->generic.in.open_disposition, false);
1473 * on a sharing violation we need to retry when the file is closed by
1474 * the other user, or after 1 second
1475 * on a non granted oplock we need to retry when the file is closed by
1476 * the other user, or after 30 seconds
1478 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
1479 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
1480 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
1481 return pvfs_open_setup_retry(ntvfs, req, io, f, lck, status);
1484 if (!NT_STATUS_IS_OK(status)) {
1489 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
1495 /* do the actual open */
1496 fd = open(f->handle->name->full_name, flags | O_NONBLOCK);
1498 status = pvfs_map_errno(f->pvfs, errno);
1501 * STATUS_MORE_ENTRIES is EAGAIN or EWOULDBLOCK
1503 if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) &&
1504 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
1505 return pvfs_open_setup_retry(ntvfs, req, io, f, lck, status);
1514 /* now really mark the file as open */
1515 status = odb_open_file(lck, f->handle, name->full_name,
1516 &f->handle->fd, name->dos.write_time,
1517 allow_level_II_oplock,
1518 oplock_level, &oplock_granted);
1520 if (!NT_STATUS_IS_OK(status)) {
1525 f->handle->have_opendb_entry = true;
1527 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
1528 oplock_granted = OPLOCK_BATCH;
1529 } else if (oplock_granted != OPLOCK_NONE) {
1530 status = pvfs_setup_oplock(f, oplock_granted);
1531 if (!NT_STATUS_IS_OK(status)) {
1537 stream_existed = name->stream_exists;
1539 /* if this was a stream create then create the stream as well */
1540 if (!name->stream_exists) {
1541 status = pvfs_stream_create(pvfs, f->handle->name, fd);
1542 if (!NT_STATUS_IS_OK(status)) {
1546 if (stream_truncate) {
1547 status = pvfs_stream_truncate(pvfs, f->handle->name, fd, 0);
1548 if (!NT_STATUS_IS_OK(status)) {
1555 /* re-resolve the open fd */
1556 status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name, PVFS_RESOLVE_NO_OPENDB);
1557 if (!NT_STATUS_IS_OK(status)) {
1562 if (f->handle->name->stream_id == 0 &&
1563 (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE ||
1564 io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) {
1565 /* for overwrite we need to replace file permissions */
1566 uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
1567 mode_t mode = pvfs_fileperms(pvfs, attrib);
1568 if (fchmod(fd, mode) == -1) {
1570 return pvfs_map_errno(pvfs, errno);
1572 name->dos.alloc_size = io->ntcreatex.in.alloc_size;
1573 name->dos.attrib = attrib;
1574 status = pvfs_dosattrib_save(pvfs, name, fd);
1575 if (!NT_STATUS_IS_OK(status)) {
1583 status = ntvfs_handle_set_backend_data(h, ntvfs, f);
1584 NT_STATUS_NOT_OK_RETURN(status);
1586 /* mark the open as having completed fully, so delete on close
1588 f->handle->open_completed = true;
1590 io->generic.out.oplock_level = oplock_granted;
1591 io->generic.out.file.ntvfs = h;
1592 io->generic.out.create_action = stream_existed?
1593 create_action:NTCREATEX_ACTION_CREATED;
1595 io->generic.out.create_time = name->dos.create_time;
1596 io->generic.out.access_time = name->dos.access_time;
1597 io->generic.out.write_time = name->dos.write_time;
1598 io->generic.out.change_time = name->dos.change_time;
1599 io->generic.out.attrib = name->dos.attrib;
1600 io->generic.out.alloc_size = name->dos.alloc_size;
1601 io->generic.out.size = name->st.st_size;
1602 io->generic.out.file_type = FILE_TYPE_DISK;
1603 io->generic.out.ipc_state = 0;
1604 io->generic.out.is_directory = 0;
1606 return NT_STATUS_OK;
1613 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
1614 struct ntvfs_request *req, union smb_close *io)
1616 struct pvfs_state *pvfs = ntvfs->private_data;
1617 struct pvfs_file *f;
1619 if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
1620 return NT_STATUS_DOS(ERRSRV, ERRerror);
1623 if (io->generic.level != RAW_CLOSE_GENERIC) {
1624 return ntvfs_map_close(ntvfs, req, io);
1627 f = pvfs_find_fd(pvfs, req, io->generic.in.file.ntvfs);
1629 return NT_STATUS_INVALID_HANDLE;
1632 if (!null_time(io->generic.in.write_time)) {
1633 f->handle->write_time.update_forced = false;
1634 f->handle->write_time.update_on_close = true;
1635 unix_to_nt_time(&f->handle->write_time.close_time, io->generic.in.write_time);
1638 if (io->generic.in.flags & SMB2_CLOSE_FLAGS_FULL_INFORMATION) {
1639 struct pvfs_filename *name;
1641 struct pvfs_file_handle *h = f->handle;
1643 status = pvfs_resolve_name_handle(pvfs, h);
1644 if (!NT_STATUS_IS_OK(status)) {
1649 io->generic.out.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION;
1650 io->generic.out.create_time = name->dos.create_time;
1651 io->generic.out.access_time = name->dos.access_time;
1652 io->generic.out.write_time = name->dos.write_time;
1653 io->generic.out.change_time = name->dos.change_time;
1654 io->generic.out.alloc_size = name->dos.alloc_size;
1655 io->generic.out.size = name->st.st_size;
1656 io->generic.out.file_attr = name->dos.attrib;
1658 ZERO_STRUCT(io->generic.out);
1663 return NT_STATUS_OK;
1668 logoff - close all file descriptors open by a vuid
1670 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
1671 struct ntvfs_request *req)
1673 struct pvfs_state *pvfs = ntvfs->private_data;
1674 struct pvfs_file *f, *next;
1676 for (f=pvfs->files.list;f;f=next) {
1678 if (f->ntvfs->session_info == req->session_info) {
1683 return NT_STATUS_OK;
1688 exit - close files for the current pid
1690 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
1691 struct ntvfs_request *req)
1693 struct pvfs_state *pvfs = ntvfs->private_data;
1694 struct pvfs_file *f, *next;
1696 for (f=pvfs->files.list;f;f=next) {
1698 if (f->ntvfs->session_info == req->session_info &&
1699 f->ntvfs->smbpid == req->smbpid) {
1704 return NT_STATUS_OK;
1709 change the delete on close flag on an already open file
1711 NTSTATUS pvfs_set_delete_on_close(struct pvfs_state *pvfs,
1712 struct ntvfs_request *req,
1713 struct pvfs_file *f, bool del_on_close)
1715 struct odb_lock *lck;
1718 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) && del_on_close) {
1719 return NT_STATUS_CANNOT_DELETE;
1722 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
1723 !pvfs_directory_empty(pvfs, f->handle->name)) {
1724 return NT_STATUS_DIRECTORY_NOT_EMPTY;
1728 f->handle->create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1730 f->handle->create_options &= ~NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1733 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1735 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1738 status = odb_set_delete_on_close(lck, del_on_close);
1747 determine if a file can be deleted, or if it is prevented by an
1750 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs,
1751 struct ntvfs_request *req,
1752 struct pvfs_filename *name,
1753 struct odb_lock **lckp)
1757 struct odb_lock *lck;
1758 uint32_t share_access;
1759 uint32_t access_mask;
1760 bool delete_on_close;
1762 status = pvfs_locking_key(name, name, &key);
1763 if (!NT_STATUS_IS_OK(status)) {
1764 return NT_STATUS_NO_MEMORY;
1767 lck = odb_lock(req, pvfs->odb_context, &key);
1769 DEBUG(0,("Unable to lock opendb for can_delete\n"));
1770 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1773 share_access = NTCREATEX_SHARE_ACCESS_READ |
1774 NTCREATEX_SHARE_ACCESS_WRITE |
1775 NTCREATEX_SHARE_ACCESS_DELETE;
1776 access_mask = SEC_STD_DELETE;
1777 delete_on_close = true;
1779 status = odb_can_open(lck, name->stream_id,
1780 share_access, access_mask, delete_on_close,
1781 NTCREATEX_DISP_OPEN, false);
1783 if (NT_STATUS_IS_OK(status)) {
1784 status = pvfs_access_check_simple(pvfs, req, name, access_mask);
1788 * if it's a sharing violation or we got no oplock
1789 * only keep the lock if the caller requested access
1792 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
1793 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
1799 } else if (!NT_STATUS_IS_OK(status)) {
1812 determine if a file can be renamed, or if it is prevented by an
1815 NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs,
1816 struct ntvfs_request *req,
1817 struct pvfs_filename *name,
1818 struct odb_lock **lckp)
1822 struct odb_lock *lck;
1823 uint32_t share_access;
1824 uint32_t access_mask;
1825 bool delete_on_close;
1827 status = pvfs_locking_key(name, name, &key);
1828 if (!NT_STATUS_IS_OK(status)) {
1829 return NT_STATUS_NO_MEMORY;
1832 lck = odb_lock(req, pvfs->odb_context, &key);
1834 DEBUG(0,("Unable to lock opendb for can_stat\n"));
1835 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1838 share_access = NTCREATEX_SHARE_ACCESS_READ |
1839 NTCREATEX_SHARE_ACCESS_WRITE;
1840 access_mask = SEC_STD_DELETE;
1841 delete_on_close = false;
1843 status = odb_can_open(lck, name->stream_id,
1844 share_access, access_mask, delete_on_close,
1845 NTCREATEX_DISP_OPEN, false);
1848 * if it's a sharing violation or we got no oplock
1849 * only keep the lock if the caller requested access
1852 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
1853 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
1859 } else if (!NT_STATUS_IS_OK(status)) {
1872 determine if the file size of a file can be changed,
1873 or if it is prevented by an already open file
1875 NTSTATUS pvfs_can_update_file_size(struct pvfs_state *pvfs,
1876 struct ntvfs_request *req,
1877 struct pvfs_filename *name,
1878 struct odb_lock **lckp)
1882 struct odb_lock *lck;
1883 uint32_t share_access;
1884 uint32_t access_mask;
1886 bool delete_on_close;
1888 status = pvfs_locking_key(name, name, &key);
1889 if (!NT_STATUS_IS_OK(status)) {
1890 return NT_STATUS_NO_MEMORY;
1893 lck = odb_lock(req, pvfs->odb_context, &key);
1895 DEBUG(0,("Unable to lock opendb for can_stat\n"));
1896 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1899 share_access = NTCREATEX_SHARE_ACCESS_READ |
1900 NTCREATEX_SHARE_ACCESS_WRITE |
1901 NTCREATEX_SHARE_ACCESS_DELETE;
1903 * I would have thought that we would need to pass
1904 * SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA here too
1906 * But you only need SEC_FILE_WRITE_ATTRIBUTE permissions
1907 * to set the filesize.
1911 access_mask = SEC_FILE_WRITE_ATTRIBUTE;
1912 delete_on_close = false;
1913 break_to_none = true;
1915 status = odb_can_open(lck, name->stream_id,
1916 share_access, access_mask, delete_on_close,
1917 NTCREATEX_DISP_OPEN, break_to_none);
1920 * if it's a sharing violation or we got no oplock
1921 * only keep the lock if the caller requested access
1924 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
1925 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
1931 } else if (!NT_STATUS_IS_OK(status)) {
1944 determine if file meta data can be accessed, or if it is prevented by an
1947 NTSTATUS pvfs_can_stat(struct pvfs_state *pvfs,
1948 struct ntvfs_request *req,
1949 struct pvfs_filename *name)
1953 struct odb_lock *lck;
1954 uint32_t share_access;
1955 uint32_t access_mask;
1956 bool delete_on_close;
1958 status = pvfs_locking_key(name, name, &key);
1959 if (!NT_STATUS_IS_OK(status)) {
1960 return NT_STATUS_NO_MEMORY;
1963 lck = odb_lock(req, pvfs->odb_context, &key);
1965 DEBUG(0,("Unable to lock opendb for can_stat\n"));
1966 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1969 share_access = NTCREATEX_SHARE_ACCESS_READ |
1970 NTCREATEX_SHARE_ACCESS_WRITE;
1971 access_mask = SEC_FILE_READ_ATTRIBUTE;
1972 delete_on_close = false;
1974 status = odb_can_open(lck, name->stream_id,
1975 share_access, access_mask, delete_on_close,
1976 NTCREATEX_DISP_OPEN, false);
1978 if (!NT_STATUS_IS_OK(status)) {
1987 determine if delete on close is set on
1989 bool pvfs_delete_on_close_set(struct pvfs_state *pvfs, struct pvfs_file_handle *h)
1994 status = odb_get_file_infos(pvfs->odb_context, &h->odb_locking_key,
1995 &del_on_close, NULL);
1996 if (!NT_STATUS_IS_OK(status)) {
1997 DEBUG(1,("WARNING: unable to determine delete on close status for open file\n"));
2001 return del_on_close;