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"
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 && errno != ENOTEMPTY) {
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, SEC_STD_WRITE_DAC, &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(req, struct pvfs_file);
185 return NT_STATUS_NO_MEMORY;
188 f->handle = talloc(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);
202 status = pvfs_access_check_create(pvfs, req, name, &access_mask);
204 if (!NT_STATUS_IS_OK(status)) {
205 idr_remove(pvfs->idtree_fnum, fnum);
210 f->session = req->session;
211 f->smbpid = req->smbpid;
213 f->pending_list = NULL;
215 f->share_access = io->generic.in.share_access;
216 f->impersonation = io->generic.in.impersonation;
217 f->access_mask = access_mask;
219 f->handle->pvfs = pvfs;
220 f->handle->name = talloc_steal(f->handle, name);
222 f->handle->odb_locking_key = data_blob(NULL, 0);
223 f->handle->brl_locking_key = data_blob(NULL, 0);
224 f->handle->create_options = io->generic.in.create_options;
225 f->handle->seek_offset = 0;
226 f->handle->position = 0;
228 f->handle->sticky_write_time = False;
230 DLIST_ADD(pvfs->open_files, f);
232 /* TODO: should we check in the opendb? Do directory opens
233 follow the share_access rules? */
235 /* setup destructors to avoid leaks on abnormal termination */
236 talloc_set_destructor(f->handle, pvfs_dir_handle_destructor);
237 talloc_set_destructor(f, pvfs_dir_fnum_destructor);
240 uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
241 mode_t mode = pvfs_fileperms(pvfs, attrib);
242 if (mkdir(name->full_name, mode) == -1) {
243 idr_remove(pvfs->idtree_fnum, fnum);
244 return pvfs_map_errno(pvfs,errno);
247 pvfs_xattr_unlink_hook(pvfs, name->full_name);
249 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
250 if (!NT_STATUS_IS_OK(status)) {
254 status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, fnum, io);
255 if (!NT_STATUS_IS_OK(status)) {
259 create_action = NTCREATEX_ACTION_CREATED;
261 create_action = NTCREATEX_ACTION_EXISTED;
265 idr_remove(pvfs->idtree_fnum, fnum);
266 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
269 /* the open succeeded, keep this handle permanently */
270 talloc_steal(pvfs, f);
272 io->generic.out.oplock_level = OPLOCK_NONE;
273 io->generic.out.fnum = f->fnum;
274 io->generic.out.create_action = create_action;
275 io->generic.out.create_time = name->dos.create_time;
276 io->generic.out.access_time = name->dos.access_time;
277 io->generic.out.write_time = name->dos.write_time;
278 io->generic.out.change_time = name->dos.change_time;
279 io->generic.out.attrib = name->dos.attrib;
280 io->generic.out.alloc_size = name->dos.alloc_size;
281 io->generic.out.size = name->st.st_size;
282 io->generic.out.file_type = FILE_TYPE_DISK;
283 io->generic.out.ipc_state = 0;
284 io->generic.out.is_directory = 1;
289 idr_remove(pvfs->idtree_fnum, fnum);
290 rmdir(name->full_name);
295 destroy a struct pvfs_file_handle
297 static int pvfs_handle_destructor(void *p)
299 struct pvfs_file_handle *h = p;
301 /* the write time is no longer sticky */
302 if (h->sticky_write_time) {
304 status = pvfs_dosattrib_load(h->pvfs, h->name, h->fd);
305 if (NT_STATUS_IS_OK(status)) {
306 h->name->dos.flags &= ~XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME;
307 pvfs_dosattrib_save(h->pvfs, h->name, h->fd);
311 if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
312 h->name->stream_name) {
314 status = pvfs_stream_delete(h->pvfs, h->name, h->fd);
315 if (!NT_STATUS_IS_OK(status)) {
316 DEBUG(0,("Failed to delete stream '%s' on close of '%s'\n",
317 h->name->stream_name, h->name->full_name));
322 if (close(h->fd) != 0) {
323 DEBUG(0,("pvfs_handle_destructor: close(%d) failed for %s - %s\n",
324 h->fd, h->name->full_name, strerror(errno)));
329 if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
330 h->name->stream_name == NULL) {
332 status = pvfs_xattr_unlink_hook(h->pvfs, h->name->full_name);
333 if (!NT_STATUS_IS_OK(status)) {
334 DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
335 h->name->full_name, nt_errstr(status)));
337 if (unlink(h->name->full_name) != 0) {
338 DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n",
339 h->name->full_name, strerror(errno)));
343 if (h->have_opendb_entry) {
344 struct odb_lock *lck;
347 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
349 DEBUG(0,("Unable to lock opendb for close\n"));
353 status = odb_close_file(lck, h);
354 if (!NT_STATUS_IS_OK(status)) {
355 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
356 h->name->full_name, nt_errstr(status)));
367 destroy a struct pvfs_file
369 static int pvfs_fnum_destructor(void *p)
371 struct pvfs_file *f = p;
373 DLIST_REMOVE(f->pvfs->open_files, f);
374 pvfs_lock_close(f->pvfs, f);
375 idr_remove(f->pvfs->idtree_fnum, f->fnum);
382 form the lock context used for opendb locking. Note that we must
383 zero here to take account of possible padding on some architectures
385 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
386 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
392 ZERO_STRUCT(lock_context);
394 lock_context.device = name->st.st_dev;
395 lock_context.inode = name->st.st_ino;
397 *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
398 if (key->data == NULL) {
399 return NT_STATUS_NO_MEMORY;
406 form the lock context used for byte range locking. This is separate
407 from the locking key used for opendb locking as it needs to take
408 account of file streams (each stream is a separate byte range
411 static NTSTATUS pvfs_brl_locking_key(struct pvfs_filename *name,
412 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
416 status = pvfs_locking_key(name, mem_ctx, &odb_key);
417 if (!NT_STATUS_IS_OK(status)) {
420 if (name->stream_name == NULL) {
424 *key = data_blob_talloc(mem_ctx, NULL,
425 odb_key.length + strlen(name->stream_name) + 1);
426 if (key->data == NULL) {
427 return NT_STATUS_NO_MEMORY;
429 memcpy(key->data, odb_key.data, odb_key.length);
430 memcpy(key->data + odb_key.length,
431 name->stream_name, strlen(name->stream_name)+1);
432 data_blob_free(&odb_key);
440 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
441 struct smbsrv_request *req,
442 struct pvfs_filename *name,
448 struct odb_lock *lck;
449 uint32_t create_options = io->generic.in.create_options;
450 uint32_t share_access = io->generic.in.share_access;
451 uint32_t access_mask = io->generic.in.access_mask;
455 if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
456 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
457 return NT_STATUS_CANNOT_DELETE;
460 status = pvfs_access_check_create(pvfs, req, name, &access_mask);
461 if (!NT_STATUS_IS_OK(status)) {
465 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
471 f = talloc(req, struct pvfs_file);
473 return NT_STATUS_NO_MEMORY;
476 f->handle = talloc(f, struct pvfs_file_handle);
477 if (f->handle == NULL) {
478 return NT_STATUS_NO_MEMORY;
481 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_NEW_FNUM, UINT16_MAX);
483 return NT_STATUS_TOO_MANY_OPENED_FILES;
486 attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
487 mode = pvfs_fileperms(pvfs, attrib);
489 /* create the file */
490 fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
492 idr_remove(pvfs->idtree_fnum, fnum);
493 return pvfs_map_errno(pvfs, errno);
496 pvfs_xattr_unlink_hook(pvfs, name->full_name);
498 /* if this was a stream create then create the stream as well */
499 if (name->stream_name) {
500 status = pvfs_stream_create(pvfs, name, fd);
501 if (!NT_STATUS_IS_OK(status)) {
502 idr_remove(pvfs->idtree_fnum, fnum);
508 /* re-resolve the open fd */
509 status = pvfs_resolve_name_fd(pvfs, fd, name);
510 if (!NT_STATUS_IS_OK(status)) {
511 idr_remove(pvfs->idtree_fnum, fnum);
516 name->dos.attrib = attrib;
517 status = pvfs_dosattrib_save(pvfs, name, fd);
518 if (!NT_STATUS_IS_OK(status)) {
523 status = pvfs_open_setup_eas_acl(pvfs, req, name, fd, fnum, io);
524 if (!NT_STATUS_IS_OK(status)) {
528 /* form the lock context used for byte range locking and
530 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
531 if (!NT_STATUS_IS_OK(status)) {
535 status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
536 if (!NT_STATUS_IS_OK(status)) {
540 /* grab a lock on the open file record */
541 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
543 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
545 /* we were supposed to do a blocking lock, so something
547 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
551 status = odb_open_file(lck, f->handle, name->stream_id,
552 share_access, create_options, access_mask);
554 if (!NT_STATUS_IS_OK(status)) {
555 /* bad news, we must have hit a race - we don't delete the file
556 here as the most likely scenario is that someone else created
557 the file at the same time */
558 idr_remove(pvfs->idtree_fnum, fnum);
564 f->session = req->session;
565 f->smbpid = req->smbpid;
567 f->pending_list = NULL;
569 f->share_access = io->generic.in.share_access;
570 f->access_mask = access_mask;
571 f->impersonation = io->generic.in.impersonation;
573 f->handle->pvfs = pvfs;
574 f->handle->name = talloc_steal(f->handle, name);
576 f->handle->create_options = io->generic.in.create_options;
577 f->handle->seek_offset = 0;
578 f->handle->position = 0;
580 f->handle->have_opendb_entry = True;
581 f->handle->sticky_write_time = False;
583 DLIST_ADD(pvfs->open_files, f);
585 /* setup a destructor to avoid file descriptor leaks on
586 abnormal termination */
587 talloc_set_destructor(f, pvfs_fnum_destructor);
588 talloc_set_destructor(f->handle, pvfs_handle_destructor);
591 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
592 io->generic.out.oplock_level = OPLOCK_EXCLUSIVE;
594 io->generic.out.oplock_level = OPLOCK_NONE;
596 io->generic.out.fnum = f->fnum;
597 io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
598 io->generic.out.create_time = name->dos.create_time;
599 io->generic.out.access_time = name->dos.access_time;
600 io->generic.out.write_time = name->dos.write_time;
601 io->generic.out.change_time = name->dos.change_time;
602 io->generic.out.attrib = name->dos.attrib;
603 io->generic.out.alloc_size = name->dos.alloc_size;
604 io->generic.out.size = name->st.st_size;
605 io->generic.out.file_type = FILE_TYPE_DISK;
606 io->generic.out.ipc_state = 0;
607 io->generic.out.is_directory = 0;
609 /* success - keep the file handle */
610 talloc_steal(pvfs, f);
615 idr_remove(pvfs->idtree_fnum, fnum);
617 unlink(name->full_name);
623 state of a pending open retry
625 struct pvfs_open_retry {
626 struct ntvfs_module_context *ntvfs;
627 struct smbsrv_request *req;
630 DATA_BLOB odb_locking_key;
633 /* destroy a pending open request */
634 static int pvfs_retry_destructor(void *ptr)
636 struct pvfs_open_retry *r = ptr;
637 struct pvfs_state *pvfs = r->ntvfs->private_data;
638 if (r->odb_locking_key.data) {
639 struct odb_lock *lck;
640 lck = odb_lock(r->req, pvfs->odb_context, &r->odb_locking_key);
642 odb_remove_pending(lck, r);
652 static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
654 struct pvfs_open_retry *r = private;
655 struct ntvfs_module_context *ntvfs = r->ntvfs;
656 struct smbsrv_request *req = r->req;
657 union smb_open *io = r->io;
660 /* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
661 just a bug in their server, but we better do the same */
662 if (reason == PVFS_WAIT_CANCEL) {
666 talloc_free(r->wait_handle);
668 if (reason == PVFS_WAIT_TIMEOUT) {
669 /* if it timed out, then give the failure
672 req->async_states->status = NT_STATUS_SHARING_VIOLATION;
673 req->async_states->send_fn(req);
677 /* the pending odb entry is already removed. We use a null locking
678 key to indicate this */
679 data_blob_free(&r->odb_locking_key);
682 /* try the open again, which could trigger another retry setup
683 if it wants to, so we have to unmark the async flag so we
684 will know if it does a second async reply */
685 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
687 status = pvfs_open(ntvfs, req, io);
688 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
689 /* the 2nd try also replied async, so we don't send
694 /* re-mark it async, just in case someone up the chain does
696 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
698 /* send the reply up the chain */
699 req->async_states->status = status;
700 req->async_states->send_fn(req);
705 special handling for openx DENY_DOS semantics
707 This function attempts a reference open using an existing handle. If its allowed,
708 then it returns NT_STATUS_OK, otherwise it returns any other code and normal
709 open processing continues.
711 static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
712 struct smbsrv_request *req, union smb_open *io,
713 struct pvfs_file *f, struct odb_lock *lck)
715 struct pvfs_state *pvfs = ntvfs->private_data;
716 struct pvfs_file *f2;
717 struct pvfs_filename *name;
719 /* search for an existing open with the right parameters. Note
720 the magic ntcreatex options flag, which is set in the
721 generic mapping code. This might look ugly, but its
722 actually pretty much now w2k does it internally as well.
724 If you look at the BASE-DENYDOS test you will see that a
725 DENY_DOS is a very special case, and in the right
726 circumstances you actually get the _same_ handle back
727 twice, rather than a new handle.
729 for (f2=pvfs->open_files;f2;f2=f2->next) {
731 f2->session == req->session &&
732 f2->smbpid == req->smbpid &&
733 (f2->handle->create_options &
734 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
735 NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) &&
736 (f2->access_mask & SEC_FILE_WRITE_DATA) &&
737 strcasecmp_m(f2->handle->name->original_name,
738 io->generic.in.fname)==0) {
744 return NT_STATUS_SHARING_VIOLATION;
747 /* quite an insane set of semantics ... */
748 if (is_exe_filename(io->generic.in.fname) &&
749 (f2->handle->create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) {
750 return NT_STATUS_SHARING_VIOLATION;
754 setup a reference to the existing handle
756 talloc_free(f->handle);
757 f->handle = talloc_reference(f, f2->handle);
761 name = f->handle->name;
763 io->generic.out.oplock_level = OPLOCK_NONE;
764 io->generic.out.fnum = f->fnum;
765 io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
766 io->generic.out.create_time = name->dos.create_time;
767 io->generic.out.access_time = name->dos.access_time;
768 io->generic.out.write_time = name->dos.write_time;
769 io->generic.out.change_time = name->dos.change_time;
770 io->generic.out.attrib = name->dos.attrib;
771 io->generic.out.alloc_size = name->dos.alloc_size;
772 io->generic.out.size = name->st.st_size;
773 io->generic.out.file_type = FILE_TYPE_DISK;
774 io->generic.out.ipc_state = 0;
775 io->generic.out.is_directory = 0;
777 talloc_steal(f->pvfs, f);
785 setup for a open retry after a sharing violation
787 static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
788 struct smbsrv_request *req,
791 struct odb_lock *lck)
793 struct pvfs_state *pvfs = ntvfs->private_data;
794 struct pvfs_open_retry *r;
796 struct timeval end_time;
798 if (io->generic.in.create_options &
799 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
800 /* see if we can satisfy the request using the special DENY_DOS
802 status = pvfs_open_deny_dos(ntvfs, req, io, f, lck);
803 if (NT_STATUS_IS_OK(status)) {
808 r = talloc(req, struct pvfs_open_retry);
810 return NT_STATUS_NO_MEMORY;
816 r->odb_locking_key = data_blob_talloc(r,
817 f->handle->odb_locking_key.data,
818 f->handle->odb_locking_key.length);
820 end_time = timeval_add(&req->request_time, 0, pvfs->sharing_violation_delay);
822 /* setup a pending lock */
823 status = odb_open_file_pending(lck, r);
824 if (!NT_STATUS_IS_OK(status)) {
831 talloc_set_destructor(r, pvfs_retry_destructor);
833 r->wait_handle = pvfs_wait_message(pvfs, req, MSG_PVFS_RETRY_OPEN, end_time,
835 if (r->wait_handle == NULL) {
836 return NT_STATUS_NO_MEMORY;
839 talloc_steal(pvfs, r);
847 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
848 struct smbsrv_request *req, union smb_open *io)
850 struct pvfs_state *pvfs = ntvfs->private_data;
852 struct pvfs_filename *name;
856 struct odb_lock *lck;
857 uint32_t create_options;
858 uint32_t share_access;
859 uint32_t access_mask;
860 BOOL stream_existed, stream_truncate=False;
862 /* use the generic mapping code to avoid implementing all the
863 different open calls. */
864 if (io->generic.level != RAW_OPEN_GENERIC &&
865 io->generic.level != RAW_OPEN_NTTRANS_CREATE) {
866 return ntvfs_map_open(req, io, ntvfs);
869 /* resolve the cifs name to a posix name */
870 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
871 PVFS_RESOLVE_STREAMS, &name);
872 if (!NT_STATUS_IS_OK(status)) {
876 /* directory opens are handled separately */
877 if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
878 (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
879 return pvfs_open_directory(pvfs, req, name, io);
882 /* FILE_ATTRIBUTE_DIRECTORY is ignored if the above test for directory
883 open doesn't match */
884 io->generic.in.file_attr &= ~FILE_ATTRIBUTE_DIRECTORY;
886 create_options = io->generic.in.create_options;
887 share_access = io->generic.in.share_access;
888 access_mask = io->generic.in.access_mask;
890 /* certain create options are not allowed */
891 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
892 !(access_mask & SEC_STD_DELETE)) {
893 return NT_STATUS_INVALID_PARAMETER;
898 switch (io->generic.in.open_disposition) {
899 case NTCREATEX_DISP_SUPERSEDE:
900 case NTCREATEX_DISP_OVERWRITE_IF:
901 if (name->stream_name == NULL) {
904 stream_truncate = True;
908 case NTCREATEX_DISP_OPEN:
909 if (!name->stream_exists) {
910 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
915 case NTCREATEX_DISP_OVERWRITE:
916 if (!name->stream_exists) {
917 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
919 if (name->stream_name == NULL) {
922 stream_truncate = True;
926 case NTCREATEX_DISP_CREATE:
927 if (name->stream_exists) {
928 return NT_STATUS_OBJECT_NAME_COLLISION;
933 case NTCREATEX_DISP_OPEN_IF:
938 return NT_STATUS_INVALID_PARAMETER;
941 /* handle creating a new file separately */
943 status = pvfs_create_file(pvfs, req, name, io);
944 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
948 /* we've hit a race - the file was created during this call */
949 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
953 /* try re-resolving the name */
954 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
955 if (!NT_STATUS_IS_OK(status)) {
958 /* fall through to a normal open */
961 if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
962 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
963 return NT_STATUS_CANNOT_DELETE;
966 /* check the security descriptor */
967 status = pvfs_access_check(pvfs, req, name, &access_mask);
968 if (!NT_STATUS_IS_OK(status)) {
972 f = talloc(req, struct pvfs_file);
974 return NT_STATUS_NO_MEMORY;
977 f->handle = talloc(f, struct pvfs_file_handle);
978 if (f->handle == NULL) {
979 return NT_STATUS_NO_MEMORY;
982 /* allocate a fnum */
983 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
985 return NT_STATUS_TOO_MANY_OPENED_FILES;
989 f->session = req->session;
990 f->smbpid = req->smbpid;
992 f->pending_list = NULL;
994 f->share_access = io->generic.in.share_access;
995 f->access_mask = access_mask;
996 f->impersonation = io->generic.in.impersonation;
998 f->handle->pvfs = pvfs;
1000 f->handle->name = talloc_steal(f->handle, name);
1001 f->handle->create_options = io->generic.in.create_options;
1002 f->handle->seek_offset = 0;
1003 f->handle->position = 0;
1004 f->handle->mode = 0;
1005 f->handle->have_opendb_entry = False;
1006 f->handle->sticky_write_time = False;
1008 /* form the lock context used for byte range locking and
1010 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
1011 if (!NT_STATUS_IS_OK(status)) {
1012 idr_remove(pvfs->idtree_fnum, f->fnum);
1016 status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
1017 if (!NT_STATUS_IS_OK(status)) {
1018 idr_remove(pvfs->idtree_fnum, f->fnum);
1022 /* get a lock on this file before the actual open */
1023 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1025 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
1027 /* we were supposed to do a blocking lock, so something
1029 idr_remove(pvfs->idtree_fnum, fnum);
1030 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1033 DLIST_ADD(pvfs->open_files, f);
1035 /* setup a destructor to avoid file descriptor leaks on
1036 abnormal termination */
1037 talloc_set_destructor(f, pvfs_fnum_destructor);
1038 talloc_set_destructor(f->handle, pvfs_handle_destructor);
1041 /* see if we are allowed to open at the same time as existing opens */
1042 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
1043 share_access, create_options, access_mask);
1045 /* on a sharing violation we need to retry when the file is closed by
1046 the other user, or after 1 second */
1047 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) &&
1048 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
1049 return pvfs_open_setup_retry(ntvfs, req, io, f, lck);
1052 if (!NT_STATUS_IS_OK(status)) {
1057 f->handle->have_opendb_entry = True;
1059 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
1065 /* do the actual open */
1066 fd = open(f->handle->name->full_name, flags);
1069 return pvfs_map_errno(f->pvfs, errno);
1074 stream_existed = name->stream_exists;
1076 /* if this was a stream create then create the stream as well */
1077 if (!name->stream_exists) {
1078 status = pvfs_stream_create(pvfs, f->handle->name, fd);
1079 if (!NT_STATUS_IS_OK(status)) {
1083 if (stream_truncate) {
1084 status = pvfs_stream_truncate(pvfs, f->handle->name, fd, 0);
1085 if (!NT_STATUS_IS_OK(status)) {
1092 /* re-resolve the open fd */
1093 status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name);
1094 if (!NT_STATUS_IS_OK(status)) {
1099 if (f->handle->name->stream_id == 0 &&
1100 (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE ||
1101 io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) {
1102 /* for overwrite we need to replace file permissions */
1103 uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
1104 mode_t mode = pvfs_fileperms(pvfs, attrib);
1105 if (fchmod(fd, mode) == -1) {
1107 return pvfs_map_errno(pvfs, errno);
1109 name->dos.attrib = attrib;
1110 status = pvfs_dosattrib_save(pvfs, name, fd);
1111 if (!NT_STATUS_IS_OK(status)) {
1119 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
1120 io->generic.out.oplock_level = OPLOCK_EXCLUSIVE;
1122 io->generic.out.oplock_level = OPLOCK_NONE;
1124 io->generic.out.fnum = f->fnum;
1125 io->generic.out.create_action = stream_existed?
1126 NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED;
1127 io->generic.out.create_time = name->dos.create_time;
1128 io->generic.out.access_time = name->dos.access_time;
1129 io->generic.out.write_time = name->dos.write_time;
1130 io->generic.out.change_time = name->dos.change_time;
1131 io->generic.out.attrib = name->dos.attrib;
1132 io->generic.out.alloc_size = name->dos.alloc_size;
1133 io->generic.out.size = name->st.st_size;
1134 io->generic.out.file_type = FILE_TYPE_DISK;
1135 io->generic.out.ipc_state = 0;
1136 io->generic.out.is_directory = 0;
1138 /* success - keep the file handle */
1139 talloc_steal(f->pvfs, f);
1141 return NT_STATUS_OK;
1148 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
1149 struct smbsrv_request *req, union smb_close *io)
1151 struct pvfs_state *pvfs = ntvfs->private_data;
1152 struct pvfs_file *f;
1153 struct utimbuf unix_times;
1155 if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
1156 return NT_STATUS_DOS(ERRSRV, ERRerror);
1159 if (io->generic.level != RAW_CLOSE_CLOSE) {
1160 return ntvfs_map_close(req, io, ntvfs);
1163 f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
1165 return NT_STATUS_INVALID_HANDLE;
1168 if (!null_time(io->close.in.write_time)) {
1169 unix_times.actime = 0;
1170 unix_times.modtime = io->close.in.write_time;
1171 utime(f->handle->name->full_name, &unix_times);
1172 } else if (f->handle->sticky_write_time) {
1173 unix_times.actime = 0;
1174 unix_times.modtime = nt_time_to_unix(f->handle->name->dos.write_time);
1175 utime(f->handle->name->full_name, &unix_times);
1180 return NT_STATUS_OK;
1185 logoff - close all file descriptors open by a vuid
1187 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
1188 struct smbsrv_request *req)
1190 struct pvfs_state *pvfs = ntvfs->private_data;
1191 struct pvfs_file *f, *next;
1193 for (f=pvfs->open_files;f;f=next) {
1195 if (f->session == req->session) {
1200 return NT_STATUS_OK;
1205 exit - close files for the current pid
1207 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
1208 struct smbsrv_request *req)
1210 struct pvfs_state *pvfs = ntvfs->private_data;
1211 struct pvfs_file *f, *next;
1213 for (f=pvfs->open_files;f;f=next) {
1215 if (f->smbpid == req->smbpid) {
1220 return NT_STATUS_OK;
1225 change the create options on an already open file
1227 NTSTATUS pvfs_change_create_options(struct pvfs_state *pvfs,
1228 struct smbsrv_request *req,
1229 struct pvfs_file *f, uint32_t create_options)
1231 struct odb_lock *lck;
1234 if (f->handle->create_options == create_options) {
1235 return NT_STATUS_OK;
1238 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
1239 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
1240 return NT_STATUS_CANNOT_DELETE;
1243 if (f->handle->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
1244 if (!pvfs_directory_empty(pvfs, f->handle->name)) {
1245 return NT_STATUS_DIRECTORY_NOT_EMPTY;
1247 f->handle->create_options = create_options;
1248 return NT_STATUS_OK;
1251 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1253 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1256 status = odb_set_create_options(lck, f->handle, create_options);
1257 if (NT_STATUS_IS_OK(status)) {
1258 f->handle->create_options = create_options;
1268 determine if a file can be deleted, or if it is prevented by an
1271 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs,
1272 struct smbsrv_request *req,
1273 struct pvfs_filename *name)
1278 status = pvfs_locking_key(name, name, &key);
1279 if (!NT_STATUS_IS_OK(status)) {
1280 return NT_STATUS_NO_MEMORY;
1283 status = odb_can_open(pvfs->odb_context, &key,
1284 NTCREATEX_SHARE_ACCESS_READ |
1285 NTCREATEX_SHARE_ACCESS_WRITE |
1286 NTCREATEX_SHARE_ACCESS_DELETE,
1287 NTCREATEX_OPTIONS_DELETE_ON_CLOSE,
1290 if (NT_STATUS_IS_OK(status)) {
1291 status = pvfs_access_check_simple(pvfs, req, name, SEC_STD_DELETE);
1298 determine if a file can be renamed, or if it is prevented by an
1301 NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs, struct pvfs_filename *name)
1306 status = pvfs_locking_key(name, name, &key);
1307 if (!NT_STATUS_IS_OK(status)) {
1308 return NT_STATUS_NO_MEMORY;
1311 status = odb_can_open(pvfs->odb_context, &key,
1312 NTCREATEX_SHARE_ACCESS_READ |
1313 NTCREATEX_SHARE_ACCESS_WRITE,