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/time.h"
26 #include "system/filesys.h"
27 #include "dlinklist.h"
29 #include "librpc/gen_ndr/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 smbsrv_request *req, uint16_t fnum)
46 f = idr_find(pvfs->idtree_fnum, fnum);
51 if (f->fnum != fnum) {
52 smb_panic("pvfs_find_fd: idtree_fnum corruption\n");
55 if (req->session != f->session) {
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;
72 if (h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
73 NTSTATUS status = pvfs_xattr_unlink_hook(h->pvfs, h->name->full_name);
74 if (!NT_STATUS_IS_OK(status)) {
75 DEBUG(0,("Warning: xattr rmdir hook failed for '%s' - %s\n",
76 h->name->full_name, nt_errstr(status)));
78 if (rmdir(h->name->full_name) != 0) {
79 DEBUG(0,("pvfs_close: failed to rmdir '%s' - %s\n",
80 h->name->full_name, strerror(errno)));
88 cleanup a open directory fnum
90 static int pvfs_dir_fnum_destructor(void *p)
92 struct pvfs_file *f = p;
93 DLIST_REMOVE(f->pvfs->open_files, f);
94 idr_remove(f->pvfs->idtree_fnum, f->fnum);
99 setup any EAs and the ACL on newly created files/directories
101 static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs,
102 struct smbsrv_request *req,
103 struct pvfs_filename *name,
109 /* setup any EAs that were asked for */
110 if (io->ntcreatex.in.ea_list) {
111 status = pvfs_setfileinfo_ea_set(pvfs, name, fd,
112 io->ntcreatex.in.ea_list->num_eas,
113 io->ntcreatex.in.ea_list->eas);
114 if (!NT_STATUS_IS_OK(status)) {
119 /* setup an initial sec_desc if requested */
120 if (io->ntcreatex.in.sec_desc) {
121 union smb_setfileinfo set;
123 set.set_secdesc.file.fnum = fnum;
124 set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
125 set.set_secdesc.in.sd = io->ntcreatex.in.sec_desc;
127 status = pvfs_acl_set(pvfs, req, name, fd, &set);
129 /* otherwise setup an inherited acl from the parent */
130 status = pvfs_acl_inherit(pvfs, req, name, fd);
139 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
140 struct smbsrv_request *req,
141 struct pvfs_filename *name,
147 uint32_t create_action;
148 uint32_t access_mask = io->generic.in.access_mask;
150 if (name->stream_name) {
151 return NT_STATUS_NOT_A_DIRECTORY;
154 /* if the client says it must be a directory, and it isn't,
156 if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
157 return NT_STATUS_NOT_A_DIRECTORY;
160 switch (io->generic.in.open_disposition) {
161 case NTCREATEX_DISP_OPEN_IF:
164 case NTCREATEX_DISP_OPEN:
166 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
170 case NTCREATEX_DISP_CREATE:
172 return NT_STATUS_OBJECT_NAME_COLLISION;
176 case NTCREATEX_DISP_OVERWRITE_IF:
177 case NTCREATEX_DISP_OVERWRITE:
178 case NTCREATEX_DISP_SUPERSEDE:
180 return NT_STATUS_INVALID_PARAMETER;
183 f = talloc_p(req, struct pvfs_file);
185 return NT_STATUS_NO_MEMORY;
188 f->handle = talloc_p(f, struct pvfs_file_handle);
189 if (f->handle == NULL) {
190 return NT_STATUS_NO_MEMORY;
193 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_DIR_FNUM, UINT16_MAX);
195 return NT_STATUS_TOO_MANY_OPENED_FILES;
199 /* check the security descriptor */
200 status = pvfs_access_check(pvfs, req, name, &access_mask);
201 if (!NT_STATUS_IS_OK(status)) {
202 idr_remove(pvfs->idtree_fnum, fnum);
208 f->session = req->session;
209 f->smbpid = req->smbpid;
211 f->pending_list = NULL;
213 f->share_access = io->generic.in.share_access;
214 f->impersonation = io->generic.in.impersonation;
215 f->access_mask = access_mask;
217 f->handle->pvfs = pvfs;
218 f->handle->name = talloc_steal(f->handle, name);
220 f->handle->odb_locking_key = data_blob(NULL, 0);
221 f->handle->brl_locking_key = data_blob(NULL, 0);
222 f->handle->create_options = io->generic.in.create_options;
223 f->handle->seek_offset = 0;
224 f->handle->position = 0;
226 f->handle->sticky_write_time = False;
228 DLIST_ADD(pvfs->open_files, f);
230 /* TODO: should we check in the opendb? Do directory opens
231 follow the share_access rules? */
233 /* setup destructors to avoid leaks on abnormal termination */
234 talloc_set_destructor(f->handle, pvfs_dir_handle_destructor);
235 talloc_set_destructor(f, pvfs_dir_fnum_destructor);
238 uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
239 mode_t mode = pvfs_fileperms(pvfs, attrib);
240 if (mkdir(name->full_name, mode) == -1) {
241 idr_remove(pvfs->idtree_fnum, fnum);
242 return pvfs_map_errno(pvfs,errno);
245 pvfs_xattr_unlink_hook(pvfs, name->full_name);
247 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
248 if (!NT_STATUS_IS_OK(status)) {
252 status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, fnum, io);
253 if (!NT_STATUS_IS_OK(status)) {
257 create_action = NTCREATEX_ACTION_CREATED;
259 create_action = NTCREATEX_ACTION_EXISTED;
263 idr_remove(pvfs->idtree_fnum, fnum);
264 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
267 /* the open succeeded, keep this handle permanently */
268 talloc_steal(pvfs, f);
270 io->generic.out.oplock_level = OPLOCK_NONE;
271 io->generic.out.fnum = f->fnum;
272 io->generic.out.create_action = create_action;
273 io->generic.out.create_time = name->dos.create_time;
274 io->generic.out.access_time = name->dos.access_time;
275 io->generic.out.write_time = name->dos.write_time;
276 io->generic.out.change_time = name->dos.change_time;
277 io->generic.out.attrib = name->dos.attrib;
278 io->generic.out.alloc_size = name->dos.alloc_size;
279 io->generic.out.size = name->st.st_size;
280 io->generic.out.file_type = FILE_TYPE_DISK;
281 io->generic.out.ipc_state = 0;
282 io->generic.out.is_directory = 1;
287 idr_remove(pvfs->idtree_fnum, fnum);
288 rmdir(name->full_name);
293 destroy a struct pvfs_file_handle
295 static int pvfs_handle_destructor(void *p)
297 struct pvfs_file_handle *h = p;
299 /* the write time is no longer sticky */
300 if (h->sticky_write_time) {
302 status = pvfs_dosattrib_load(h->pvfs, h->name, h->fd);
303 if (NT_STATUS_IS_OK(status)) {
304 h->name->dos.flags &= ~XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME;
305 pvfs_dosattrib_save(h->pvfs, h->name, h->fd);
309 if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
310 h->name->stream_name) {
312 status = pvfs_stream_delete(h->pvfs, h->name, h->fd);
313 if (!NT_STATUS_IS_OK(status)) {
314 DEBUG(0,("Failed to delete stream '%s' on close of '%s'\n",
315 h->name->stream_name, h->name->full_name));
320 if (close(h->fd) != 0) {
321 DEBUG(0,("pvfs_handle_destructor: close(%d) failed for %s - %s\n",
322 h->fd, h->name->full_name, strerror(errno)));
327 if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
328 h->name->stream_name == NULL) {
330 status = pvfs_xattr_unlink_hook(h->pvfs, h->name->full_name);
331 if (!NT_STATUS_IS_OK(status)) {
332 DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
333 h->name->full_name, nt_errstr(status)));
335 if (unlink(h->name->full_name) != 0) {
336 DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n",
337 h->name->full_name, strerror(errno)));
341 if (h->have_opendb_entry) {
342 struct odb_lock *lck;
345 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
347 DEBUG(0,("Unable to lock opendb for close\n"));
351 status = odb_close_file(lck, h);
352 if (!NT_STATUS_IS_OK(status)) {
353 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
354 h->name->full_name, nt_errstr(status)));
365 destroy a struct pvfs_file
367 static int pvfs_fnum_destructor(void *p)
369 struct pvfs_file *f = p;
371 DLIST_REMOVE(f->pvfs->open_files, f);
372 pvfs_lock_close(f->pvfs, f);
373 idr_remove(f->pvfs->idtree_fnum, f->fnum);
380 form the lock context used for opendb locking. Note that we must
381 zero here to take account of possible padding on some architectures
383 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
384 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
390 ZERO_STRUCT(lock_context);
392 lock_context.device = name->st.st_dev;
393 lock_context.inode = name->st.st_ino;
395 *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
396 if (key->data == NULL) {
397 return NT_STATUS_NO_MEMORY;
404 form the lock context used for byte range locking. This is separate
405 from the locking key used for opendb locking as it needs to take
406 account of file streams (each stream is a separate byte range
409 static NTSTATUS pvfs_brl_locking_key(struct pvfs_filename *name,
410 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
414 status = pvfs_locking_key(name, mem_ctx, &odb_key);
415 if (!NT_STATUS_IS_OK(status)) {
418 if (name->stream_name == NULL) {
422 *key = data_blob_talloc(mem_ctx, NULL,
423 odb_key.length + strlen(name->stream_name) + 1);
424 if (key->data == NULL) {
425 return NT_STATUS_NO_MEMORY;
427 memcpy(key->data, odb_key.data, odb_key.length);
428 memcpy(key->data + odb_key.length,
429 name->stream_name, strlen(name->stream_name)+1);
430 data_blob_free(&odb_key);
438 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
439 struct smbsrv_request *req,
440 struct pvfs_filename *name,
446 struct odb_lock *lck;
447 uint32_t create_options = io->generic.in.create_options;
448 uint32_t share_access = io->generic.in.share_access;
449 uint32_t access_mask = io->generic.in.access_mask;
453 if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
454 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
455 return NT_STATUS_CANNOT_DELETE;
458 if (access_mask & SEC_FLAG_MAXIMUM_ALLOWED) {
459 access_mask = SEC_RIGHTS_FILE_READ | SEC_RIGHTS_FILE_WRITE;
462 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
468 f = talloc_p(req, struct pvfs_file);
470 return NT_STATUS_NO_MEMORY;
473 f->handle = talloc_p(f, struct pvfs_file_handle);
474 if (f->handle == NULL) {
475 return NT_STATUS_NO_MEMORY;
478 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_NEW_FNUM, UINT16_MAX);
480 return NT_STATUS_TOO_MANY_OPENED_FILES;
483 attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
484 mode = pvfs_fileperms(pvfs, attrib);
486 /* create the file */
487 fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
489 idr_remove(pvfs->idtree_fnum, fnum);
490 return pvfs_map_errno(pvfs, errno);
493 pvfs_xattr_unlink_hook(pvfs, name->full_name);
495 /* if this was a stream create then create the stream as well */
496 if (name->stream_name) {
497 status = pvfs_stream_create(pvfs, name, fd);
498 if (!NT_STATUS_IS_OK(status)) {
499 idr_remove(pvfs->idtree_fnum, fnum);
505 /* re-resolve the open fd */
506 status = pvfs_resolve_name_fd(pvfs, fd, name);
507 if (!NT_STATUS_IS_OK(status)) {
508 idr_remove(pvfs->idtree_fnum, fnum);
513 name->dos.attrib = attrib;
514 status = pvfs_dosattrib_save(pvfs, name, fd);
515 if (!NT_STATUS_IS_OK(status)) {
520 status = pvfs_open_setup_eas_acl(pvfs, req, name, fd, fnum, io);
521 if (!NT_STATUS_IS_OK(status)) {
525 /* form the lock context used for byte range locking and
527 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
528 if (!NT_STATUS_IS_OK(status)) {
532 status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
533 if (!NT_STATUS_IS_OK(status)) {
537 /* grab a lock on the open file record */
538 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
540 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
542 /* we were supposed to do a blocking lock, so something
544 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
548 status = odb_open_file(lck, f->handle, name->stream_id,
549 share_access, create_options, access_mask);
551 if (!NT_STATUS_IS_OK(status)) {
552 /* bad news, we must have hit a race - we don't delete the file
553 here as the most likely scenario is that someone else created
554 the file at the same time */
555 idr_remove(pvfs->idtree_fnum, fnum);
561 f->session = req->session;
562 f->smbpid = req->smbpid;
564 f->pending_list = NULL;
566 f->share_access = io->generic.in.share_access;
567 f->access_mask = access_mask;
568 f->impersonation = io->generic.in.impersonation;
570 f->handle->pvfs = pvfs;
571 f->handle->name = talloc_steal(f->handle, name);
573 f->handle->create_options = io->generic.in.create_options;
574 f->handle->seek_offset = 0;
575 f->handle->position = 0;
577 f->handle->have_opendb_entry = True;
578 f->handle->sticky_write_time = False;
580 DLIST_ADD(pvfs->open_files, f);
582 /* setup a destructor to avoid file descriptor leaks on
583 abnormal termination */
584 talloc_set_destructor(f, pvfs_fnum_destructor);
585 talloc_set_destructor(f->handle, pvfs_handle_destructor);
588 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
589 io->generic.out.oplock_level = OPLOCK_EXCLUSIVE;
591 io->generic.out.oplock_level = OPLOCK_NONE;
593 io->generic.out.fnum = f->fnum;
594 io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
595 io->generic.out.create_time = name->dos.create_time;
596 io->generic.out.access_time = name->dos.access_time;
597 io->generic.out.write_time = name->dos.write_time;
598 io->generic.out.change_time = name->dos.change_time;
599 io->generic.out.attrib = name->dos.attrib;
600 io->generic.out.alloc_size = name->dos.alloc_size;
601 io->generic.out.size = name->st.st_size;
602 io->generic.out.file_type = FILE_TYPE_DISK;
603 io->generic.out.ipc_state = 0;
604 io->generic.out.is_directory = 0;
606 /* success - keep the file handle */
607 talloc_steal(pvfs, f);
612 idr_remove(pvfs->idtree_fnum, fnum);
614 unlink(name->full_name);
620 state of a pending open retry
622 struct pvfs_open_retry {
623 struct ntvfs_module_context *ntvfs;
624 struct smbsrv_request *req;
627 DATA_BLOB odb_locking_key;
630 /* destroy a pending open request */
631 static int pvfs_retry_destructor(void *ptr)
633 struct pvfs_open_retry *r = ptr;
634 struct pvfs_state *pvfs = r->ntvfs->private_data;
635 if (r->odb_locking_key.data) {
636 struct odb_lock *lck;
637 lck = odb_lock(r->req, pvfs->odb_context, &r->odb_locking_key);
639 odb_remove_pending(lck, r);
649 static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
651 struct pvfs_open_retry *r = private;
652 struct ntvfs_module_context *ntvfs = r->ntvfs;
653 struct smbsrv_request *req = r->req;
654 union smb_open *io = r->io;
657 /* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
658 just a bug in their server, but we better do the same */
659 if (reason == PVFS_WAIT_CANCEL) {
663 talloc_free(r->wait_handle);
665 if (reason == PVFS_WAIT_TIMEOUT) {
666 /* if it timed out, then give the failure
669 req->async_states->status = NT_STATUS_SHARING_VIOLATION;
670 req->async_states->send_fn(req);
674 /* the pending odb entry is already removed. We use a null locking
675 key to indicate this */
676 data_blob_free(&r->odb_locking_key);
679 /* try the open again, which could trigger another retry setup
680 if it wants to, so we have to unmark the async flag so we
681 will know if it does a second async reply */
682 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
684 status = pvfs_open(ntvfs, req, io);
685 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
686 /* the 2nd try also replied async, so we don't send
691 /* re-mark it async, just in case someone up the chain does
693 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
695 /* send the reply up the chain */
696 req->async_states->status = status;
697 req->async_states->send_fn(req);
702 special handling for openx DENY_DOS semantics
704 This function attempts a reference open using an existing handle. If its allowed,
705 then it returns NT_STATUS_OK, otherwise it returns any other code and normal
706 open processing continues.
708 static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
709 struct smbsrv_request *req, union smb_open *io,
710 struct pvfs_file *f, struct odb_lock *lck)
712 struct pvfs_state *pvfs = ntvfs->private_data;
713 struct pvfs_file *f2;
714 struct pvfs_filename *name;
716 /* search for an existing open with the right parameters. Note
717 the magic ntcreatex options flag, which is set in the
718 generic mapping code. This might look ugly, but its
719 actually pretty much now w2k does it internally as well.
721 If you look at the BASE-DENYDOS test you will see that a
722 DENY_DOS is a very special case, and in the right
723 circumstances you actually get the _same_ handle back
724 twice, rather than a new handle.
726 for (f2=pvfs->open_files;f2;f2=f2->next) {
728 f2->session == req->session &&
729 f2->smbpid == req->smbpid &&
730 (f2->handle->create_options &
731 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
732 NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) &&
733 (f2->access_mask & SEC_FILE_WRITE_DATA) &&
734 StrCaseCmp(f2->handle->name->original_name,
735 io->generic.in.fname)==0) {
741 return NT_STATUS_SHARING_VIOLATION;
744 /* quite an insane set of semantics ... */
745 if (is_exe_filename(io->generic.in.fname) &&
746 (f2->handle->create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) {
747 return NT_STATUS_SHARING_VIOLATION;
751 setup a reference to the existing handle
753 talloc_free(f->handle);
754 f->handle = talloc_reference(f, f2->handle);
758 name = f->handle->name;
760 io->generic.out.oplock_level = OPLOCK_NONE;
761 io->generic.out.fnum = f->fnum;
762 io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
763 io->generic.out.create_time = name->dos.create_time;
764 io->generic.out.access_time = name->dos.access_time;
765 io->generic.out.write_time = name->dos.write_time;
766 io->generic.out.change_time = name->dos.change_time;
767 io->generic.out.attrib = name->dos.attrib;
768 io->generic.out.alloc_size = name->dos.alloc_size;
769 io->generic.out.size = name->st.st_size;
770 io->generic.out.file_type = FILE_TYPE_DISK;
771 io->generic.out.ipc_state = 0;
772 io->generic.out.is_directory = 0;
774 talloc_steal(f->pvfs, f);
782 setup for a open retry after a sharing violation
784 static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
785 struct smbsrv_request *req,
788 struct odb_lock *lck)
790 struct pvfs_state *pvfs = ntvfs->private_data;
791 struct pvfs_open_retry *r;
793 struct timeval end_time;
795 if (io->generic.in.create_options &
796 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
797 /* see if we can satisfy the request using the special DENY_DOS
799 status = pvfs_open_deny_dos(ntvfs, req, io, f, lck);
800 if (NT_STATUS_IS_OK(status)) {
805 r = talloc_p(req, struct pvfs_open_retry);
807 return NT_STATUS_NO_MEMORY;
813 r->odb_locking_key = data_blob_talloc(r,
814 f->handle->odb_locking_key.data,
815 f->handle->odb_locking_key.length);
817 end_time = timeval_add(&req->request_time, 0, pvfs->sharing_violation_delay);
819 /* setup a pending lock */
820 status = odb_open_file_pending(lck, r);
821 if (!NT_STATUS_IS_OK(status)) {
828 talloc_set_destructor(r, pvfs_retry_destructor);
830 r->wait_handle = pvfs_wait_message(pvfs, req, MSG_PVFS_RETRY_OPEN, end_time,
832 if (r->wait_handle == NULL) {
833 return NT_STATUS_NO_MEMORY;
836 talloc_steal(pvfs, r);
844 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
845 struct smbsrv_request *req, union smb_open *io)
847 struct pvfs_state *pvfs = ntvfs->private_data;
849 struct pvfs_filename *name;
853 struct odb_lock *lck;
854 uint32_t create_options;
855 uint32_t share_access;
856 uint32_t access_mask;
859 /* use the generic mapping code to avoid implementing all the
860 different open calls. */
861 if (io->generic.level != RAW_OPEN_GENERIC &&
862 io->generic.level != RAW_OPEN_NTTRANS_CREATE) {
863 return ntvfs_map_open(req, io, ntvfs);
866 /* resolve the cifs name to a posix name */
867 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
868 PVFS_RESOLVE_STREAMS, &name);
869 if (!NT_STATUS_IS_OK(status)) {
873 /* directory opens are handled separately */
874 if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
875 (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
876 return pvfs_open_directory(pvfs, req, name, io);
879 /* FILE_ATTRIBUTE_DIRECTORY is ignored if the above test for directory
880 open doesn't match */
881 io->generic.in.file_attr &= ~FILE_ATTRIBUTE_DIRECTORY;
883 create_options = io->generic.in.create_options;
884 share_access = io->generic.in.share_access;
885 access_mask = io->generic.in.access_mask;
887 /* certain create options are not allowed */
888 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
889 !(access_mask & SEC_STD_DELETE)) {
890 return NT_STATUS_INVALID_PARAMETER;
893 switch (io->generic.in.open_disposition) {
894 case NTCREATEX_DISP_SUPERSEDE:
898 case NTCREATEX_DISP_OVERWRITE_IF:
902 case NTCREATEX_DISP_OPEN:
903 if (!name->stream_exists) {
904 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
909 case NTCREATEX_DISP_OVERWRITE:
910 if (!name->stream_exists) {
911 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
916 case NTCREATEX_DISP_CREATE:
917 if (name->stream_exists) {
918 return NT_STATUS_OBJECT_NAME_COLLISION;
923 case NTCREATEX_DISP_OPEN_IF:
928 return NT_STATUS_INVALID_PARAMETER;
931 /* handle creating a new file separately */
933 status = pvfs_create_file(pvfs, req, name, io);
934 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
938 /* we've hit a race - the file was created during this call */
939 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
943 /* try re-resolving the name */
944 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
945 if (!NT_STATUS_IS_OK(status)) {
948 /* fall through to a normal open */
951 if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
952 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
953 return NT_STATUS_CANNOT_DELETE;
956 /* check the security descriptor */
957 status = pvfs_access_check(pvfs, req, name, &access_mask);
958 if (!NT_STATUS_IS_OK(status)) {
962 f = talloc_p(req, struct pvfs_file);
964 return NT_STATUS_NO_MEMORY;
967 f->handle = talloc_p(f, struct pvfs_file_handle);
968 if (f->handle == NULL) {
969 return NT_STATUS_NO_MEMORY;
972 /* allocate a fnum */
973 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
975 return NT_STATUS_TOO_MANY_OPENED_FILES;
979 f->session = req->session;
980 f->smbpid = req->smbpid;
982 f->pending_list = NULL;
984 f->share_access = io->generic.in.share_access;
985 f->access_mask = access_mask;
986 f->impersonation = io->generic.in.impersonation;
988 f->handle->pvfs = pvfs;
990 f->handle->name = talloc_steal(f->handle, name);
991 f->handle->create_options = io->generic.in.create_options;
992 f->handle->seek_offset = 0;
993 f->handle->position = 0;
995 f->handle->have_opendb_entry = False;
996 f->handle->sticky_write_time = False;
998 /* form the lock context used for byte range locking and
1000 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
1001 if (!NT_STATUS_IS_OK(status)) {
1002 idr_remove(pvfs->idtree_fnum, f->fnum);
1006 status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
1007 if (!NT_STATUS_IS_OK(status)) {
1008 idr_remove(pvfs->idtree_fnum, f->fnum);
1012 /* get a lock on this file before the actual open */
1013 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1015 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
1017 /* we were supposed to do a blocking lock, so something
1019 idr_remove(pvfs->idtree_fnum, fnum);
1020 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1023 DLIST_ADD(pvfs->open_files, f);
1025 /* setup a destructor to avoid file descriptor leaks on
1026 abnormal termination */
1027 talloc_set_destructor(f, pvfs_fnum_destructor);
1028 talloc_set_destructor(f->handle, pvfs_handle_destructor);
1031 /* see if we are allowed to open at the same time as existing opens */
1032 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
1033 share_access, create_options, access_mask);
1035 /* on a sharing violation we need to retry when the file is closed by
1036 the other user, or after 1 second */
1037 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) &&
1038 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
1039 return pvfs_open_setup_retry(ntvfs, req, io, f, lck);
1042 if (!NT_STATUS_IS_OK(status)) {
1047 f->handle->have_opendb_entry = True;
1049 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
1055 /* do the actual open */
1056 fd = open(f->handle->name->full_name, flags);
1059 return pvfs_map_errno(f->pvfs, errno);
1064 stream_existed = name->stream_exists;
1066 /* if this was a stream create then create the stream as well */
1067 if (!name->stream_exists) {
1068 status = pvfs_stream_create(pvfs, f->handle->name, fd);
1069 if (!NT_STATUS_IS_OK(status)) {
1075 /* re-resolve the open fd */
1076 status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name);
1077 if (!NT_STATUS_IS_OK(status)) {
1082 if (f->handle->name->stream_id == 0 &&
1083 (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE ||
1084 io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) {
1085 /* for overwrite we need to replace file permissions */
1086 uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
1087 mode_t mode = pvfs_fileperms(pvfs, attrib);
1088 if (fchmod(fd, mode) == -1) {
1090 return pvfs_map_errno(pvfs, errno);
1092 name->dos.attrib = attrib;
1093 status = pvfs_dosattrib_save(pvfs, name, fd);
1094 if (!NT_STATUS_IS_OK(status)) {
1102 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
1103 io->generic.out.oplock_level = OPLOCK_EXCLUSIVE;
1105 io->generic.out.oplock_level = OPLOCK_NONE;
1107 io->generic.out.fnum = f->fnum;
1108 io->generic.out.create_action = stream_existed?
1109 NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED;
1110 io->generic.out.create_time = name->dos.create_time;
1111 io->generic.out.access_time = name->dos.access_time;
1112 io->generic.out.write_time = name->dos.write_time;
1113 io->generic.out.change_time = name->dos.change_time;
1114 io->generic.out.attrib = name->dos.attrib;
1115 io->generic.out.alloc_size = name->dos.alloc_size;
1116 io->generic.out.size = name->st.st_size;
1117 io->generic.out.file_type = FILE_TYPE_DISK;
1118 io->generic.out.ipc_state = 0;
1119 io->generic.out.is_directory = 0;
1121 /* success - keep the file handle */
1122 talloc_steal(f->pvfs, f);
1124 return NT_STATUS_OK;
1131 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
1132 struct smbsrv_request *req, union smb_close *io)
1134 struct pvfs_state *pvfs = ntvfs->private_data;
1135 struct pvfs_file *f;
1136 struct utimbuf unix_times;
1138 if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
1139 return NT_STATUS_UNSUCCESSFUL;
1142 if (io->generic.level != RAW_CLOSE_CLOSE) {
1143 return ntvfs_map_close(req, io, ntvfs);
1146 f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
1148 return NT_STATUS_INVALID_HANDLE;
1151 if (!null_time(io->close.in.write_time)) {
1152 unix_times.actime = 0;
1153 unix_times.modtime = io->close.in.write_time;
1154 utime(f->handle->name->full_name, &unix_times);
1155 } else if (f->handle->sticky_write_time) {
1156 unix_times.actime = 0;
1157 unix_times.modtime = nt_time_to_unix(f->handle->name->dos.write_time);
1158 utime(f->handle->name->full_name, &unix_times);
1163 return NT_STATUS_OK;
1168 logoff - close all file descriptors open by a vuid
1170 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
1171 struct smbsrv_request *req)
1173 struct pvfs_state *pvfs = ntvfs->private_data;
1174 struct pvfs_file *f, *next;
1176 for (f=pvfs->open_files;f;f=next) {
1178 if (f->session == req->session) {
1183 return NT_STATUS_OK;
1188 exit - close files for the current pid
1190 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
1191 struct smbsrv_request *req)
1193 struct pvfs_state *pvfs = ntvfs->private_data;
1194 struct pvfs_file *f, *next;
1196 for (f=pvfs->open_files;f;f=next) {
1198 if (f->smbpid == req->smbpid) {
1203 return NT_STATUS_OK;
1208 change the create options on an already open file
1210 NTSTATUS pvfs_change_create_options(struct pvfs_state *pvfs,
1211 struct smbsrv_request *req,
1212 struct pvfs_file *f, uint32_t create_options)
1214 struct odb_lock *lck;
1217 if (f->handle->create_options == create_options) {
1218 return NT_STATUS_OK;
1221 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
1222 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
1223 return NT_STATUS_CANNOT_DELETE;
1226 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1228 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1231 status = odb_set_create_options(lck, f->handle, create_options);
1232 if (NT_STATUS_IS_OK(status)) {
1233 f->handle->create_options = create_options;
1241 determine if a file can be deleted, or if it is prevented by an
1244 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs,
1245 struct smbsrv_request *req,
1246 struct pvfs_filename *name)
1251 status = pvfs_locking_key(name, name, &key);
1252 if (!NT_STATUS_IS_OK(status)) {
1253 return NT_STATUS_NO_MEMORY;
1256 status = odb_can_open(pvfs->odb_context, &key,
1257 NTCREATEX_SHARE_ACCESS_READ |
1258 NTCREATEX_SHARE_ACCESS_WRITE |
1259 NTCREATEX_SHARE_ACCESS_DELETE,
1260 NTCREATEX_OPTIONS_DELETE_ON_CLOSE,
1263 if (NT_STATUS_IS_OK(status)) {
1264 status = pvfs_access_check_simple(pvfs, req, name, SEC_STD_DELETE);
1271 determine if a file can be renamed, or if it is prevented by an
1274 NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs, struct pvfs_filename *name)
1279 status = pvfs_locking_key(name, name, &key);
1280 if (!NT_STATUS_IS_OK(status)) {
1281 return NT_STATUS_NO_MEMORY;
1284 status = odb_can_open(pvfs->odb_context, &key,
1285 NTCREATEX_SHARE_ACCESS_READ |
1286 NTCREATEX_SHARE_ACCESS_WRITE,