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"
31 create file handles with convenient numbers for sniffers
33 #define PVFS_MIN_FILE_FNUM 0x100
34 #define PVFS_MIN_NEW_FNUM 0x200
35 #define PVFS_MIN_DIR_FNUM 0x300
38 find open file handle given fnum
40 struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
41 struct smbsrv_request *req, uint16_t fnum)
45 f = idr_find(pvfs->idtree_fnum, fnum);
50 if (f->fnum != fnum) {
51 smb_panic("pvfs_find_fd: idtree_fnum corruption\n");
54 if (req->session != f->session) {
55 DEBUG(2,("pvfs_find_fd: attempt to use wrong session for fnum %d\n",
65 cleanup a open directory handle
67 static int pvfs_dir_handle_destructor(void *p)
69 struct pvfs_file_handle *h = p;
71 if (h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
72 if (rmdir(h->name->full_name) != 0) {
73 DEBUG(0,("pvfs_close: failed to rmdir '%s' - %s\n",
74 h->name->full_name, strerror(errno)));
82 cleanup a open directory fnum
84 static int pvfs_dir_fnum_destructor(void *p)
86 struct pvfs_file *f = p;
87 DLIST_REMOVE(f->pvfs->open_files, f);
88 idr_remove(f->pvfs->idtree_fnum, f->fnum);
96 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
97 struct smbsrv_request *req,
98 struct pvfs_filename *name,
104 uint32_t create_action;
106 if (name->stream_name) {
107 return NT_STATUS_NOT_A_DIRECTORY;
110 /* if the client says it must be a directory, and it isn't,
112 if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
113 return NT_STATUS_NOT_A_DIRECTORY;
116 switch (io->generic.in.open_disposition) {
117 case NTCREATEX_DISP_OPEN_IF:
120 case NTCREATEX_DISP_OPEN:
122 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
126 case NTCREATEX_DISP_CREATE:
128 return NT_STATUS_OBJECT_NAME_COLLISION;
132 case NTCREATEX_DISP_OVERWRITE_IF:
133 case NTCREATEX_DISP_OVERWRITE:
134 case NTCREATEX_DISP_SUPERSEDE:
136 return NT_STATUS_INVALID_PARAMETER;
139 f = talloc_p(req, struct pvfs_file);
141 return NT_STATUS_NO_MEMORY;
144 f->handle = talloc_p(f, struct pvfs_file_handle);
145 if (f->handle == NULL) {
146 return NT_STATUS_NO_MEMORY;
149 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_DIR_FNUM, UINT16_MAX);
151 return NT_STATUS_TOO_MANY_OPENED_FILES;
155 f->session = req->session;
156 f->smbpid = req->smbpid;
158 f->pending_list = NULL;
160 f->share_access = io->generic.in.share_access;
161 f->impersonation = io->generic.in.impersonation;
163 f->handle->pvfs = pvfs;
164 f->handle->name = talloc_steal(f->handle, name);
166 f->handle->odb_locking_key = data_blob(NULL, 0);
167 f->handle->brl_locking_key = data_blob(NULL, 0);
168 f->handle->create_options = io->generic.in.create_options;
169 f->handle->seek_offset = 0;
170 f->handle->position = 0;
173 DLIST_ADD(pvfs->open_files, f);
175 /* TODO: should we check in the opendb? Do directory opens
176 follow the share_access rules? */
178 /* setup destructors to avoid leaks on abnormal termination */
179 talloc_set_destructor(f->handle, pvfs_dir_handle_destructor);
180 talloc_set_destructor(f, pvfs_dir_fnum_destructor);
183 uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
184 mode_t mode = pvfs_fileperms(pvfs, attrib);
185 if (mkdir(name->full_name, mode) == -1) {
186 return pvfs_map_errno(pvfs,errno);
188 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
189 if (!NT_STATUS_IS_OK(status)) {
192 create_action = NTCREATEX_ACTION_CREATED;
194 create_action = NTCREATEX_ACTION_EXISTED;
198 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
201 /* the open succeeded, keep this handle permanently */
202 talloc_steal(pvfs, f);
204 io->generic.out.oplock_level = NO_OPLOCK;
205 io->generic.out.fnum = f->fnum;
206 io->generic.out.create_action = create_action;
207 io->generic.out.create_time = name->dos.create_time;
208 io->generic.out.access_time = name->dos.access_time;
209 io->generic.out.write_time = name->dos.write_time;
210 io->generic.out.change_time = name->dos.change_time;
211 io->generic.out.attrib = name->dos.attrib;
212 io->generic.out.alloc_size = name->dos.alloc_size;
213 io->generic.out.size = name->st.st_size;
214 io->generic.out.file_type = FILE_TYPE_DISK;
215 io->generic.out.ipc_state = 0;
216 io->generic.out.is_directory = 1;
222 destroy a struct pvfs_file_handle
224 static int pvfs_handle_destructor(void *p)
226 struct pvfs_file_handle *h = p;
229 if (close(h->fd) != 0) {
230 DEBUG(0,("pvfs_handle_destructor: close(%d) failed for %s - %s\n",
231 h->fd, h->name->full_name, strerror(errno)));
236 if (h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
237 if (unlink(h->name->full_name) != 0) {
238 DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n",
239 h->name->full_name, strerror(errno)));
243 if (h->have_opendb_entry) {
244 struct odb_lock *lck;
247 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
249 DEBUG(0,("Unable to lock opendb for close\n"));
253 status = odb_close_file(lck, h);
254 if (!NT_STATUS_IS_OK(status)) {
255 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
256 h->name->full_name, nt_errstr(status)));
267 destroy a struct pvfs_file
269 static int pvfs_fnum_destructor(void *p)
271 struct pvfs_file *f = p;
273 DLIST_REMOVE(f->pvfs->open_files, f);
274 pvfs_lock_close(f->pvfs, f);
275 idr_remove(f->pvfs->idtree_fnum, f->fnum);
282 form the lock context used for opendb locking. Note that we must
283 zero here to take account of possible padding on some architectures
285 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
286 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
292 ZERO_STRUCT(lock_context);
294 lock_context.device = name->st.st_dev;
295 lock_context.inode = name->st.st_ino;
297 *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
298 if (key->data == NULL) {
299 return NT_STATUS_NO_MEMORY;
306 form the lock context used for byte range locking. This is separate
307 from the locking key used for opendb locking as it needs to take
308 account of file streams (each stream is a separate byte range
311 static NTSTATUS pvfs_brl_locking_key(struct pvfs_filename *name,
312 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
316 status = pvfs_locking_key(name, mem_ctx, &odb_key);
317 if (!NT_STATUS_IS_OK(status)) {
320 if (name->stream_name == NULL) {
324 *key = data_blob_talloc(mem_ctx, NULL,
325 odb_key.length + strlen(name->stream_name) + 1);
326 if (key->data == NULL) {
327 return NT_STATUS_NO_MEMORY;
329 memcpy(key->data, odb_key.data, odb_key.length);
330 memcpy(key->data + odb_key.length,
331 name->stream_name, strlen(name->stream_name)+1);
332 data_blob_free(&odb_key);
340 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
341 struct smbsrv_request *req,
342 struct pvfs_filename *name,
348 struct odb_lock *lck;
349 uint32_t create_options = io->generic.in.create_options;
350 uint32_t share_access = io->generic.in.share_access;
351 uint32_t access_mask = io->generic.in.access_mask;
355 if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
356 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
357 return NT_STATUS_CANNOT_DELETE;
360 if (access_mask & SEC_RIGHT_MAXIMUM_ALLOWED) {
361 access_mask = GENERIC_RIGHTS_FILE_READ | GENERIC_RIGHTS_FILE_WRITE;
364 if (access_mask & SA_RIGHT_FILE_WRITE_APPEND) {
370 f = talloc_p(req, struct pvfs_file);
372 return NT_STATUS_NO_MEMORY;
375 f->handle = talloc_p(f, struct pvfs_file_handle);
376 if (f->handle == NULL) {
377 return NT_STATUS_NO_MEMORY;
380 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_NEW_FNUM, UINT16_MAX);
382 return NT_STATUS_TOO_MANY_OPENED_FILES;
385 attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
386 mode = pvfs_fileperms(pvfs, attrib);
388 /* create the file */
389 fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
391 idr_remove(pvfs->idtree_fnum, fnum);
392 return pvfs_map_errno(pvfs, errno);
395 /* if this was a stream create then create the stream as well */
396 if (name->stream_name) {
397 status = pvfs_stream_create(pvfs, name, fd);
398 if (!NT_STATUS_IS_OK(status)) {
399 idr_remove(pvfs->idtree_fnum, fnum);
405 /* re-resolve the open fd */
406 status = pvfs_resolve_name_fd(pvfs, fd, name);
407 if (!NT_STATUS_IS_OK(status)) {
408 idr_remove(pvfs->idtree_fnum, fnum);
413 name->dos.attrib = attrib;
414 status = pvfs_dosattrib_save(pvfs, name, fd);
415 if (!NT_STATUS_IS_OK(status)) {
416 idr_remove(pvfs->idtree_fnum, fnum);
421 /* form the lock context used for byte range locking and
423 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
424 if (!NT_STATUS_IS_OK(status)) {
425 idr_remove(pvfs->idtree_fnum, fnum);
430 status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
431 if (!NT_STATUS_IS_OK(status)) {
432 idr_remove(pvfs->idtree_fnum, fnum);
437 /* grab a lock on the open file record */
438 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
440 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
442 /* we were supposed to do a blocking lock, so something
444 idr_remove(pvfs->idtree_fnum, fnum);
446 return NT_STATUS_INTERNAL_DB_CORRUPTION;
449 status = odb_open_file(lck, f->handle, name->stream_id,
450 share_access, create_options, access_mask);
452 if (!NT_STATUS_IS_OK(status)) {
453 /* bad news, we must have hit a race */
454 idr_remove(pvfs->idtree_fnum, fnum);
460 f->session = req->session;
461 f->smbpid = req->smbpid;
463 f->pending_list = NULL;
465 f->share_access = io->generic.in.share_access;
466 f->access_mask = access_mask;
467 f->impersonation = io->generic.in.impersonation;
469 f->handle->pvfs = pvfs;
470 f->handle->name = talloc_steal(f->handle, name);
472 f->handle->create_options = io->generic.in.create_options;
473 f->handle->seek_offset = 0;
474 f->handle->position = 0;
476 f->handle->have_opendb_entry = True;
478 DLIST_ADD(pvfs->open_files, f);
480 /* setup a destructor to avoid file descriptor leaks on
481 abnormal termination */
482 talloc_set_destructor(f, pvfs_fnum_destructor);
483 talloc_set_destructor(f->handle, pvfs_handle_destructor);
485 io->generic.out.oplock_level = NO_OPLOCK;
486 io->generic.out.fnum = f->fnum;
487 io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
488 io->generic.out.create_time = name->dos.create_time;
489 io->generic.out.access_time = name->dos.access_time;
490 io->generic.out.write_time = name->dos.write_time;
491 io->generic.out.change_time = name->dos.change_time;
492 io->generic.out.attrib = name->dos.attrib;
493 io->generic.out.alloc_size = name->dos.alloc_size;
494 io->generic.out.size = name->st.st_size;
495 io->generic.out.file_type = FILE_TYPE_DISK;
496 io->generic.out.ipc_state = 0;
497 io->generic.out.is_directory = 0;
499 /* success - keep the file handle */
500 talloc_steal(pvfs, f);
507 state of a pending open retry
509 struct pvfs_open_retry {
510 struct ntvfs_module_context *ntvfs;
511 struct smbsrv_request *req;
514 DATA_BLOB odb_locking_key;
517 /* destroy a pending open request */
518 static int pvfs_retry_destructor(void *ptr)
520 struct pvfs_open_retry *r = ptr;
521 struct pvfs_state *pvfs = r->ntvfs->private_data;
522 if (r->odb_locking_key.data) {
523 struct odb_lock *lck;
524 lck = odb_lock(r->req, pvfs->odb_context, &r->odb_locking_key);
526 odb_remove_pending(lck, r);
536 static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
538 struct pvfs_open_retry *r = private;
539 struct ntvfs_module_context *ntvfs = r->ntvfs;
540 struct smbsrv_request *req = r->req;
541 union smb_open *io = r->io;
544 /* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
545 just a bug in their server, but we better do the same */
546 if (reason == PVFS_WAIT_CANCEL) {
550 talloc_free(r->wait_handle);
552 if (reason == PVFS_WAIT_TIMEOUT) {
553 /* if it timed out, then give the failure
556 req->async_states->status = NT_STATUS_SHARING_VIOLATION;
557 req->async_states->send_fn(req);
561 /* the pending odb entry is already removed. We use a null locking
562 key to indicate this */
563 data_blob_free(&r->odb_locking_key);
566 /* try the open again, which could trigger another retry setup
567 if it wants to, so we have to unmark the async flag so we
568 will know if it does a second async reply */
569 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
571 status = pvfs_open(ntvfs, req, io);
572 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
573 /* the 2nd try also replied async, so we don't send
578 /* re-mark it async, just in case someone up the chain does
580 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
582 /* send the reply up the chain */
583 req->async_states->status = status;
584 req->async_states->send_fn(req);
589 special handling for openx DENY_DOS semantics
591 This function attempts a reference open using an existing handle. If its allowed,
592 then it returns NT_STATUS_OK, otherwise it returns any other code and normal
593 open processing continues.
595 static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
596 struct smbsrv_request *req, union smb_open *io,
597 struct pvfs_file *f, struct odb_lock *lck)
599 struct pvfs_state *pvfs = ntvfs->private_data;
600 struct pvfs_file *f2;
601 struct pvfs_filename *name;
603 /* search for an existing open with the right parameters. Note
604 the magic ntcreatex options flag, which is set in the
605 generic mapping code. This might look ugly, but its
606 actually pretty much now w2k does it internally as well.
608 If you look at the BASE-DENYDOS test you will see that a
609 DENY_DOS is a very special case, and in the right
610 circumstances you actually get the _same_ handle back
611 twice, rather than a new handle.
613 for (f2=pvfs->open_files;f2;f2=f2->next) {
615 f2->session == req->session &&
616 f2->smbpid == req->smbpid &&
617 (f2->handle->create_options &
618 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
619 NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) &&
620 (f2->access_mask & SA_RIGHT_FILE_WRITE_DATA) &&
621 StrCaseCmp(f2->handle->name->original_name,
622 io->generic.in.fname)==0) {
628 return NT_STATUS_SHARING_VIOLATION;
631 /* quite an insane set of semantics ... */
632 if (is_exe_filename(io->generic.in.fname) &&
633 (f2->handle->create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) {
634 return NT_STATUS_SHARING_VIOLATION;
638 setup a reference to the existing handle
640 talloc_free(f->handle);
641 f->handle = talloc_reference(f, f2->handle);
645 name = f->handle->name;
647 io->generic.out.oplock_level = NO_OPLOCK;
648 io->generic.out.fnum = f->fnum;
649 io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
650 io->generic.out.create_time = name->dos.create_time;
651 io->generic.out.access_time = name->dos.access_time;
652 io->generic.out.write_time = name->dos.write_time;
653 io->generic.out.change_time = name->dos.change_time;
654 io->generic.out.attrib = name->dos.attrib;
655 io->generic.out.alloc_size = name->dos.alloc_size;
656 io->generic.out.size = name->st.st_size;
657 io->generic.out.file_type = FILE_TYPE_DISK;
658 io->generic.out.ipc_state = 0;
659 io->generic.out.is_directory = 0;
661 talloc_steal(f->pvfs, f);
669 setup for a open retry after a sharing violation
671 static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
672 struct smbsrv_request *req,
675 struct odb_lock *lck)
677 struct pvfs_state *pvfs = ntvfs->private_data;
678 struct pvfs_open_retry *r;
680 struct timeval end_time;
682 if (io->generic.in.create_options &
683 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
684 /* see if we can satisfy the request using the special DENY_DOS
686 status = pvfs_open_deny_dos(ntvfs, req, io, f, lck);
687 if (NT_STATUS_IS_OK(status)) {
692 r = talloc_p(req, struct pvfs_open_retry);
694 return NT_STATUS_NO_MEMORY;
700 r->odb_locking_key = data_blob_talloc(r,
701 f->handle->odb_locking_key.data,
702 f->handle->odb_locking_key.length);
704 end_time = timeval_add(&req->request_time, 0, pvfs->sharing_violation_delay);
706 /* setup a pending lock */
707 status = odb_open_file_pending(lck, r);
708 if (!NT_STATUS_IS_OK(status)) {
715 talloc_set_destructor(r, pvfs_retry_destructor);
717 r->wait_handle = pvfs_wait_message(pvfs, req, MSG_PVFS_RETRY_OPEN, end_time,
719 if (r->wait_handle == NULL) {
720 return NT_STATUS_NO_MEMORY;
723 talloc_steal(pvfs, r);
729 special handling for t2open
731 static NTSTATUS pvfs_open_t2open(struct ntvfs_module_context *ntvfs,
732 struct smbsrv_request *req, union smb_open *io)
734 struct pvfs_state *pvfs = ntvfs->private_data;
735 struct pvfs_filename *name;
738 status = pvfs_resolve_name(pvfs, req, io->t2open.in.fname, 0, &name);
739 if (!NT_STATUS_IS_OK(status)) {
743 if (io->t2open.in.open_func & OPENX_OPEN_FUNC_CREATE) {
744 if (!name->stream_exists) return NT_STATUS_ACCESS_DENIED;
746 if (io->t2open.in.open_func & OPENX_OPEN_FUNC_TRUNC) {
747 if (name->stream_exists) return NT_STATUS_ACCESS_DENIED;
748 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
750 if ((io->t2open.in.open_func & 0xF) == OPENX_OPEN_FUNC_FAIL) {
751 if (!name->stream_exists) return NT_STATUS_ACCESS_DENIED;
752 return NT_STATUS_OBJECT_NAME_COLLISION;
757 return ntvfs_map_open(req, io, ntvfs);
763 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
764 struct smbsrv_request *req, union smb_open *io)
766 struct pvfs_state *pvfs = ntvfs->private_data;
768 struct pvfs_filename *name;
772 struct odb_lock *lck;
773 uint32_t create_options;
774 uint32_t share_access;
775 uint32_t access_mask;
778 if (io->generic.level == RAW_OPEN_T2OPEN) {
779 return pvfs_open_t2open(ntvfs, req, io);
782 /* use the generic mapping code to avoid implementing all the
783 different open calls. */
784 if (io->generic.level != RAW_OPEN_GENERIC) {
785 return ntvfs_map_open(req, io, ntvfs);
788 /* resolve the cifs name to a posix name */
789 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
790 PVFS_RESOLVE_STREAMS, &name);
791 if (!NT_STATUS_IS_OK(status)) {
795 /* directory opens are handled separately */
796 if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
797 (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
798 return pvfs_open_directory(pvfs, req, name, io);
801 create_options = io->generic.in.create_options;
802 share_access = io->generic.in.share_access;
803 access_mask = io->generic.in.access_mask;
805 if (access_mask & SEC_RIGHT_MAXIMUM_ALLOWED) {
806 if (name->exists && (name->dos.attrib & FILE_ATTRIBUTE_READONLY)) {
807 access_mask = GENERIC_RIGHTS_FILE_READ;
809 access_mask = GENERIC_RIGHTS_FILE_READ | GENERIC_RIGHTS_FILE_WRITE;
813 /* certain create options are not allowed */
814 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
815 !(access_mask & STD_RIGHT_DELETE_ACCESS)) {
816 return NT_STATUS_INVALID_PARAMETER;
819 switch (io->generic.in.open_disposition) {
820 case NTCREATEX_DISP_SUPERSEDE:
824 case NTCREATEX_DISP_OVERWRITE_IF:
828 case NTCREATEX_DISP_OPEN:
829 if (!name->stream_exists) {
830 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
835 case NTCREATEX_DISP_OVERWRITE:
836 if (!name->stream_exists) {
837 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
842 case NTCREATEX_DISP_CREATE:
843 if (name->stream_exists) {
844 return NT_STATUS_OBJECT_NAME_COLLISION;
849 case NTCREATEX_DISP_OPEN_IF:
854 return NT_STATUS_INVALID_PARAMETER;
857 if (access_mask & SA_RIGHT_FILE_WRITE_APPEND) {
863 if (io->generic.in.file_attr & FILE_ATTRIBUTE_DIRECTORY) {
864 return NT_STATUS_INVALID_PARAMETER;
867 /* handle creating a new file separately */
869 status = pvfs_create_file(pvfs, req, name, io);
870 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
874 /* we've hit a race - the file was created during this call */
875 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
879 /* try re-resolving the name */
880 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
881 if (!NT_STATUS_IS_OK(status)) {
884 /* fall through to a normal open */
887 if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
888 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
889 return NT_STATUS_CANNOT_DELETE;
892 f = talloc_p(req, struct pvfs_file);
894 return NT_STATUS_NO_MEMORY;
897 f->handle = talloc_p(f, struct pvfs_file_handle);
898 if (f->handle == NULL) {
899 return NT_STATUS_NO_MEMORY;
902 /* allocate a fnum */
903 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
905 return NT_STATUS_TOO_MANY_OPENED_FILES;
909 f->session = req->session;
910 f->smbpid = req->smbpid;
912 f->pending_list = NULL;
914 f->share_access = io->generic.in.share_access;
915 f->access_mask = access_mask;
916 f->impersonation = io->generic.in.impersonation;
918 f->handle->pvfs = pvfs;
920 f->handle->name = talloc_steal(f->handle, name);
921 f->handle->create_options = io->generic.in.create_options;
922 f->handle->seek_offset = 0;
923 f->handle->position = 0;
924 f->handle->have_opendb_entry = False;
926 /* form the lock context used for byte range locking and
928 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
929 if (!NT_STATUS_IS_OK(status)) {
930 idr_remove(pvfs->idtree_fnum, f->fnum);
934 status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
935 if (!NT_STATUS_IS_OK(status)) {
936 idr_remove(pvfs->idtree_fnum, f->fnum);
940 /* get a lock on this file before the actual open */
941 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
943 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
945 /* we were supposed to do a blocking lock, so something
947 idr_remove(pvfs->idtree_fnum, fnum);
948 return NT_STATUS_INTERNAL_DB_CORRUPTION;
951 DLIST_ADD(pvfs->open_files, f);
953 /* setup a destructor to avoid file descriptor leaks on
954 abnormal termination */
955 talloc_set_destructor(f, pvfs_fnum_destructor);
956 talloc_set_destructor(f->handle, pvfs_handle_destructor);
959 /* see if we are allowed to open at the same time as existing opens */
960 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
961 share_access, create_options, access_mask);
963 /* on a sharing violation we need to retry when the file is closed by
964 the other user, or after 1 second */
965 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) &&
966 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
967 return pvfs_open_setup_retry(ntvfs, req, io, f, lck);
970 if (!NT_STATUS_IS_OK(status)) {
975 f->handle->have_opendb_entry = True;
977 /* do the actual open */
978 fd = open(f->handle->name->full_name, flags);
981 return pvfs_map_errno(f->pvfs, errno);
986 stream_existed = name->stream_exists;
988 /* if this was a stream create then create the stream as well */
989 if (!name->stream_exists) {
990 status = pvfs_stream_create(pvfs, f->handle->name, fd);
991 if (!NT_STATUS_IS_OK(status)) {
997 /* re-resolve the open fd */
998 status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name);
999 if (!NT_STATUS_IS_OK(status)) {
1004 if (f->handle->name->stream_id == 0 &&
1005 (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE ||
1006 io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) {
1007 /* for overwrite we need to replace file permissions */
1008 uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
1009 mode_t mode = pvfs_fileperms(pvfs, attrib);
1010 if (fchmod(fd, mode) == -1) {
1012 return pvfs_map_errno(pvfs, errno);
1014 name->dos.attrib = attrib;
1015 status = pvfs_dosattrib_save(pvfs, name, fd);
1016 if (!NT_STATUS_IS_OK(status)) {
1024 io->generic.out.oplock_level = NO_OPLOCK;
1025 io->generic.out.fnum = f->fnum;
1026 io->generic.out.create_action = stream_existed?
1027 NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED;
1028 io->generic.out.create_time = name->dos.create_time;
1029 io->generic.out.access_time = name->dos.access_time;
1030 io->generic.out.write_time = name->dos.write_time;
1031 io->generic.out.change_time = name->dos.change_time;
1032 io->generic.out.attrib = name->dos.attrib;
1033 io->generic.out.alloc_size = name->dos.alloc_size;
1034 io->generic.out.size = name->st.st_size;
1035 io->generic.out.file_type = FILE_TYPE_DISK;
1036 io->generic.out.ipc_state = 0;
1037 io->generic.out.is_directory = 0;
1039 /* success - keep the file handle */
1040 talloc_steal(f->pvfs, f);
1042 return NT_STATUS_OK;
1049 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
1050 struct smbsrv_request *req, union smb_close *io)
1052 struct pvfs_state *pvfs = ntvfs->private_data;
1053 struct pvfs_file *f;
1054 struct utimbuf unix_times;
1056 if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
1057 return NT_STATUS_UNSUCCESSFUL;
1060 if (io->generic.level != RAW_CLOSE_CLOSE) {
1061 return ntvfs_map_close(req, io, ntvfs);
1064 f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
1066 return NT_STATUS_INVALID_HANDLE;
1069 if (!null_time(io->close.in.write_time)) {
1070 unix_times.actime = 0;
1071 unix_times.modtime = io->close.in.write_time;
1072 utime(f->handle->name->full_name, &unix_times);
1077 return NT_STATUS_OK;
1082 logoff - close all file descriptors open by a vuid
1084 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
1085 struct smbsrv_request *req)
1087 struct pvfs_state *pvfs = ntvfs->private_data;
1088 struct pvfs_file *f, *next;
1090 for (f=pvfs->open_files;f;f=next) {
1092 if (f->session == req->session) {
1097 return NT_STATUS_OK;
1102 exit - close files for the current pid
1104 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
1105 struct smbsrv_request *req)
1107 struct pvfs_state *pvfs = ntvfs->private_data;
1108 struct pvfs_file *f, *next;
1110 for (f=pvfs->open_files;f;f=next) {
1112 if (f->smbpid == req->smbpid) {
1117 return NT_STATUS_OK;
1122 change the create options on an already open file
1124 NTSTATUS pvfs_change_create_options(struct pvfs_state *pvfs,
1125 struct smbsrv_request *req,
1126 struct pvfs_file *f, uint32_t create_options)
1128 struct odb_lock *lck;
1131 if (f->handle->create_options == create_options) {
1132 return NT_STATUS_OK;
1135 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
1136 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
1137 return NT_STATUS_CANNOT_DELETE;
1140 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1142 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1145 status = odb_set_create_options(lck, f->handle, create_options);
1146 if (NT_STATUS_IS_OK(status)) {
1147 f->handle->create_options = create_options;
1155 determine if a file can be deleted, or if it is prevented by an
1158 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs, struct pvfs_filename *name)
1163 status = pvfs_locking_key(name, name, &key);
1164 if (!NT_STATUS_IS_OK(status)) {
1165 return NT_STATUS_NO_MEMORY;
1168 status = odb_can_open(pvfs->odb_context, &key,
1169 NTCREATEX_SHARE_ACCESS_READ |
1170 NTCREATEX_SHARE_ACCESS_WRITE |
1171 NTCREATEX_SHARE_ACCESS_DELETE,
1172 NTCREATEX_OPTIONS_DELETE_ON_CLOSE,
1173 STD_RIGHT_DELETE_ACCESS);
1179 determine if a file can be renamed, or if it is prevented by an
1182 NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs, struct pvfs_filename *name)
1187 status = pvfs_locking_key(name, name, &key);
1188 if (!NT_STATUS_IS_OK(status)) {
1189 return NT_STATUS_NO_MEMORY;
1192 status = odb_can_open(pvfs->odb_context, &key,
1193 NTCREATEX_SHARE_ACCESS_READ |
1194 NTCREATEX_SHARE_ACCESS_WRITE,
1196 STD_RIGHT_DELETE_ACCESS);