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);
102 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
103 struct smbsrv_request *req,
104 struct pvfs_filename *name,
110 uint32_t create_action;
111 uint32_t access_mask = io->generic.in.access_mask;
113 if (name->stream_name) {
114 return NT_STATUS_NOT_A_DIRECTORY;
117 /* if the client says it must be a directory, and it isn't,
119 if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
120 return NT_STATUS_NOT_A_DIRECTORY;
123 switch (io->generic.in.open_disposition) {
124 case NTCREATEX_DISP_OPEN_IF:
127 case NTCREATEX_DISP_OPEN:
129 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
133 case NTCREATEX_DISP_CREATE:
135 return NT_STATUS_OBJECT_NAME_COLLISION;
139 case NTCREATEX_DISP_OVERWRITE_IF:
140 case NTCREATEX_DISP_OVERWRITE:
141 case NTCREATEX_DISP_SUPERSEDE:
143 return NT_STATUS_INVALID_PARAMETER;
146 f = talloc_p(req, struct pvfs_file);
148 return NT_STATUS_NO_MEMORY;
151 f->handle = talloc_p(f, struct pvfs_file_handle);
152 if (f->handle == NULL) {
153 return NT_STATUS_NO_MEMORY;
156 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_DIR_FNUM, UINT16_MAX);
158 return NT_STATUS_TOO_MANY_OPENED_FILES;
162 /* check the security descriptor */
163 status = pvfs_access_check(pvfs, req, name, &access_mask);
164 if (!NT_STATUS_IS_OK(status)) {
170 f->session = req->session;
171 f->smbpid = req->smbpid;
173 f->pending_list = NULL;
175 f->share_access = io->generic.in.share_access;
176 f->impersonation = io->generic.in.impersonation;
177 f->access_mask = access_mask;
179 f->handle->pvfs = pvfs;
180 f->handle->name = talloc_steal(f->handle, name);
182 f->handle->odb_locking_key = data_blob(NULL, 0);
183 f->handle->brl_locking_key = data_blob(NULL, 0);
184 f->handle->create_options = io->generic.in.create_options;
185 f->handle->seek_offset = 0;
186 f->handle->position = 0;
188 f->handle->sticky_write_time = False;
190 DLIST_ADD(pvfs->open_files, f);
192 /* TODO: should we check in the opendb? Do directory opens
193 follow the share_access rules? */
195 /* setup destructors to avoid leaks on abnormal termination */
196 talloc_set_destructor(f->handle, pvfs_dir_handle_destructor);
197 talloc_set_destructor(f, pvfs_dir_fnum_destructor);
200 uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
201 mode_t mode = pvfs_fileperms(pvfs, attrib);
202 if (mkdir(name->full_name, mode) == -1) {
203 return pvfs_map_errno(pvfs,errno);
206 pvfs_xattr_unlink_hook(pvfs, name->full_name);
208 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
209 if (!NT_STATUS_IS_OK(status)) {
212 create_action = NTCREATEX_ACTION_CREATED;
214 create_action = NTCREATEX_ACTION_EXISTED;
218 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
221 /* the open succeeded, keep this handle permanently */
222 talloc_steal(pvfs, f);
224 io->generic.out.oplock_level = OPLOCK_NONE;
225 io->generic.out.fnum = f->fnum;
226 io->generic.out.create_action = create_action;
227 io->generic.out.create_time = name->dos.create_time;
228 io->generic.out.access_time = name->dos.access_time;
229 io->generic.out.write_time = name->dos.write_time;
230 io->generic.out.change_time = name->dos.change_time;
231 io->generic.out.attrib = name->dos.attrib;
232 io->generic.out.alloc_size = name->dos.alloc_size;
233 io->generic.out.size = name->st.st_size;
234 io->generic.out.file_type = FILE_TYPE_DISK;
235 io->generic.out.ipc_state = 0;
236 io->generic.out.is_directory = 1;
242 destroy a struct pvfs_file_handle
244 static int pvfs_handle_destructor(void *p)
246 struct pvfs_file_handle *h = p;
248 /* the write time is no longer sticky */
249 if (h->sticky_write_time) {
251 status = pvfs_dosattrib_load(h->pvfs, h->name, h->fd);
252 if (NT_STATUS_IS_OK(status)) {
253 h->name->dos.flags &= ~XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME;
254 pvfs_dosattrib_save(h->pvfs, h->name, h->fd);
258 if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
259 h->name->stream_name) {
261 status = pvfs_stream_delete(h->pvfs, h->name, h->fd);
262 if (!NT_STATUS_IS_OK(status)) {
263 DEBUG(0,("Failed to delete stream '%s' on close of '%s'\n",
264 h->name->stream_name, h->name->full_name));
269 if (close(h->fd) != 0) {
270 DEBUG(0,("pvfs_handle_destructor: close(%d) failed for %s - %s\n",
271 h->fd, h->name->full_name, strerror(errno)));
276 if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
277 h->name->stream_name == NULL) {
279 status = pvfs_xattr_unlink_hook(h->pvfs, h->name->full_name);
280 if (!NT_STATUS_IS_OK(status)) {
281 DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
282 h->name->full_name, nt_errstr(status)));
284 if (unlink(h->name->full_name) != 0) {
285 DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n",
286 h->name->full_name, strerror(errno)));
290 if (h->have_opendb_entry) {
291 struct odb_lock *lck;
294 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
296 DEBUG(0,("Unable to lock opendb for close\n"));
300 status = odb_close_file(lck, h);
301 if (!NT_STATUS_IS_OK(status)) {
302 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
303 h->name->full_name, nt_errstr(status)));
314 destroy a struct pvfs_file
316 static int pvfs_fnum_destructor(void *p)
318 struct pvfs_file *f = p;
320 DLIST_REMOVE(f->pvfs->open_files, f);
321 pvfs_lock_close(f->pvfs, f);
322 idr_remove(f->pvfs->idtree_fnum, f->fnum);
329 form the lock context used for opendb locking. Note that we must
330 zero here to take account of possible padding on some architectures
332 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
333 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
339 ZERO_STRUCT(lock_context);
341 lock_context.device = name->st.st_dev;
342 lock_context.inode = name->st.st_ino;
344 *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
345 if (key->data == NULL) {
346 return NT_STATUS_NO_MEMORY;
353 form the lock context used for byte range locking. This is separate
354 from the locking key used for opendb locking as it needs to take
355 account of file streams (each stream is a separate byte range
358 static NTSTATUS pvfs_brl_locking_key(struct pvfs_filename *name,
359 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
363 status = pvfs_locking_key(name, mem_ctx, &odb_key);
364 if (!NT_STATUS_IS_OK(status)) {
367 if (name->stream_name == NULL) {
371 *key = data_blob_talloc(mem_ctx, NULL,
372 odb_key.length + strlen(name->stream_name) + 1);
373 if (key->data == NULL) {
374 return NT_STATUS_NO_MEMORY;
376 memcpy(key->data, odb_key.data, odb_key.length);
377 memcpy(key->data + odb_key.length,
378 name->stream_name, strlen(name->stream_name)+1);
379 data_blob_free(&odb_key);
387 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
388 struct smbsrv_request *req,
389 struct pvfs_filename *name,
395 struct odb_lock *lck;
396 uint32_t create_options = io->generic.in.create_options;
397 uint32_t share_access = io->generic.in.share_access;
398 uint32_t access_mask = io->generic.in.access_mask;
402 if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
403 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
404 return NT_STATUS_CANNOT_DELETE;
407 if (access_mask & SEC_FLAG_MAXIMUM_ALLOWED) {
408 access_mask = SEC_RIGHTS_FILE_READ | SEC_RIGHTS_FILE_WRITE;
411 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
417 f = talloc_p(req, struct pvfs_file);
419 return NT_STATUS_NO_MEMORY;
422 f->handle = talloc_p(f, struct pvfs_file_handle);
423 if (f->handle == NULL) {
424 return NT_STATUS_NO_MEMORY;
427 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_NEW_FNUM, UINT16_MAX);
429 return NT_STATUS_TOO_MANY_OPENED_FILES;
432 attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
433 mode = pvfs_fileperms(pvfs, attrib);
435 /* create the file */
436 fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
438 idr_remove(pvfs->idtree_fnum, fnum);
439 return pvfs_map_errno(pvfs, errno);
442 pvfs_xattr_unlink_hook(pvfs, name->full_name);
444 /* if this was a stream create then create the stream as well */
445 if (name->stream_name) {
446 status = pvfs_stream_create(pvfs, name, fd);
447 if (!NT_STATUS_IS_OK(status)) {
448 idr_remove(pvfs->idtree_fnum, fnum);
454 /* re-resolve the open fd */
455 status = pvfs_resolve_name_fd(pvfs, fd, name);
456 if (!NT_STATUS_IS_OK(status)) {
457 idr_remove(pvfs->idtree_fnum, fnum);
462 name->dos.attrib = attrib;
463 status = pvfs_dosattrib_save(pvfs, name, fd);
464 if (!NT_STATUS_IS_OK(status)) {
465 idr_remove(pvfs->idtree_fnum, fnum);
470 /* setup any EAs that were asked for */
471 if (io->ntcreatex.in.ea_list) {
472 status = pvfs_setfileinfo_ea_set(pvfs, name, fd,
473 io->ntcreatex.in.ea_list->num_eas,
474 io->ntcreatex.in.ea_list->eas);
475 if (!NT_STATUS_IS_OK(status)) {
476 idr_remove(pvfs->idtree_fnum, fnum);
482 /* setup an initial sec_desc is required */
483 if (io->ntcreatex.in.sec_desc) {
484 union smb_setfileinfo set;
486 set.set_secdesc.file.fnum = fnum;
487 set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
488 set.set_secdesc.in.sd = io->ntcreatex.in.sec_desc;
490 status = pvfs_acl_set(pvfs, req, name, fd, &set);
491 if (!NT_STATUS_IS_OK(status)) {
492 idr_remove(pvfs->idtree_fnum, fnum);
498 /* form the lock context used for byte range locking and
500 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
501 if (!NT_STATUS_IS_OK(status)) {
502 idr_remove(pvfs->idtree_fnum, fnum);
507 status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
508 if (!NT_STATUS_IS_OK(status)) {
509 idr_remove(pvfs->idtree_fnum, fnum);
514 /* grab a lock on the open file record */
515 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
517 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
519 /* we were supposed to do a blocking lock, so something
521 idr_remove(pvfs->idtree_fnum, fnum);
523 return NT_STATUS_INTERNAL_DB_CORRUPTION;
526 status = odb_open_file(lck, f->handle, name->stream_id,
527 share_access, create_options, access_mask);
529 if (!NT_STATUS_IS_OK(status)) {
530 /* bad news, we must have hit a race */
531 idr_remove(pvfs->idtree_fnum, fnum);
537 f->session = req->session;
538 f->smbpid = req->smbpid;
540 f->pending_list = NULL;
542 f->share_access = io->generic.in.share_access;
543 f->access_mask = access_mask;
544 f->impersonation = io->generic.in.impersonation;
546 f->handle->pvfs = pvfs;
547 f->handle->name = talloc_steal(f->handle, name);
549 f->handle->create_options = io->generic.in.create_options;
550 f->handle->seek_offset = 0;
551 f->handle->position = 0;
553 f->handle->have_opendb_entry = True;
554 f->handle->sticky_write_time = False;
556 DLIST_ADD(pvfs->open_files, f);
558 /* setup a destructor to avoid file descriptor leaks on
559 abnormal termination */
560 talloc_set_destructor(f, pvfs_fnum_destructor);
561 talloc_set_destructor(f->handle, pvfs_handle_destructor);
564 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
565 io->generic.out.oplock_level = OPLOCK_EXCLUSIVE;
567 io->generic.out.oplock_level = OPLOCK_NONE;
569 io->generic.out.fnum = f->fnum;
570 io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
571 io->generic.out.create_time = name->dos.create_time;
572 io->generic.out.access_time = name->dos.access_time;
573 io->generic.out.write_time = name->dos.write_time;
574 io->generic.out.change_time = name->dos.change_time;
575 io->generic.out.attrib = name->dos.attrib;
576 io->generic.out.alloc_size = name->dos.alloc_size;
577 io->generic.out.size = name->st.st_size;
578 io->generic.out.file_type = FILE_TYPE_DISK;
579 io->generic.out.ipc_state = 0;
580 io->generic.out.is_directory = 0;
582 /* success - keep the file handle */
583 talloc_steal(pvfs, f);
590 state of a pending open retry
592 struct pvfs_open_retry {
593 struct ntvfs_module_context *ntvfs;
594 struct smbsrv_request *req;
597 DATA_BLOB odb_locking_key;
600 /* destroy a pending open request */
601 static int pvfs_retry_destructor(void *ptr)
603 struct pvfs_open_retry *r = ptr;
604 struct pvfs_state *pvfs = r->ntvfs->private_data;
605 if (r->odb_locking_key.data) {
606 struct odb_lock *lck;
607 lck = odb_lock(r->req, pvfs->odb_context, &r->odb_locking_key);
609 odb_remove_pending(lck, r);
619 static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
621 struct pvfs_open_retry *r = private;
622 struct ntvfs_module_context *ntvfs = r->ntvfs;
623 struct smbsrv_request *req = r->req;
624 union smb_open *io = r->io;
627 /* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
628 just a bug in their server, but we better do the same */
629 if (reason == PVFS_WAIT_CANCEL) {
633 talloc_free(r->wait_handle);
635 if (reason == PVFS_WAIT_TIMEOUT) {
636 /* if it timed out, then give the failure
639 req->async_states->status = NT_STATUS_SHARING_VIOLATION;
640 req->async_states->send_fn(req);
644 /* the pending odb entry is already removed. We use a null locking
645 key to indicate this */
646 data_blob_free(&r->odb_locking_key);
649 /* try the open again, which could trigger another retry setup
650 if it wants to, so we have to unmark the async flag so we
651 will know if it does a second async reply */
652 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
654 status = pvfs_open(ntvfs, req, io);
655 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
656 /* the 2nd try also replied async, so we don't send
661 /* re-mark it async, just in case someone up the chain does
663 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
665 /* send the reply up the chain */
666 req->async_states->status = status;
667 req->async_states->send_fn(req);
672 special handling for openx DENY_DOS semantics
674 This function attempts a reference open using an existing handle. If its allowed,
675 then it returns NT_STATUS_OK, otherwise it returns any other code and normal
676 open processing continues.
678 static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
679 struct smbsrv_request *req, union smb_open *io,
680 struct pvfs_file *f, struct odb_lock *lck)
682 struct pvfs_state *pvfs = ntvfs->private_data;
683 struct pvfs_file *f2;
684 struct pvfs_filename *name;
686 /* search for an existing open with the right parameters. Note
687 the magic ntcreatex options flag, which is set in the
688 generic mapping code. This might look ugly, but its
689 actually pretty much now w2k does it internally as well.
691 If you look at the BASE-DENYDOS test you will see that a
692 DENY_DOS is a very special case, and in the right
693 circumstances you actually get the _same_ handle back
694 twice, rather than a new handle.
696 for (f2=pvfs->open_files;f2;f2=f2->next) {
698 f2->session == req->session &&
699 f2->smbpid == req->smbpid &&
700 (f2->handle->create_options &
701 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
702 NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) &&
703 (f2->access_mask & SEC_FILE_WRITE_DATA) &&
704 StrCaseCmp(f2->handle->name->original_name,
705 io->generic.in.fname)==0) {
711 return NT_STATUS_SHARING_VIOLATION;
714 /* quite an insane set of semantics ... */
715 if (is_exe_filename(io->generic.in.fname) &&
716 (f2->handle->create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) {
717 return NT_STATUS_SHARING_VIOLATION;
721 setup a reference to the existing handle
723 talloc_free(f->handle);
724 f->handle = talloc_reference(f, f2->handle);
728 name = f->handle->name;
730 io->generic.out.oplock_level = OPLOCK_NONE;
731 io->generic.out.fnum = f->fnum;
732 io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
733 io->generic.out.create_time = name->dos.create_time;
734 io->generic.out.access_time = name->dos.access_time;
735 io->generic.out.write_time = name->dos.write_time;
736 io->generic.out.change_time = name->dos.change_time;
737 io->generic.out.attrib = name->dos.attrib;
738 io->generic.out.alloc_size = name->dos.alloc_size;
739 io->generic.out.size = name->st.st_size;
740 io->generic.out.file_type = FILE_TYPE_DISK;
741 io->generic.out.ipc_state = 0;
742 io->generic.out.is_directory = 0;
744 talloc_steal(f->pvfs, f);
752 setup for a open retry after a sharing violation
754 static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
755 struct smbsrv_request *req,
758 struct odb_lock *lck)
760 struct pvfs_state *pvfs = ntvfs->private_data;
761 struct pvfs_open_retry *r;
763 struct timeval end_time;
765 if (io->generic.in.create_options &
766 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
767 /* see if we can satisfy the request using the special DENY_DOS
769 status = pvfs_open_deny_dos(ntvfs, req, io, f, lck);
770 if (NT_STATUS_IS_OK(status)) {
775 r = talloc_p(req, struct pvfs_open_retry);
777 return NT_STATUS_NO_MEMORY;
783 r->odb_locking_key = data_blob_talloc(r,
784 f->handle->odb_locking_key.data,
785 f->handle->odb_locking_key.length);
787 end_time = timeval_add(&req->request_time, 0, pvfs->sharing_violation_delay);
789 /* setup a pending lock */
790 status = odb_open_file_pending(lck, r);
791 if (!NT_STATUS_IS_OK(status)) {
798 talloc_set_destructor(r, pvfs_retry_destructor);
800 r->wait_handle = pvfs_wait_message(pvfs, req, MSG_PVFS_RETRY_OPEN, end_time,
802 if (r->wait_handle == NULL) {
803 return NT_STATUS_NO_MEMORY;
806 talloc_steal(pvfs, r);
814 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
815 struct smbsrv_request *req, union smb_open *io)
817 struct pvfs_state *pvfs = ntvfs->private_data;
819 struct pvfs_filename *name;
823 struct odb_lock *lck;
824 uint32_t create_options;
825 uint32_t share_access;
826 uint32_t access_mask;
829 /* use the generic mapping code to avoid implementing all the
830 different open calls. */
831 if (io->generic.level != RAW_OPEN_GENERIC &&
832 io->generic.level != RAW_OPEN_NTTRANS_CREATE) {
833 return ntvfs_map_open(req, io, ntvfs);
836 /* resolve the cifs name to a posix name */
837 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
838 PVFS_RESOLVE_STREAMS, &name);
839 if (!NT_STATUS_IS_OK(status)) {
843 /* directory opens are handled separately */
844 if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
845 (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
846 return pvfs_open_directory(pvfs, req, name, io);
849 create_options = io->generic.in.create_options;
850 share_access = io->generic.in.share_access;
851 access_mask = io->generic.in.access_mask;
853 /* certain create options are not allowed */
854 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
855 !(access_mask & SEC_STD_DELETE)) {
856 return NT_STATUS_INVALID_PARAMETER;
859 switch (io->generic.in.open_disposition) {
860 case NTCREATEX_DISP_SUPERSEDE:
864 case NTCREATEX_DISP_OVERWRITE_IF:
868 case NTCREATEX_DISP_OPEN:
869 if (!name->stream_exists) {
870 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
875 case NTCREATEX_DISP_OVERWRITE:
876 if (!name->stream_exists) {
877 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
882 case NTCREATEX_DISP_CREATE:
883 if (name->stream_exists) {
884 return NT_STATUS_OBJECT_NAME_COLLISION;
889 case NTCREATEX_DISP_OPEN_IF:
894 return NT_STATUS_INVALID_PARAMETER;
897 if (io->generic.in.file_attr & FILE_ATTRIBUTE_DIRECTORY) {
898 return NT_STATUS_INVALID_PARAMETER;
901 /* handle creating a new file separately */
903 status = pvfs_create_file(pvfs, req, name, io);
904 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
908 /* we've hit a race - the file was created during this call */
909 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
913 /* try re-resolving the name */
914 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
915 if (!NT_STATUS_IS_OK(status)) {
918 /* fall through to a normal open */
921 if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
922 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
923 return NT_STATUS_CANNOT_DELETE;
926 /* check the security descriptor */
927 status = pvfs_access_check(pvfs, req, name, &access_mask);
928 if (!NT_STATUS_IS_OK(status)) {
932 f = talloc_p(req, struct pvfs_file);
934 return NT_STATUS_NO_MEMORY;
937 f->handle = talloc_p(f, struct pvfs_file_handle);
938 if (f->handle == NULL) {
939 return NT_STATUS_NO_MEMORY;
942 /* allocate a fnum */
943 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
945 return NT_STATUS_TOO_MANY_OPENED_FILES;
949 f->session = req->session;
950 f->smbpid = req->smbpid;
952 f->pending_list = NULL;
954 f->share_access = io->generic.in.share_access;
955 f->access_mask = access_mask;
956 f->impersonation = io->generic.in.impersonation;
958 f->handle->pvfs = pvfs;
960 f->handle->name = talloc_steal(f->handle, name);
961 f->handle->create_options = io->generic.in.create_options;
962 f->handle->seek_offset = 0;
963 f->handle->position = 0;
965 f->handle->have_opendb_entry = False;
966 f->handle->sticky_write_time = False;
968 /* form the lock context used for byte range locking and
970 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
971 if (!NT_STATUS_IS_OK(status)) {
972 idr_remove(pvfs->idtree_fnum, f->fnum);
976 status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
977 if (!NT_STATUS_IS_OK(status)) {
978 idr_remove(pvfs->idtree_fnum, f->fnum);
982 /* get a lock on this file before the actual open */
983 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
985 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
987 /* we were supposed to do a blocking lock, so something
989 idr_remove(pvfs->idtree_fnum, fnum);
990 return NT_STATUS_INTERNAL_DB_CORRUPTION;
993 DLIST_ADD(pvfs->open_files, f);
995 /* setup a destructor to avoid file descriptor leaks on
996 abnormal termination */
997 talloc_set_destructor(f, pvfs_fnum_destructor);
998 talloc_set_destructor(f->handle, pvfs_handle_destructor);
1001 /* see if we are allowed to open at the same time as existing opens */
1002 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
1003 share_access, create_options, access_mask);
1005 /* on a sharing violation we need to retry when the file is closed by
1006 the other user, or after 1 second */
1007 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) &&
1008 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
1009 return pvfs_open_setup_retry(ntvfs, req, io, f, lck);
1012 if (!NT_STATUS_IS_OK(status)) {
1017 f->handle->have_opendb_entry = True;
1019 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
1025 /* do the actual open */
1026 fd = open(f->handle->name->full_name, flags);
1029 return pvfs_map_errno(f->pvfs, errno);
1034 stream_existed = name->stream_exists;
1036 /* if this was a stream create then create the stream as well */
1037 if (!name->stream_exists) {
1038 status = pvfs_stream_create(pvfs, f->handle->name, fd);
1039 if (!NT_STATUS_IS_OK(status)) {
1045 /* re-resolve the open fd */
1046 status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name);
1047 if (!NT_STATUS_IS_OK(status)) {
1052 if (f->handle->name->stream_id == 0 &&
1053 (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE ||
1054 io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) {
1055 /* for overwrite we need to replace file permissions */
1056 uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
1057 mode_t mode = pvfs_fileperms(pvfs, attrib);
1058 if (fchmod(fd, mode) == -1) {
1060 return pvfs_map_errno(pvfs, errno);
1062 name->dos.attrib = attrib;
1063 status = pvfs_dosattrib_save(pvfs, name, fd);
1064 if (!NT_STATUS_IS_OK(status)) {
1072 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
1073 io->generic.out.oplock_level = OPLOCK_EXCLUSIVE;
1075 io->generic.out.oplock_level = OPLOCK_NONE;
1077 io->generic.out.fnum = f->fnum;
1078 io->generic.out.create_action = stream_existed?
1079 NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED;
1080 io->generic.out.create_time = name->dos.create_time;
1081 io->generic.out.access_time = name->dos.access_time;
1082 io->generic.out.write_time = name->dos.write_time;
1083 io->generic.out.change_time = name->dos.change_time;
1084 io->generic.out.attrib = name->dos.attrib;
1085 io->generic.out.alloc_size = name->dos.alloc_size;
1086 io->generic.out.size = name->st.st_size;
1087 io->generic.out.file_type = FILE_TYPE_DISK;
1088 io->generic.out.ipc_state = 0;
1089 io->generic.out.is_directory = 0;
1091 /* success - keep the file handle */
1092 talloc_steal(f->pvfs, f);
1094 return NT_STATUS_OK;
1101 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
1102 struct smbsrv_request *req, union smb_close *io)
1104 struct pvfs_state *pvfs = ntvfs->private_data;
1105 struct pvfs_file *f;
1106 struct utimbuf unix_times;
1108 if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
1109 return NT_STATUS_UNSUCCESSFUL;
1112 if (io->generic.level != RAW_CLOSE_CLOSE) {
1113 return ntvfs_map_close(req, io, ntvfs);
1116 f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
1118 return NT_STATUS_INVALID_HANDLE;
1121 if (!null_time(io->close.in.write_time)) {
1122 unix_times.actime = 0;
1123 unix_times.modtime = io->close.in.write_time;
1124 utime(f->handle->name->full_name, &unix_times);
1125 } else if (f->handle->sticky_write_time) {
1126 unix_times.actime = 0;
1127 unix_times.modtime = nt_time_to_unix(f->handle->name->dos.write_time);
1128 utime(f->handle->name->full_name, &unix_times);
1133 return NT_STATUS_OK;
1138 logoff - close all file descriptors open by a vuid
1140 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
1141 struct smbsrv_request *req)
1143 struct pvfs_state *pvfs = ntvfs->private_data;
1144 struct pvfs_file *f, *next;
1146 for (f=pvfs->open_files;f;f=next) {
1148 if (f->session == req->session) {
1153 return NT_STATUS_OK;
1158 exit - close files for the current pid
1160 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
1161 struct smbsrv_request *req)
1163 struct pvfs_state *pvfs = ntvfs->private_data;
1164 struct pvfs_file *f, *next;
1166 for (f=pvfs->open_files;f;f=next) {
1168 if (f->smbpid == req->smbpid) {
1173 return NT_STATUS_OK;
1178 change the create options on an already open file
1180 NTSTATUS pvfs_change_create_options(struct pvfs_state *pvfs,
1181 struct smbsrv_request *req,
1182 struct pvfs_file *f, uint32_t create_options)
1184 struct odb_lock *lck;
1187 if (f->handle->create_options == create_options) {
1188 return NT_STATUS_OK;
1191 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
1192 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
1193 return NT_STATUS_CANNOT_DELETE;
1196 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1198 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1201 status = odb_set_create_options(lck, f->handle, create_options);
1202 if (NT_STATUS_IS_OK(status)) {
1203 f->handle->create_options = create_options;
1211 determine if a file can be deleted, or if it is prevented by an
1214 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs,
1215 struct smbsrv_request *req,
1216 struct pvfs_filename *name)
1221 status = pvfs_locking_key(name, name, &key);
1222 if (!NT_STATUS_IS_OK(status)) {
1223 return NT_STATUS_NO_MEMORY;
1226 status = odb_can_open(pvfs->odb_context, &key,
1227 NTCREATEX_SHARE_ACCESS_READ |
1228 NTCREATEX_SHARE_ACCESS_WRITE |
1229 NTCREATEX_SHARE_ACCESS_DELETE,
1230 NTCREATEX_OPTIONS_DELETE_ON_CLOSE,
1233 if (NT_STATUS_IS_OK(status)) {
1234 status = pvfs_access_check_simple(pvfs, req, name, SEC_STD_DELETE);
1241 determine if a file can be renamed, or if it is prevented by an
1244 NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs, struct pvfs_filename *name)
1249 status = pvfs_locking_key(name, name, &key);
1250 if (!NT_STATUS_IS_OK(status)) {
1251 return NT_STATUS_NO_MEMORY;
1254 status = odb_can_open(pvfs->odb_context, &key,
1255 NTCREATEX_SHARE_ACCESS_READ |
1256 NTCREATEX_SHARE_ACCESS_WRITE,