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_BATCH;
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);
733 notify_trigger(pvfs->notify_context, NOTIFY_ACTION_ADDED, name->full_name);
738 idr_remove(pvfs->files.idtree, fnum);
740 unlink(name->full_name);
746 state of a pending open retry
748 struct pvfs_open_retry {
749 struct ntvfs_module_context *ntvfs;
750 struct ntvfs_request *req;
753 DATA_BLOB odb_locking_key;
756 /* destroy a pending open request */
757 static int pvfs_retry_destructor(void *ptr)
759 struct pvfs_open_retry *r = ptr;
760 struct pvfs_state *pvfs = r->ntvfs->private_data;
761 if (r->odb_locking_key.data) {
762 struct odb_lock *lck;
763 lck = odb_lock(r->req, pvfs->odb_context, &r->odb_locking_key);
765 odb_remove_pending(lck, r);
775 static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
777 struct pvfs_open_retry *r = private;
778 struct ntvfs_module_context *ntvfs = r->ntvfs;
779 struct ntvfs_request *req = r->req;
780 union smb_open *io = r->io;
783 /* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
784 just a bug in their server, but we better do the same */
785 if (reason == PVFS_WAIT_CANCEL) {
789 talloc_free(r->wait_handle);
791 if (reason == PVFS_WAIT_TIMEOUT) {
792 /* if it timed out, then give the failure
795 req->async_states->status = NT_STATUS_SHARING_VIOLATION;
796 req->async_states->send_fn(req);
800 /* the pending odb entry is already removed. We use a null locking
801 key to indicate this */
802 data_blob_free(&r->odb_locking_key);
805 /* try the open again, which could trigger another retry setup
806 if it wants to, so we have to unmark the async flag so we
807 will know if it does a second async reply */
808 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
810 status = pvfs_open(ntvfs, req, io);
811 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
812 /* the 2nd try also replied async, so we don't send
817 /* re-mark it async, just in case someone up the chain does
819 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
821 /* send the reply up the chain */
822 req->async_states->status = status;
823 req->async_states->send_fn(req);
828 special handling for openx DENY_DOS semantics
830 This function attempts a reference open using an existing handle. If its allowed,
831 then it returns NT_STATUS_OK, otherwise it returns any other code and normal
832 open processing continues.
834 static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
835 struct ntvfs_request *req, union smb_open *io,
836 struct pvfs_file *f, struct odb_lock *lck)
838 struct pvfs_state *pvfs = ntvfs->private_data;
839 struct pvfs_file *f2;
840 struct pvfs_filename *name;
842 /* search for an existing open with the right parameters. Note
843 the magic ntcreatex options flag, which is set in the
844 generic mapping code. This might look ugly, but its
845 actually pretty much now w2k does it internally as well.
847 If you look at the BASE-DENYDOS test you will see that a
848 DENY_DOS is a very special case, and in the right
849 circumstances you actually get the _same_ handle back
850 twice, rather than a new handle.
852 for (f2=pvfs->files.list;f2;f2=f2->next) {
854 f2->session_info == req->session_info &&
855 f2->smbpid == req->smbpid &&
856 (f2->handle->create_options &
857 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
858 NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) &&
859 (f2->access_mask & SEC_FILE_WRITE_DATA) &&
860 strcasecmp_m(f2->handle->name->original_name,
861 io->generic.in.fname)==0) {
867 return NT_STATUS_SHARING_VIOLATION;
870 /* quite an insane set of semantics ... */
871 if (is_exe_filename(io->generic.in.fname) &&
872 (f2->handle->create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) {
873 return NT_STATUS_SHARING_VIOLATION;
877 setup a reference to the existing handle
879 talloc_free(f->handle);
880 f->handle = talloc_reference(f, f2->handle);
884 name = f->handle->name;
886 io->generic.out.oplock_level = OPLOCK_NONE;
887 io->generic.out.file.fnum = f->fnum;
888 io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
889 io->generic.out.create_time = name->dos.create_time;
890 io->generic.out.access_time = name->dos.access_time;
891 io->generic.out.write_time = name->dos.write_time;
892 io->generic.out.change_time = name->dos.change_time;
893 io->generic.out.attrib = name->dos.attrib;
894 io->generic.out.alloc_size = name->dos.alloc_size;
895 io->generic.out.size = name->st.st_size;
896 io->generic.out.file_type = FILE_TYPE_DISK;
897 io->generic.out.ipc_state = 0;
898 io->generic.out.is_directory = 0;
900 talloc_steal(f->pvfs, f);
908 setup for a open retry after a sharing violation
910 static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
911 struct ntvfs_request *req,
914 struct odb_lock *lck)
916 struct pvfs_state *pvfs = ntvfs->private_data;
917 struct pvfs_open_retry *r;
919 struct timeval end_time;
921 if (io->generic.in.create_options &
922 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
923 /* see if we can satisfy the request using the special DENY_DOS
925 status = pvfs_open_deny_dos(ntvfs, req, io, f, lck);
926 if (NT_STATUS_IS_OK(status)) {
931 r = talloc(req, struct pvfs_open_retry);
933 return NT_STATUS_NO_MEMORY;
939 r->odb_locking_key = data_blob_talloc(r,
940 f->handle->odb_locking_key.data,
941 f->handle->odb_locking_key.length);
943 end_time = timeval_add(&req->statistics.request_time, 0, pvfs->sharing_violation_delay);
945 /* setup a pending lock */
946 status = odb_open_file_pending(lck, r);
947 if (!NT_STATUS_IS_OK(status)) {
954 talloc_set_destructor(r, pvfs_retry_destructor);
956 r->wait_handle = pvfs_wait_message(pvfs, req, MSG_PVFS_RETRY_OPEN, end_time,
958 if (r->wait_handle == NULL) {
959 return NT_STATUS_NO_MEMORY;
962 talloc_steal(pvfs, r);
970 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
971 struct ntvfs_request *req, union smb_open *io)
973 struct pvfs_state *pvfs = ntvfs->private_data;
975 struct pvfs_filename *name;
979 struct odb_lock *lck;
980 uint32_t create_options;
981 uint32_t share_access;
982 uint32_t access_mask;
983 BOOL stream_existed, stream_truncate=False;
985 /* use the generic mapping code to avoid implementing all the
986 different open calls. */
987 if (io->generic.level != RAW_OPEN_GENERIC &&
988 io->generic.level != RAW_OPEN_NTTRANS_CREATE) {
989 return ntvfs_map_open(ntvfs, req, io);
992 /* resolve the cifs name to a posix name */
993 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
994 PVFS_RESOLVE_STREAMS, &name);
995 if (!NT_STATUS_IS_OK(status)) {
999 /* directory opens are handled separately */
1000 if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
1001 (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
1002 return pvfs_open_directory(pvfs, req, name, io);
1005 /* FILE_ATTRIBUTE_DIRECTORY is ignored if the above test for directory
1006 open doesn't match */
1007 io->generic.in.file_attr &= ~FILE_ATTRIBUTE_DIRECTORY;
1009 create_options = io->generic.in.create_options;
1010 share_access = io->generic.in.share_access;
1011 access_mask = io->generic.in.access_mask;
1013 /* certain create options are not allowed */
1014 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
1015 !(access_mask & SEC_STD_DELETE)) {
1016 return NT_STATUS_INVALID_PARAMETER;
1021 switch (io->generic.in.open_disposition) {
1022 case NTCREATEX_DISP_SUPERSEDE:
1023 case NTCREATEX_DISP_OVERWRITE_IF:
1024 if (name->stream_name == NULL) {
1027 stream_truncate = True;
1031 case NTCREATEX_DISP_OPEN:
1032 if (!name->stream_exists) {
1033 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1038 case NTCREATEX_DISP_OVERWRITE:
1039 if (!name->stream_exists) {
1040 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1042 if (name->stream_name == NULL) {
1045 stream_truncate = True;
1049 case NTCREATEX_DISP_CREATE:
1050 if (name->stream_exists) {
1051 return NT_STATUS_OBJECT_NAME_COLLISION;
1056 case NTCREATEX_DISP_OPEN_IF:
1061 return NT_STATUS_INVALID_PARAMETER;
1064 /* handle creating a new file separately */
1065 if (!name->exists) {
1066 status = pvfs_create_file(pvfs, req, name, io);
1067 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
1071 /* we've hit a race - the file was created during this call */
1072 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
1076 /* try re-resolving the name */
1077 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
1078 if (!NT_STATUS_IS_OK(status)) {
1081 /* fall through to a normal open */
1084 if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
1085 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
1086 return NT_STATUS_CANNOT_DELETE;
1089 /* check the security descriptor */
1090 status = pvfs_access_check(pvfs, req, name, &access_mask);
1091 if (!NT_STATUS_IS_OK(status)) {
1095 f = talloc(req, struct pvfs_file);
1097 return NT_STATUS_NO_MEMORY;
1100 f->handle = talloc(f, struct pvfs_file_handle);
1101 if (f->handle == NULL) {
1102 return NT_STATUS_NO_MEMORY;
1105 /* allocate a fnum */
1106 fnum = idr_get_new_above(pvfs->files.idtree, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
1108 return NT_STATUS_TOO_MANY_OPENED_FILES;
1112 f->session_info = req->session_info;
1113 f->smbpid = req->smbpid;
1115 f->pending_list = NULL;
1117 f->share_access = io->generic.in.share_access;
1118 f->access_mask = access_mask;
1119 f->impersonation = io->generic.in.impersonation;
1120 f->notify_buffer = NULL;
1122 f->handle->pvfs = pvfs;
1124 f->handle->name = talloc_steal(f->handle, name);
1125 f->handle->create_options = io->generic.in.create_options;
1126 f->handle->seek_offset = 0;
1127 f->handle->position = 0;
1128 f->handle->mode = 0;
1129 f->handle->have_opendb_entry = False;
1130 f->handle->sticky_write_time = False;
1132 /* form the lock context used for byte range locking and
1134 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
1135 if (!NT_STATUS_IS_OK(status)) {
1136 idr_remove(pvfs->files.idtree, f->fnum);
1140 status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
1141 if (!NT_STATUS_IS_OK(status)) {
1142 idr_remove(pvfs->files.idtree, f->fnum);
1146 /* get a lock on this file before the actual open */
1147 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1149 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
1151 /* we were supposed to do a blocking lock, so something
1153 idr_remove(pvfs->files.idtree, fnum);
1154 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1157 DLIST_ADD(pvfs->files.list, f);
1159 /* setup a destructor to avoid file descriptor leaks on
1160 abnormal termination */
1161 talloc_set_destructor(f, pvfs_fnum_destructor);
1162 talloc_set_destructor(f->handle, pvfs_handle_destructor);
1164 /* see if we are allowed to open at the same time as existing opens */
1165 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
1166 share_access, access_mask, False, name->full_name);
1168 /* on a sharing violation we need to retry when the file is closed by
1169 the other user, or after 1 second */
1170 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) &&
1171 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
1172 return pvfs_open_setup_retry(ntvfs, req, io, f, lck);
1175 if (!NT_STATUS_IS_OK(status)) {
1180 f->handle->have_opendb_entry = True;
1182 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
1188 /* do the actual open */
1189 fd = open(f->handle->name->full_name, flags);
1192 return pvfs_map_errno(f->pvfs, errno);
1197 stream_existed = name->stream_exists;
1199 /* if this was a stream create then create the stream as well */
1200 if (!name->stream_exists) {
1201 status = pvfs_stream_create(pvfs, f->handle->name, fd);
1202 if (!NT_STATUS_IS_OK(status)) {
1206 if (stream_truncate) {
1207 status = pvfs_stream_truncate(pvfs, f->handle->name, fd, 0);
1208 if (!NT_STATUS_IS_OK(status)) {
1215 /* re-resolve the open fd */
1216 status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name);
1217 if (!NT_STATUS_IS_OK(status)) {
1222 if (f->handle->name->stream_id == 0 &&
1223 (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE ||
1224 io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) {
1225 /* for overwrite we need to replace file permissions */
1226 uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
1227 mode_t mode = pvfs_fileperms(pvfs, attrib);
1228 if (fchmod(fd, mode) == -1) {
1230 return pvfs_map_errno(pvfs, errno);
1232 name->dos.attrib = attrib;
1233 status = pvfs_dosattrib_save(pvfs, name, fd);
1234 if (!NT_STATUS_IS_OK(status)) {
1242 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
1243 io->generic.out.oplock_level = OPLOCK_BATCH;
1245 io->generic.out.oplock_level = OPLOCK_NONE;
1247 io->generic.out.file.fnum = f->fnum;
1248 io->generic.out.create_action = stream_existed?
1249 NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED;
1250 io->generic.out.create_time = name->dos.create_time;
1251 io->generic.out.access_time = name->dos.access_time;
1252 io->generic.out.write_time = name->dos.write_time;
1253 io->generic.out.change_time = name->dos.change_time;
1254 io->generic.out.attrib = name->dos.attrib;
1255 io->generic.out.alloc_size = name->dos.alloc_size;
1256 io->generic.out.size = name->st.st_size;
1257 io->generic.out.file_type = FILE_TYPE_DISK;
1258 io->generic.out.ipc_state = 0;
1259 io->generic.out.is_directory = 0;
1261 /* success - keep the file handle */
1262 talloc_steal(f->pvfs, f);
1264 return NT_STATUS_OK;
1271 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
1272 struct ntvfs_request *req, union smb_close *io)
1274 struct pvfs_state *pvfs = ntvfs->private_data;
1275 struct pvfs_file *f;
1276 struct utimbuf unix_times;
1278 if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
1279 return NT_STATUS_DOS(ERRSRV, ERRerror);
1282 if (io->generic.level != RAW_CLOSE_CLOSE) {
1283 return ntvfs_map_close(ntvfs, req, io);
1286 f = pvfs_find_fd(pvfs, req, io->close.in.file.fnum);
1288 return NT_STATUS_INVALID_HANDLE;
1291 if (!null_time(io->close.in.write_time)) {
1292 unix_times.actime = 0;
1293 unix_times.modtime = io->close.in.write_time;
1294 utime(f->handle->name->full_name, &unix_times);
1295 } else if (f->handle->sticky_write_time) {
1296 unix_times.actime = 0;
1297 unix_times.modtime = nt_time_to_unix(f->handle->name->dos.write_time);
1298 utime(f->handle->name->full_name, &unix_times);
1303 return NT_STATUS_OK;
1308 logoff - close all file descriptors open by a vuid
1310 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
1311 struct ntvfs_request *req)
1313 struct pvfs_state *pvfs = ntvfs->private_data;
1314 struct pvfs_file *f, *next;
1316 for (f=pvfs->files.list;f;f=next) {
1318 if (f->session_info == req->session_info) {
1323 return NT_STATUS_OK;
1328 exit - close files for the current pid
1330 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
1331 struct ntvfs_request *req)
1333 struct pvfs_state *pvfs = ntvfs->private_data;
1334 struct pvfs_file *f, *next;
1336 for (f=pvfs->files.list;f;f=next) {
1338 if (f->session_info == req->session_info &&
1339 f->smbpid == req->smbpid) {
1344 return NT_STATUS_OK;
1349 change the delete on close flag on an already open file
1351 NTSTATUS pvfs_set_delete_on_close(struct pvfs_state *pvfs,
1352 struct ntvfs_request *req,
1353 struct pvfs_file *f, BOOL del_on_close)
1355 struct odb_lock *lck;
1358 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) && del_on_close) {
1359 return NT_STATUS_CANNOT_DELETE;
1362 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
1363 !pvfs_directory_empty(pvfs, f->handle->name)) {
1364 return NT_STATUS_DIRECTORY_NOT_EMPTY;
1368 f->handle->create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1370 f->handle->create_options &= ~NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1373 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1375 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1378 status = odb_set_delete_on_close(lck, del_on_close);
1387 determine if a file can be deleted, or if it is prevented by an
1390 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs,
1391 struct ntvfs_request *req,
1392 struct pvfs_filename *name,
1393 struct odb_lock **lckp)
1397 struct odb_lock *lck;
1399 status = pvfs_locking_key(name, name, &key);
1400 if (!NT_STATUS_IS_OK(status)) {
1401 return NT_STATUS_NO_MEMORY;
1404 lck = odb_lock(req, pvfs->odb_context, &key);
1406 DEBUG(0,("Unable to lock opendb for can_delete\n"));
1407 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1410 status = odb_can_open(lck,
1411 NTCREATEX_SHARE_ACCESS_READ |
1412 NTCREATEX_SHARE_ACCESS_WRITE |
1413 NTCREATEX_SHARE_ACCESS_DELETE,
1414 NTCREATEX_OPTIONS_DELETE_ON_CLOSE,
1417 if (NT_STATUS_IS_OK(status)) {
1418 status = pvfs_access_check_simple(pvfs, req, name, SEC_STD_DELETE);
1421 if (!NT_STATUS_IS_OK(status)) {
1424 } else if (lckp != NULL) {
1432 determine if a file can be renamed, or if it is prevented by an
1435 NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs,
1436 struct ntvfs_request *req,
1437 struct pvfs_filename *name,
1438 struct odb_lock **lckp)
1442 struct odb_lock *lck;
1444 status = pvfs_locking_key(name, name, &key);
1445 if (!NT_STATUS_IS_OK(status)) {
1446 return NT_STATUS_NO_MEMORY;
1449 lck = odb_lock(req, pvfs->odb_context, &key);
1451 DEBUG(0,("Unable to lock opendb for can_stat\n"));
1452 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1455 status = odb_can_open(lck,
1456 NTCREATEX_SHARE_ACCESS_READ |
1457 NTCREATEX_SHARE_ACCESS_WRITE,
1461 if (!NT_STATUS_IS_OK(status)) {
1464 } else if (lckp != NULL) {
1472 determine if file meta data can be accessed, or if it is prevented by an
1475 NTSTATUS pvfs_can_stat(struct pvfs_state *pvfs,
1476 struct ntvfs_request *req,
1477 struct pvfs_filename *name)
1481 struct odb_lock *lck;
1483 status = pvfs_locking_key(name, name, &key);
1484 if (!NT_STATUS_IS_OK(status)) {
1485 return NT_STATUS_NO_MEMORY;
1488 lck = odb_lock(req, pvfs->odb_context, &key);
1490 DEBUG(0,("Unable to lock opendb for can_stat\n"));
1491 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1494 status = odb_can_open(lck,
1495 NTCREATEX_SHARE_ACCESS_READ |
1496 NTCREATEX_SHARE_ACCESS_WRITE,
1504 determine if delete on close is set on
1506 BOOL pvfs_delete_on_close_set(struct pvfs_state *pvfs, struct pvfs_file_handle *h,
1507 int *open_count, char **path)
1512 status = odb_get_delete_on_close(pvfs->odb_context, &h->odb_locking_key,
1513 &del_on_close, open_count, path);
1514 if (!NT_STATUS_IS_OK(status)) {
1515 DEBUG(1,("WARNING: unable to determine delete on close status for open file\n"));
1519 return del_on_close;