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 2 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, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "vfs_posix.h"
25 #include "system/dir.h"
26 #include "system/time.h"
27 #include "dlinklist.h"
28 #include "messaging/messaging.h"
29 #include "librpc/gen_ndr/xattr.h"
32 create file handles with convenient numbers for sniffers
34 #define PVFS_MIN_FILE_FNUM 0x100
35 #define PVFS_MIN_NEW_FNUM 0x200
36 #define PVFS_MIN_DIR_FNUM 0x300
39 find open file handle given fnum
41 struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
42 struct ntvfs_request *req, uint16_t fnum)
46 f = idr_find(pvfs->files.idtree, fnum);
51 if (f->fnum != fnum) {
52 smb_panic("pvfs_find_fd: idtree_fnum corruption\n");
55 if (req->session_info != f->session_info) {
56 DEBUG(2,("pvfs_find_fd: attempt to use wrong session for fnum %d\n",
66 cleanup a open directory handle
68 static int pvfs_dir_handle_destructor(void *p)
70 struct pvfs_file_handle *h = p;
74 if (h->name->stream_name == NULL &&
75 pvfs_delete_on_close_set(h->pvfs, h, &open_count, &path) &&
78 status = pvfs_xattr_unlink_hook(h->pvfs, path);
79 if (!NT_STATUS_IS_OK(status)) {
80 DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
81 path, nt_errstr(status)));
83 if (rmdir(path) != 0) {
84 DEBUG(0,("pvfs_dir_handle_destructor: failed to rmdir '%s' - %s\n",
85 path, strerror(errno)));
89 if (h->have_opendb_entry) {
93 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
95 DEBUG(0,("Unable to lock opendb for close\n"));
99 status = odb_close_file(lck, h);
100 if (!NT_STATUS_IS_OK(status)) {
101 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
102 h->name->full_name, nt_errstr(status)));
112 cleanup a open directory fnum
114 static int pvfs_dir_fnum_destructor(void *p)
116 struct pvfs_file *f = p;
117 DLIST_REMOVE(f->pvfs->files.list, f);
118 idr_remove(f->pvfs->files.idtree, f->fnum);
123 setup any EAs and the ACL on newly created files/directories
125 static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs,
126 struct ntvfs_request *req,
127 struct pvfs_filename *name,
133 /* setup any EAs that were asked for */
134 if (io->ntcreatex.in.ea_list) {
135 status = pvfs_setfileinfo_ea_set(pvfs, name, fd,
136 io->ntcreatex.in.ea_list->num_eas,
137 io->ntcreatex.in.ea_list->eas);
138 if (!NT_STATUS_IS_OK(status)) {
143 /* setup an initial sec_desc if requested */
144 if (io->ntcreatex.in.sec_desc) {
145 union smb_setfileinfo set;
147 set.set_secdesc.in.file.fnum = fnum;
148 set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
149 set.set_secdesc.in.sd = io->ntcreatex.in.sec_desc;
151 status = pvfs_acl_set(pvfs, req, name, fd, SEC_STD_WRITE_DAC, &set);
153 /* otherwise setup an inherited acl from the parent */
154 status = pvfs_acl_inherit(pvfs, req, name, fd);
161 form the lock context used for opendb locking. Note that we must
162 zero here to take account of possible padding on some architectures
164 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
165 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
171 ZERO_STRUCT(lock_context);
173 lock_context.device = name->st.st_dev;
174 lock_context.inode = name->st.st_ino;
176 *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
177 if (key->data == NULL) {
178 return NT_STATUS_NO_MEMORY;
188 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
189 struct ntvfs_request *req,
190 struct pvfs_filename *name,
196 uint32_t create_action;
197 uint32_t access_mask = io->generic.in.access_mask;
198 struct odb_lock *lck;
200 uint32_t create_options;
201 uint32_t share_access;
203 create_options = io->generic.in.create_options;
204 share_access = io->generic.in.share_access;
206 if (name->stream_name) {
207 return NT_STATUS_NOT_A_DIRECTORY;
210 /* if the client says it must be a directory, and it isn't,
212 if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
213 return NT_STATUS_NOT_A_DIRECTORY;
216 switch (io->generic.in.open_disposition) {
217 case NTCREATEX_DISP_OPEN_IF:
220 case NTCREATEX_DISP_OPEN:
222 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
226 case NTCREATEX_DISP_CREATE:
228 return NT_STATUS_OBJECT_NAME_COLLISION;
232 case NTCREATEX_DISP_OVERWRITE_IF:
233 case NTCREATEX_DISP_OVERWRITE:
234 case NTCREATEX_DISP_SUPERSEDE:
236 return NT_STATUS_INVALID_PARAMETER;
239 f = talloc(req, 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;
249 fnum = idr_get_new_above(pvfs->files.idtree, f, PVFS_MIN_DIR_FNUM, UINT16_MAX);
251 return NT_STATUS_TOO_MANY_OPENED_FILES;
255 /* check the security descriptor */
256 status = pvfs_access_check(pvfs, req, name, &access_mask);
258 status = pvfs_access_check_create(pvfs, req, name, &access_mask);
260 if (!NT_STATUS_IS_OK(status)) {
261 idr_remove(pvfs->files.idtree, fnum);
266 f->session_info = req->session_info;
267 f->smbpid = req->smbpid;
269 f->pending_list = NULL;
271 f->share_access = io->generic.in.share_access;
272 f->impersonation = io->generic.in.impersonation;
273 f->access_mask = access_mask;
274 f->notify_buffer = NULL;
276 f->handle->pvfs = pvfs;
277 f->handle->name = talloc_steal(f->handle, name);
279 f->handle->odb_locking_key = data_blob(NULL, 0);
280 f->handle->brl_locking_key = data_blob(NULL, 0);
281 f->handle->create_options = io->generic.in.create_options;
282 f->handle->seek_offset = 0;
283 f->handle->position = 0;
285 f->handle->sticky_write_time = False;
287 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
288 pvfs_directory_empty(pvfs, f->handle->name)) {
291 del_on_close = False;
296 /* form the lock context used for opendb locking */
297 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
298 if (!NT_STATUS_IS_OK(status)) {
299 idr_remove(pvfs->files.idtree, f->fnum);
303 /* get a lock on this file before the actual open */
304 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
306 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
308 /* we were supposed to do a blocking lock, so something
310 idr_remove(pvfs->files.idtree, fnum);
311 return NT_STATUS_INTERNAL_DB_CORRUPTION;
314 /* see if we are allowed to open at the same time as existing opens */
315 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
316 share_access, access_mask, del_on_close, name->full_name);
318 if (!NT_STATUS_IS_OK(status)) {
319 idr_remove(pvfs->files.idtree, f->fnum);
324 f->handle->have_opendb_entry = True;
327 DLIST_ADD(pvfs->files.list, f);
329 /* setup destructors to avoid leaks on abnormal termination */
330 talloc_set_destructor(f->handle, pvfs_dir_handle_destructor);
331 talloc_set_destructor(f, pvfs_dir_fnum_destructor);
334 uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
335 mode_t mode = pvfs_fileperms(pvfs, attrib);
337 if (mkdir(name->full_name, mode) == -1) {
338 idr_remove(pvfs->files.idtree, fnum);
339 return pvfs_map_errno(pvfs,errno);
342 pvfs_xattr_unlink_hook(pvfs, name->full_name);
344 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
345 if (!NT_STATUS_IS_OK(status)) {
349 status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, fnum, io);
350 if (!NT_STATUS_IS_OK(status)) {
354 /* form the lock context used for opendb locking */
355 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
356 if (!NT_STATUS_IS_OK(status)) {
357 idr_remove(pvfs->files.idtree, f->fnum);
361 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
363 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
365 /* we were supposed to do a blocking lock, so something
367 idr_remove(pvfs->files.idtree, fnum);
368 return NT_STATUS_INTERNAL_DB_CORRUPTION;
371 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
372 share_access, access_mask, del_on_close, name->full_name);
374 if (!NT_STATUS_IS_OK(status)) {
378 f->handle->have_opendb_entry = True;
380 create_action = NTCREATEX_ACTION_CREATED;
382 create_action = NTCREATEX_ACTION_EXISTED;
386 idr_remove(pvfs->files.idtree, fnum);
387 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
390 /* the open succeeded, keep this handle permanently */
391 talloc_steal(pvfs, f);
393 io->generic.out.oplock_level = OPLOCK_NONE;
394 io->generic.out.file.fnum = f->fnum;
395 io->generic.out.create_action = create_action;
396 io->generic.out.create_time = name->dos.create_time;
397 io->generic.out.access_time = name->dos.access_time;
398 io->generic.out.write_time = name->dos.write_time;
399 io->generic.out.change_time = name->dos.change_time;
400 io->generic.out.attrib = name->dos.attrib;
401 io->generic.out.alloc_size = name->dos.alloc_size;
402 io->generic.out.size = name->st.st_size;
403 io->generic.out.file_type = FILE_TYPE_DISK;
404 io->generic.out.ipc_state = 0;
405 io->generic.out.is_directory = 1;
410 idr_remove(pvfs->files.idtree, fnum);
411 rmdir(name->full_name);
416 destroy a struct pvfs_file_handle
418 static int pvfs_handle_destructor(void *p)
420 struct pvfs_file_handle *h = p;
424 /* the write time is no longer sticky */
425 if (h->sticky_write_time) {
427 status = pvfs_dosattrib_load(h->pvfs, h->name, h->fd);
428 if (NT_STATUS_IS_OK(status)) {
429 h->name->dos.flags &= ~XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME;
430 pvfs_dosattrib_save(h->pvfs, h->name, h->fd);
434 if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
435 h->name->stream_name) {
437 status = pvfs_stream_delete(h->pvfs, h->name, h->fd);
438 if (!NT_STATUS_IS_OK(status)) {
439 DEBUG(0,("Failed to delete stream '%s' on close of '%s'\n",
440 h->name->stream_name, h->name->full_name));
445 if (close(h->fd) != 0) {
446 DEBUG(0,("pvfs_handle_destructor: close(%d) failed for %s - %s\n",
447 h->fd, h->name->full_name, strerror(errno)));
452 if (h->name->stream_name == NULL &&
453 pvfs_delete_on_close_set(h->pvfs, h, &open_count, &path) &&
456 status = pvfs_xattr_unlink_hook(h->pvfs, path);
457 if (!NT_STATUS_IS_OK(status)) {
458 DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
459 path, nt_errstr(status)));
461 if (unlink(path) != 0) {
462 DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n",
463 path, strerror(errno)));
467 if (h->have_opendb_entry) {
468 struct odb_lock *lck;
471 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
473 DEBUG(0,("Unable to lock opendb for close\n"));
477 status = odb_close_file(lck, h);
478 if (!NT_STATUS_IS_OK(status)) {
479 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
480 h->name->full_name, nt_errstr(status)));
491 destroy a struct pvfs_file
493 static int pvfs_fnum_destructor(void *p)
495 struct pvfs_file *f = p;
497 DLIST_REMOVE(f->pvfs->files.list, f);
498 pvfs_lock_close(f->pvfs, f);
499 idr_remove(f->pvfs->files.idtree, f->fnum);
506 form the lock context used for byte range locking. This is separate
507 from the locking key used for opendb locking as it needs to take
508 account of file streams (each stream is a separate byte range
511 static NTSTATUS pvfs_brl_locking_key(struct pvfs_filename *name,
512 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
516 status = pvfs_locking_key(name, mem_ctx, &odb_key);
517 if (!NT_STATUS_IS_OK(status)) {
520 if (name->stream_name == NULL) {
524 *key = data_blob_talloc(mem_ctx, NULL,
525 odb_key.length + strlen(name->stream_name) + 1);
526 if (key->data == NULL) {
527 return NT_STATUS_NO_MEMORY;
529 memcpy(key->data, odb_key.data, odb_key.length);
530 memcpy(key->data + odb_key.length,
531 name->stream_name, strlen(name->stream_name)+1);
532 data_blob_free(&odb_key);
540 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
541 struct ntvfs_request *req,
542 struct pvfs_filename *name,
548 struct odb_lock *lck;
549 uint32_t create_options = io->generic.in.create_options;
550 uint32_t share_access = io->generic.in.share_access;
551 uint32_t access_mask = io->generic.in.access_mask;
555 struct pvfs_filename *parent;
557 if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
558 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
559 return NT_STATUS_CANNOT_DELETE;
562 status = pvfs_access_check_create(pvfs, req, name, &access_mask);
563 NT_STATUS_NOT_OK_RETURN(status);
565 /* check that the parent isn't opened with delete on close set */
566 status = pvfs_resolve_parent(pvfs, req, name, &parent);
567 if (NT_STATUS_IS_OK(status)) {
568 DATA_BLOB locking_key;
569 status = pvfs_locking_key(parent, req, &locking_key);
570 NT_STATUS_NOT_OK_RETURN(status);
571 status = odb_get_delete_on_close(pvfs->odb_context, &locking_key,
572 &del_on_close, NULL, NULL);
573 NT_STATUS_NOT_OK_RETURN(status);
575 return NT_STATUS_DELETE_PENDING;
579 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
585 f = talloc(req, struct pvfs_file);
587 return NT_STATUS_NO_MEMORY;
590 f->handle = talloc(f, struct pvfs_file_handle);
591 if (f->handle == NULL) {
592 return NT_STATUS_NO_MEMORY;
595 fnum = idr_get_new_above(pvfs->files.idtree, f, PVFS_MIN_NEW_FNUM, UINT16_MAX);
597 return NT_STATUS_TOO_MANY_OPENED_FILES;
600 attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
601 mode = pvfs_fileperms(pvfs, attrib);
603 /* create the file */
604 fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
606 idr_remove(pvfs->files.idtree, fnum);
607 return pvfs_map_errno(pvfs, errno);
610 pvfs_xattr_unlink_hook(pvfs, name->full_name);
612 /* if this was a stream create then create the stream as well */
613 if (name->stream_name) {
614 status = pvfs_stream_create(pvfs, name, fd);
615 if (!NT_STATUS_IS_OK(status)) {
616 idr_remove(pvfs->files.idtree, fnum);
622 /* re-resolve the open fd */
623 status = pvfs_resolve_name_fd(pvfs, fd, name);
624 if (!NT_STATUS_IS_OK(status)) {
625 idr_remove(pvfs->files.idtree, fnum);
630 name->dos.attrib = attrib;
631 status = pvfs_dosattrib_save(pvfs, name, fd);
632 if (!NT_STATUS_IS_OK(status)) {
637 status = pvfs_open_setup_eas_acl(pvfs, req, name, fd, fnum, io);
638 if (!NT_STATUS_IS_OK(status)) {
642 /* form the lock context used for byte range locking and
644 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
645 if (!NT_STATUS_IS_OK(status)) {
649 status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
650 if (!NT_STATUS_IS_OK(status)) {
654 /* grab a lock on the open file record */
655 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
657 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
659 /* we were supposed to do a blocking lock, so something
661 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
665 if (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
668 del_on_close = False;
671 status = odb_open_file(lck, f->handle, name->stream_id,
672 share_access, access_mask, del_on_close, name->full_name);
674 if (!NT_STATUS_IS_OK(status)) {
675 /* bad news, we must have hit a race - we don't delete the file
676 here as the most likely scenario is that someone else created
677 the file at the same time */
678 idr_remove(pvfs->files.idtree, fnum);
684 f->session_info = req->session_info;
685 f->smbpid = req->smbpid;
687 f->pending_list = NULL;
689 f->share_access = io->generic.in.share_access;
690 f->access_mask = access_mask;
691 f->impersonation = io->generic.in.impersonation;
692 f->notify_buffer = NULL;
694 f->handle->pvfs = pvfs;
695 f->handle->name = talloc_steal(f->handle, name);
697 f->handle->create_options = io->generic.in.create_options;
698 f->handle->seek_offset = 0;
699 f->handle->position = 0;
701 f->handle->have_opendb_entry = True;
702 f->handle->sticky_write_time = False;
704 DLIST_ADD(pvfs->files.list, f);
706 /* setup a destructor to avoid file descriptor leaks on
707 abnormal termination */
708 talloc_set_destructor(f, pvfs_fnum_destructor);
709 talloc_set_destructor(f->handle, pvfs_handle_destructor);
712 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
713 io->generic.out.oplock_level = OPLOCK_EXCLUSIVE;
715 io->generic.out.oplock_level = OPLOCK_NONE;
717 io->generic.out.file.fnum = f->fnum;
718 io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
719 io->generic.out.create_time = name->dos.create_time;
720 io->generic.out.access_time = name->dos.access_time;
721 io->generic.out.write_time = name->dos.write_time;
722 io->generic.out.change_time = name->dos.change_time;
723 io->generic.out.attrib = name->dos.attrib;
724 io->generic.out.alloc_size = name->dos.alloc_size;
725 io->generic.out.size = name->st.st_size;
726 io->generic.out.file_type = FILE_TYPE_DISK;
727 io->generic.out.ipc_state = 0;
728 io->generic.out.is_directory = 0;
730 /* success - keep the file handle */
731 talloc_steal(pvfs, f);
736 idr_remove(pvfs->files.idtree, fnum);
738 unlink(name->full_name);
744 state of a pending open retry
746 struct pvfs_open_retry {
747 struct ntvfs_module_context *ntvfs;
748 struct ntvfs_request *req;
751 DATA_BLOB odb_locking_key;
754 /* destroy a pending open request */
755 static int pvfs_retry_destructor(void *ptr)
757 struct pvfs_open_retry *r = ptr;
758 struct pvfs_state *pvfs = r->ntvfs->private_data;
759 if (r->odb_locking_key.data) {
760 struct odb_lock *lck;
761 lck = odb_lock(r->req, pvfs->odb_context, &r->odb_locking_key);
763 odb_remove_pending(lck, r);
773 static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
775 struct pvfs_open_retry *r = private;
776 struct ntvfs_module_context *ntvfs = r->ntvfs;
777 struct ntvfs_request *req = r->req;
778 union smb_open *io = r->io;
781 /* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
782 just a bug in their server, but we better do the same */
783 if (reason == PVFS_WAIT_CANCEL) {
787 talloc_free(r->wait_handle);
789 if (reason == PVFS_WAIT_TIMEOUT) {
790 /* if it timed out, then give the failure
793 req->async_states->status = NT_STATUS_SHARING_VIOLATION;
794 req->async_states->send_fn(req);
798 /* the pending odb entry is already removed. We use a null locking
799 key to indicate this */
800 data_blob_free(&r->odb_locking_key);
803 /* try the open again, which could trigger another retry setup
804 if it wants to, so we have to unmark the async flag so we
805 will know if it does a second async reply */
806 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
808 status = pvfs_open(ntvfs, req, io);
809 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
810 /* the 2nd try also replied async, so we don't send
815 /* re-mark it async, just in case someone up the chain does
817 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
819 /* send the reply up the chain */
820 req->async_states->status = status;
821 req->async_states->send_fn(req);
826 special handling for openx DENY_DOS semantics
828 This function attempts a reference open using an existing handle. If its allowed,
829 then it returns NT_STATUS_OK, otherwise it returns any other code and normal
830 open processing continues.
832 static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
833 struct ntvfs_request *req, union smb_open *io,
834 struct pvfs_file *f, struct odb_lock *lck)
836 struct pvfs_state *pvfs = ntvfs->private_data;
837 struct pvfs_file *f2;
838 struct pvfs_filename *name;
840 /* search for an existing open with the right parameters. Note
841 the magic ntcreatex options flag, which is set in the
842 generic mapping code. This might look ugly, but its
843 actually pretty much now w2k does it internally as well.
845 If you look at the BASE-DENYDOS test you will see that a
846 DENY_DOS is a very special case, and in the right
847 circumstances you actually get the _same_ handle back
848 twice, rather than a new handle.
850 for (f2=pvfs->files.list;f2;f2=f2->next) {
852 f2->session_info == req->session_info &&
853 f2->smbpid == req->smbpid &&
854 (f2->handle->create_options &
855 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
856 NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) &&
857 (f2->access_mask & SEC_FILE_WRITE_DATA) &&
858 strcasecmp_m(f2->handle->name->original_name,
859 io->generic.in.fname)==0) {
865 return NT_STATUS_SHARING_VIOLATION;
868 /* quite an insane set of semantics ... */
869 if (is_exe_filename(io->generic.in.fname) &&
870 (f2->handle->create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) {
871 return NT_STATUS_SHARING_VIOLATION;
875 setup a reference to the existing handle
877 talloc_free(f->handle);
878 f->handle = talloc_reference(f, f2->handle);
882 name = f->handle->name;
884 io->generic.out.oplock_level = OPLOCK_NONE;
885 io->generic.out.file.fnum = f->fnum;
886 io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
887 io->generic.out.create_time = name->dos.create_time;
888 io->generic.out.access_time = name->dos.access_time;
889 io->generic.out.write_time = name->dos.write_time;
890 io->generic.out.change_time = name->dos.change_time;
891 io->generic.out.attrib = name->dos.attrib;
892 io->generic.out.alloc_size = name->dos.alloc_size;
893 io->generic.out.size = name->st.st_size;
894 io->generic.out.file_type = FILE_TYPE_DISK;
895 io->generic.out.ipc_state = 0;
896 io->generic.out.is_directory = 0;
898 talloc_steal(f->pvfs, f);
906 setup for a open retry after a sharing violation
908 static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
909 struct ntvfs_request *req,
912 struct odb_lock *lck)
914 struct pvfs_state *pvfs = ntvfs->private_data;
915 struct pvfs_open_retry *r;
917 struct timeval end_time;
919 if (io->generic.in.create_options &
920 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
921 /* see if we can satisfy the request using the special DENY_DOS
923 status = pvfs_open_deny_dos(ntvfs, req, io, f, lck);
924 if (NT_STATUS_IS_OK(status)) {
929 r = talloc(req, struct pvfs_open_retry);
931 return NT_STATUS_NO_MEMORY;
937 r->odb_locking_key = data_blob_talloc(r,
938 f->handle->odb_locking_key.data,
939 f->handle->odb_locking_key.length);
941 end_time = timeval_add(&req->statistics.request_time, 0, pvfs->sharing_violation_delay);
943 /* setup a pending lock */
944 status = odb_open_file_pending(lck, r);
945 if (!NT_STATUS_IS_OK(status)) {
952 talloc_set_destructor(r, pvfs_retry_destructor);
954 r->wait_handle = pvfs_wait_message(pvfs, req, MSG_PVFS_RETRY_OPEN, end_time,
956 if (r->wait_handle == NULL) {
957 return NT_STATUS_NO_MEMORY;
960 talloc_steal(pvfs, r);
968 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
969 struct ntvfs_request *req, union smb_open *io)
971 struct pvfs_state *pvfs = ntvfs->private_data;
973 struct pvfs_filename *name;
977 struct odb_lock *lck;
978 uint32_t create_options;
979 uint32_t share_access;
980 uint32_t access_mask;
981 BOOL stream_existed, stream_truncate=False;
983 /* use the generic mapping code to avoid implementing all the
984 different open calls. */
985 if (io->generic.level != RAW_OPEN_GENERIC &&
986 io->generic.level != RAW_OPEN_NTTRANS_CREATE) {
987 return ntvfs_map_open(ntvfs, req, io);
990 /* resolve the cifs name to a posix name */
991 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
992 PVFS_RESOLVE_STREAMS, &name);
993 if (!NT_STATUS_IS_OK(status)) {
997 /* directory opens are handled separately */
998 if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
999 (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
1000 return pvfs_open_directory(pvfs, req, name, io);
1003 /* FILE_ATTRIBUTE_DIRECTORY is ignored if the above test for directory
1004 open doesn't match */
1005 io->generic.in.file_attr &= ~FILE_ATTRIBUTE_DIRECTORY;
1007 create_options = io->generic.in.create_options;
1008 share_access = io->generic.in.share_access;
1009 access_mask = io->generic.in.access_mask;
1011 /* certain create options are not allowed */
1012 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
1013 !(access_mask & SEC_STD_DELETE)) {
1014 return NT_STATUS_INVALID_PARAMETER;
1019 switch (io->generic.in.open_disposition) {
1020 case NTCREATEX_DISP_SUPERSEDE:
1021 case NTCREATEX_DISP_OVERWRITE_IF:
1022 if (name->stream_name == NULL) {
1025 stream_truncate = True;
1029 case NTCREATEX_DISP_OPEN:
1030 if (!name->stream_exists) {
1031 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1036 case NTCREATEX_DISP_OVERWRITE:
1037 if (!name->stream_exists) {
1038 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1040 if (name->stream_name == NULL) {
1043 stream_truncate = True;
1047 case NTCREATEX_DISP_CREATE:
1048 if (name->stream_exists) {
1049 return NT_STATUS_OBJECT_NAME_COLLISION;
1054 case NTCREATEX_DISP_OPEN_IF:
1059 return NT_STATUS_INVALID_PARAMETER;
1062 /* handle creating a new file separately */
1063 if (!name->exists) {
1064 status = pvfs_create_file(pvfs, req, name, io);
1065 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
1069 /* we've hit a race - the file was created during this call */
1070 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
1074 /* try re-resolving the name */
1075 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
1076 if (!NT_STATUS_IS_OK(status)) {
1079 /* fall through to a normal open */
1082 if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
1083 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
1084 return NT_STATUS_CANNOT_DELETE;
1087 /* check the security descriptor */
1088 status = pvfs_access_check(pvfs, req, name, &access_mask);
1089 if (!NT_STATUS_IS_OK(status)) {
1093 f = talloc(req, struct pvfs_file);
1095 return NT_STATUS_NO_MEMORY;
1098 f->handle = talloc(f, struct pvfs_file_handle);
1099 if (f->handle == NULL) {
1100 return NT_STATUS_NO_MEMORY;
1103 /* allocate a fnum */
1104 fnum = idr_get_new_above(pvfs->files.idtree, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
1106 return NT_STATUS_TOO_MANY_OPENED_FILES;
1110 f->session_info = req->session_info;
1111 f->smbpid = req->smbpid;
1113 f->pending_list = NULL;
1115 f->share_access = io->generic.in.share_access;
1116 f->access_mask = access_mask;
1117 f->impersonation = io->generic.in.impersonation;
1118 f->notify_buffer = NULL;
1120 f->handle->pvfs = pvfs;
1122 f->handle->name = talloc_steal(f->handle, name);
1123 f->handle->create_options = io->generic.in.create_options;
1124 f->handle->seek_offset = 0;
1125 f->handle->position = 0;
1126 f->handle->mode = 0;
1127 f->handle->have_opendb_entry = False;
1128 f->handle->sticky_write_time = False;
1130 /* form the lock context used for byte range locking and
1132 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
1133 if (!NT_STATUS_IS_OK(status)) {
1134 idr_remove(pvfs->files.idtree, f->fnum);
1138 status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
1139 if (!NT_STATUS_IS_OK(status)) {
1140 idr_remove(pvfs->files.idtree, f->fnum);
1144 /* get a lock on this file before the actual open */
1145 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1147 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
1149 /* we were supposed to do a blocking lock, so something
1151 idr_remove(pvfs->files.idtree, fnum);
1152 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1155 DLIST_ADD(pvfs->files.list, f);
1157 /* setup a destructor to avoid file descriptor leaks on
1158 abnormal termination */
1159 talloc_set_destructor(f, pvfs_fnum_destructor);
1160 talloc_set_destructor(f->handle, pvfs_handle_destructor);
1162 /* see if we are allowed to open at the same time as existing opens */
1163 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
1164 share_access, access_mask, False, name->full_name);
1166 /* on a sharing violation we need to retry when the file is closed by
1167 the other user, or after 1 second */
1168 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) &&
1169 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
1170 return pvfs_open_setup_retry(ntvfs, req, io, f, lck);
1173 if (!NT_STATUS_IS_OK(status)) {
1178 f->handle->have_opendb_entry = True;
1180 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
1186 /* do the actual open */
1187 fd = open(f->handle->name->full_name, flags);
1190 return pvfs_map_errno(f->pvfs, errno);
1195 stream_existed = name->stream_exists;
1197 /* if this was a stream create then create the stream as well */
1198 if (!name->stream_exists) {
1199 status = pvfs_stream_create(pvfs, f->handle->name, fd);
1200 if (!NT_STATUS_IS_OK(status)) {
1204 if (stream_truncate) {
1205 status = pvfs_stream_truncate(pvfs, f->handle->name, fd, 0);
1206 if (!NT_STATUS_IS_OK(status)) {
1213 /* re-resolve the open fd */
1214 status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name);
1215 if (!NT_STATUS_IS_OK(status)) {
1220 if (f->handle->name->stream_id == 0 &&
1221 (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE ||
1222 io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) {
1223 /* for overwrite we need to replace file permissions */
1224 uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
1225 mode_t mode = pvfs_fileperms(pvfs, attrib);
1226 if (fchmod(fd, mode) == -1) {
1228 return pvfs_map_errno(pvfs, errno);
1230 name->dos.attrib = attrib;
1231 status = pvfs_dosattrib_save(pvfs, name, fd);
1232 if (!NT_STATUS_IS_OK(status)) {
1240 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
1241 io->generic.out.oplock_level = OPLOCK_EXCLUSIVE;
1243 io->generic.out.oplock_level = OPLOCK_NONE;
1245 io->generic.out.file.fnum = f->fnum;
1246 io->generic.out.create_action = stream_existed?
1247 NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED;
1248 io->generic.out.create_time = name->dos.create_time;
1249 io->generic.out.access_time = name->dos.access_time;
1250 io->generic.out.write_time = name->dos.write_time;
1251 io->generic.out.change_time = name->dos.change_time;
1252 io->generic.out.attrib = name->dos.attrib;
1253 io->generic.out.alloc_size = name->dos.alloc_size;
1254 io->generic.out.size = name->st.st_size;
1255 io->generic.out.file_type = FILE_TYPE_DISK;
1256 io->generic.out.ipc_state = 0;
1257 io->generic.out.is_directory = 0;
1259 /* success - keep the file handle */
1260 talloc_steal(f->pvfs, f);
1262 return NT_STATUS_OK;
1269 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
1270 struct ntvfs_request *req, union smb_close *io)
1272 struct pvfs_state *pvfs = ntvfs->private_data;
1273 struct pvfs_file *f;
1274 struct utimbuf unix_times;
1276 if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
1277 return NT_STATUS_DOS(ERRSRV, ERRerror);
1280 if (io->generic.level != RAW_CLOSE_CLOSE) {
1281 return ntvfs_map_close(ntvfs, req, io);
1284 f = pvfs_find_fd(pvfs, req, io->close.in.file.fnum);
1286 return NT_STATUS_INVALID_HANDLE;
1289 if (!null_time(io->close.in.write_time)) {
1290 unix_times.actime = 0;
1291 unix_times.modtime = io->close.in.write_time;
1292 utime(f->handle->name->full_name, &unix_times);
1293 } else if (f->handle->sticky_write_time) {
1294 unix_times.actime = 0;
1295 unix_times.modtime = nt_time_to_unix(f->handle->name->dos.write_time);
1296 utime(f->handle->name->full_name, &unix_times);
1301 return NT_STATUS_OK;
1306 logoff - close all file descriptors open by a vuid
1308 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
1309 struct ntvfs_request *req)
1311 struct pvfs_state *pvfs = ntvfs->private_data;
1312 struct pvfs_file *f, *next;
1314 for (f=pvfs->files.list;f;f=next) {
1316 if (f->session_info == req->session_info) {
1321 return NT_STATUS_OK;
1326 exit - close files for the current pid
1328 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
1329 struct ntvfs_request *req)
1331 struct pvfs_state *pvfs = ntvfs->private_data;
1332 struct pvfs_file *f, *next;
1334 for (f=pvfs->files.list;f;f=next) {
1336 if (f->session_info == req->session_info &&
1337 f->smbpid == req->smbpid) {
1342 return NT_STATUS_OK;
1347 change the delete on close flag on an already open file
1349 NTSTATUS pvfs_set_delete_on_close(struct pvfs_state *pvfs,
1350 struct ntvfs_request *req,
1351 struct pvfs_file *f, BOOL del_on_close)
1353 struct odb_lock *lck;
1356 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) && del_on_close) {
1357 return NT_STATUS_CANNOT_DELETE;
1360 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
1361 !pvfs_directory_empty(pvfs, f->handle->name)) {
1362 return NT_STATUS_DIRECTORY_NOT_EMPTY;
1366 f->handle->create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1368 f->handle->create_options &= ~NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1371 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1373 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1376 status = odb_set_delete_on_close(lck, del_on_close);
1385 determine if a file can be deleted, or if it is prevented by an
1388 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs,
1389 struct ntvfs_request *req,
1390 struct pvfs_filename *name,
1391 struct odb_lock **lckp)
1395 struct odb_lock *lck;
1397 status = pvfs_locking_key(name, name, &key);
1398 if (!NT_STATUS_IS_OK(status)) {
1399 return NT_STATUS_NO_MEMORY;
1402 lck = odb_lock(req, pvfs->odb_context, &key);
1404 DEBUG(0,("Unable to lock opendb for can_delete\n"));
1405 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1408 status = odb_can_open(lck,
1409 NTCREATEX_SHARE_ACCESS_READ |
1410 NTCREATEX_SHARE_ACCESS_WRITE |
1411 NTCREATEX_SHARE_ACCESS_DELETE,
1412 NTCREATEX_OPTIONS_DELETE_ON_CLOSE,
1415 if (NT_STATUS_IS_OK(status)) {
1416 status = pvfs_access_check_simple(pvfs, req, name, SEC_STD_DELETE);
1419 if (!NT_STATUS_IS_OK(status)) {
1422 } else if (lckp != NULL) {
1430 determine if a file can be renamed, or if it is prevented by an
1433 NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs,
1434 struct ntvfs_request *req,
1435 struct pvfs_filename *name,
1436 struct odb_lock **lckp)
1440 struct odb_lock *lck;
1442 status = pvfs_locking_key(name, name, &key);
1443 if (!NT_STATUS_IS_OK(status)) {
1444 return NT_STATUS_NO_MEMORY;
1447 lck = odb_lock(req, pvfs->odb_context, &key);
1449 DEBUG(0,("Unable to lock opendb for can_stat\n"));
1450 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1453 status = odb_can_open(lck,
1454 NTCREATEX_SHARE_ACCESS_READ |
1455 NTCREATEX_SHARE_ACCESS_WRITE,
1459 if (!NT_STATUS_IS_OK(status)) {
1462 } else if (lckp != NULL) {
1470 determine if file meta data can be accessed, or if it is prevented by an
1473 NTSTATUS pvfs_can_stat(struct pvfs_state *pvfs,
1474 struct ntvfs_request *req,
1475 struct pvfs_filename *name)
1479 struct odb_lock *lck;
1481 status = pvfs_locking_key(name, name, &key);
1482 if (!NT_STATUS_IS_OK(status)) {
1483 return NT_STATUS_NO_MEMORY;
1486 lck = odb_lock(req, pvfs->odb_context, &key);
1488 DEBUG(0,("Unable to lock opendb for can_stat\n"));
1489 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1492 status = odb_can_open(lck,
1493 NTCREATEX_SHARE_ACCESS_READ |
1494 NTCREATEX_SHARE_ACCESS_WRITE,
1502 determine if delete on close is set on
1504 BOOL pvfs_delete_on_close_set(struct pvfs_state *pvfs, struct pvfs_file_handle *h,
1505 int *open_count, char **path)
1510 status = odb_get_delete_on_close(pvfs->odb_context, &h->odb_locking_key,
1511 &del_on_close, open_count, path);
1512 if (!NT_STATUS_IS_OK(status)) {
1513 DEBUG(1,("WARNING: unable to determine delete on close status for open file\n"));
1517 return del_on_close;