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.
23 #include "include/includes.h"
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
37 #define SHARING_VIOLATION_DELAY 1000000
40 find open file handle given fnum
42 struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
43 struct smbsrv_request *req, uint16_t fnum)
47 f = idr_find(pvfs->idtree_fnum, fnum);
52 if (req->session != f->session) {
53 DEBUG(2,("pvfs_find_fd: attempt to use wrong session for fnum %d\n",
63 cleanup a open directory handle
65 static int pvfs_dir_fd_destructor(void *p)
67 struct pvfs_file *f = p;
68 DLIST_REMOVE(f->pvfs->open_files, f);
69 idr_remove(f->pvfs->idtree_fnum, f->fnum);
71 if (f->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
72 if (rmdir(f->name->full_name) != 0) {
73 DEBUG(0,("pvfs_close: failed to rmdir '%s' - %s\n",
74 f->name->full_name, strerror(errno)));
85 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
86 struct smbsrv_request *req,
87 struct pvfs_filename *name,
93 uint32_t create_action;
95 /* if the client says it must be a directory, and it isn't,
97 if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
98 return NT_STATUS_NOT_A_DIRECTORY;
101 switch (io->generic.in.open_disposition) {
102 case NTCREATEX_DISP_OPEN_IF:
105 case NTCREATEX_DISP_OPEN:
107 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
111 case NTCREATEX_DISP_CREATE:
113 return NT_STATUS_OBJECT_NAME_COLLISION;
117 case NTCREATEX_DISP_OVERWRITE_IF:
118 case NTCREATEX_DISP_OVERWRITE:
119 case NTCREATEX_DISP_SUPERSEDE:
121 return NT_STATUS_INVALID_PARAMETER;
124 f = talloc_p(req, struct pvfs_file);
126 return NT_STATUS_NO_MEMORY;
129 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_DIR_FNUM, UINT16_MAX);
131 return NT_STATUS_TOO_MANY_OPENED_FILES;
136 f->name = talloc_steal(f, name);
137 f->session = req->session;
138 f->smbpid = req->smbpid;
140 f->pending_list = NULL;
142 f->locking_key = data_blob(NULL, 0);
143 f->create_options = io->generic.in.create_options;
144 f->share_access = io->generic.in.share_access;
148 DLIST_ADD(pvfs->open_files, f);
150 /* TODO: should we check in the opendb? Do directory opens
151 follow the share_access rules? */
154 /* setup a destructor to avoid leaks on abnormal termination */
155 talloc_set_destructor(f, pvfs_dir_fd_destructor);
158 uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
159 mode_t mode = pvfs_fileperms(pvfs, attrib);
160 if (mkdir(name->full_name, mode) == -1) {
161 return pvfs_map_errno(pvfs,errno);
163 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
164 PVFS_RESOLVE_NO_WILDCARD, &name);
165 if (!NT_STATUS_IS_OK(status)) {
168 create_action = NTCREATEX_ACTION_CREATED;
170 create_action = NTCREATEX_ACTION_EXISTED;
174 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
177 /* the open succeeded, keep this handle permanently */
178 talloc_steal(pvfs, f);
180 io->generic.out.oplock_level = NO_OPLOCK;
181 io->generic.out.fnum = f->fnum;
182 io->generic.out.create_action = create_action;
183 io->generic.out.create_time = name->dos.create_time;
184 io->generic.out.access_time = name->dos.access_time;
185 io->generic.out.write_time = name->dos.write_time;
186 io->generic.out.change_time = name->dos.change_time;
187 io->generic.out.attrib = name->dos.attrib;
188 io->generic.out.alloc_size = name->dos.alloc_size;
189 io->generic.out.size = name->st.st_size;
190 io->generic.out.file_type = FILE_TYPE_DISK;
191 io->generic.out.ipc_state = 0;
192 io->generic.out.is_directory = 1;
199 by using a destructor we make sure that abnormal cleanup will not
200 leak file descriptors (assuming at least the top level pointer is freed, which
201 will cascade down to here)
203 static int pvfs_fd_destructor(void *p)
205 struct pvfs_file *f = p;
206 struct odb_lock *lck;
209 DLIST_REMOVE(f->pvfs->open_files, f);
211 pvfs_lock_close(f->pvfs, f);
218 idr_remove(f->pvfs->idtree_fnum, f->fnum);
220 if (f->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
221 if (unlink(f->name->full_name) != 0) {
222 DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n",
223 f->name->full_name, strerror(errno)));
227 lck = odb_lock(f, f->pvfs->odb_context, &f->locking_key);
229 DEBUG(0,("Unable to lock opendb for close\n"));
233 if (f->have_opendb_entry) {
234 status = odb_close_file(lck, f->fnum);
235 if (!NT_STATUS_IS_OK(status)) {
236 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
237 f->name->full_name, nt_errstr(status)));
248 form the lock context used for byte range locking and opendb
249 locking. Note that we must zero here to take account of
250 possible padding on some architectures
252 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
253 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
259 ZERO_STRUCT(lock_context);
261 lock_context.device = name->st.st_dev;
262 lock_context.inode = name->st.st_ino;
264 *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
265 if (key->data == NULL) {
266 return NT_STATUS_NO_MEMORY;
276 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
277 struct smbsrv_request *req,
278 struct pvfs_filename *name,
284 struct odb_lock *lck;
285 uint32_t create_options = io->generic.in.create_options;
286 uint32_t share_access = io->generic.in.share_access;
287 uint32_t access_mask = io->generic.in.access_mask;
290 if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
291 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
292 return NT_STATUS_CANNOT_DELETE;
295 if (access_mask & SEC_RIGHT_MAXIMUM_ALLOWED) {
296 access_mask = GENERIC_RIGHTS_FILE_READ | GENERIC_RIGHTS_FILE_WRITE;
299 if ((access_mask & SA_RIGHT_FILE_READ_EXEC) &&
300 (access_mask & SA_RIGHT_FILE_WRITE_APPEND)) {
302 } else if (access_mask & SA_RIGHT_FILE_WRITE_APPEND) {
308 f = talloc_p(req, struct pvfs_file);
310 return NT_STATUS_NO_MEMORY;
313 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_NEW_FNUM, UINT16_MAX);
315 return NT_STATUS_TOO_MANY_OPENED_FILES;
318 mode = pvfs_fileperms(pvfs, io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE);
320 /* create the file */
321 fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
323 idr_remove(pvfs->idtree_fnum, fnum);
324 return pvfs_map_errno(pvfs, errno);
327 /* re-resolve the open fd */
328 status = pvfs_resolve_name_fd(pvfs, fd, name);
329 if (!NT_STATUS_IS_OK(status)) {
330 idr_remove(pvfs->idtree_fnum, fnum);
335 /* form the lock context used for byte range locking and
337 status = pvfs_locking_key(name, f, &f->locking_key);
338 if (!NT_STATUS_IS_OK(status)) {
339 idr_remove(pvfs->idtree_fnum, fnum);
344 /* grab a lock on the open file record */
345 lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
347 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
349 /* we were supposed to do a blocking lock, so something
351 idr_remove(pvfs->idtree_fnum, fnum);
353 return NT_STATUS_INTERNAL_DB_CORRUPTION;
356 status = odb_open_file(lck, fnum, share_access, create_options, access_mask);
357 if (!NT_STATUS_IS_OK(status)) {
358 /* bad news, we must have hit a race */
359 idr_remove(pvfs->idtree_fnum, fnum);
366 f->name = talloc_steal(f, name);
367 f->session = req->session;
368 f->smbpid = req->smbpid;
370 f->pending_list = NULL;
372 f->create_options = io->generic.in.create_options;
373 f->share_access = io->generic.in.share_access;
374 f->access_mask = access_mask;
377 f->have_opendb_entry = True;
379 DLIST_ADD(pvfs->open_files, f);
381 /* setup a destructor to avoid file descriptor leaks on
382 abnormal termination */
383 talloc_set_destructor(f, pvfs_fd_destructor);
385 io->generic.out.oplock_level = NO_OPLOCK;
386 io->generic.out.fnum = f->fnum;
387 io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
388 io->generic.out.create_time = name->dos.create_time;
389 io->generic.out.access_time = name->dos.access_time;
390 io->generic.out.write_time = name->dos.write_time;
391 io->generic.out.change_time = name->dos.change_time;
392 io->generic.out.attrib = name->dos.attrib;
393 io->generic.out.alloc_size = name->dos.alloc_size;
394 io->generic.out.size = name->st.st_size;
395 io->generic.out.file_type = FILE_TYPE_DISK;
396 io->generic.out.ipc_state = 0;
397 io->generic.out.is_directory = 0;
399 /* success - keep the file handle */
400 talloc_steal(pvfs, f);
407 open am existing file - called from both the open retry code
408 and the main open code
410 NTSTATUS pvfs_open_existing(struct pvfs_file *f,
417 /* do the actual open */
418 fd = open(f->name->full_name, open_flags);
420 return pvfs_map_errno(f->pvfs, errno);
425 /* re-resolve the open fd */
426 status = pvfs_resolve_name_fd(f->pvfs, fd, f->name);
427 if (!NT_STATUS_IS_OK(status)) {
431 io->generic.out.oplock_level = NO_OPLOCK;
432 io->generic.out.fnum = f->fnum;
433 io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
434 io->generic.out.create_time = f->name->dos.create_time;
435 io->generic.out.access_time = f->name->dos.access_time;
436 io->generic.out.write_time = f->name->dos.write_time;
437 io->generic.out.change_time = f->name->dos.change_time;
438 io->generic.out.attrib = f->name->dos.attrib;
439 io->generic.out.alloc_size = f->name->dos.alloc_size;
440 io->generic.out.size = f->name->st.st_size;
441 io->generic.out.file_type = FILE_TYPE_DISK;
442 io->generic.out.ipc_state = 0;
443 io->generic.out.is_directory = 0;
445 /* success - keep the file handle */
446 talloc_steal(f->pvfs, f);
452 state of a pending open retry
454 struct pvfs_open_retry {
457 struct smbsrv_request *req;
459 struct timeval end_time;
463 /* destroy a pending open request */
464 static int pvfs_retry_destructor(void *ptr)
466 struct pvfs_open_retry *r = ptr;
467 struct odb_lock *lck;
468 lck = odb_lock(r->req, r->f->pvfs->odb_context, &r->f->locking_key);
470 odb_remove_pending(lck, r);
478 static void pvfs_open_retry(void *private, BOOL timed_out)
480 struct pvfs_open_retry *r = private;
481 struct odb_lock *lck;
482 struct pvfs_file *f = r->f;
483 struct smbsrv_request *req = r->req;
486 lck = odb_lock(req, f->pvfs->odb_context, &f->locking_key);
488 req->async_states->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
489 req->async_states->send_fn(req);
493 /* see if we are allowed to open at the same time as existing opens */
494 status = odb_open_file(lck, f->fnum, f->share_access,
495 f->create_options, f->access_mask);
496 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) && !timed_out) {
501 talloc_free(r->wait_handle);
503 if (!NT_STATUS_IS_OK(status)) {
504 req->async_states->status = status;
505 req->async_states->send_fn(req);
509 f->have_opendb_entry = True;
511 /* do the rest of the open work */
512 status = pvfs_open_existing(f, r->io, r->open_flags);
514 if (NT_STATUS_IS_OK(status)) {
515 talloc_steal(f->pvfs, f);
518 req->async_states->status = status;
519 req->async_states->send_fn(req);
523 setup for a open retry after a sharing violation
525 static NTSTATUS pvfs_open_setup_retry(struct smbsrv_request *req,
528 struct odb_lock *lck,
531 struct pvfs_open_retry *r;
532 struct pvfs_state *pvfs = f->pvfs;
535 r = talloc_p(req, struct pvfs_open_retry);
537 return NT_STATUS_NO_MEMORY;
543 r->end_time = timeval_current_ofs(0, SHARING_VIOLATION_DELAY);
544 r->open_flags = open_flags;
546 /* setup a pending lock */
547 status = odb_open_file_pending(lck, r);
548 if (!NT_STATUS_IS_OK(status)) {
552 r->wait_handle = pvfs_wait_message(pvfs, req, MSG_PVFS_RETRY_OPEN, r->end_time,
554 if (r->wait_handle == NULL) {
555 return NT_STATUS_NO_MEMORY;
559 talloc_steal(pvfs, req);
561 talloc_set_destructor(r, pvfs_retry_destructor);
569 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
570 struct smbsrv_request *req, union smb_open *io)
572 struct pvfs_state *pvfs = ntvfs->private_data;
574 struct pvfs_filename *name;
578 struct odb_lock *lck;
579 uint32_t create_options;
580 uint32_t share_access;
581 uint32_t access_mask;
583 /* use the generic mapping code to avoid implementing all the
584 different open calls. This won't allow openx to work
585 perfectly as the mapping code has no way of knowing if two
586 opens are on the same connection, so this will need to
588 if (io->generic.level != RAW_OPEN_GENERIC) {
589 return ntvfs_map_open(req, io, ntvfs);
592 /* resolve the cifs name to a posix name */
593 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
594 PVFS_RESOLVE_NO_WILDCARD, &name);
595 if (!NT_STATUS_IS_OK(status)) {
599 /* directory opens are handled separately */
600 if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
601 (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
602 return pvfs_open_directory(pvfs, req, name, io);
605 create_options = io->generic.in.create_options;
606 share_access = io->generic.in.share_access;
607 access_mask = io->generic.in.access_mask;
609 if (access_mask & SEC_RIGHT_MAXIMUM_ALLOWED) {
610 if (name->exists && (name->dos.attrib & FILE_ATTRIBUTE_READONLY)) {
611 access_mask = GENERIC_RIGHTS_FILE_READ;
613 access_mask = GENERIC_RIGHTS_FILE_READ | GENERIC_RIGHTS_FILE_WRITE;
617 /* certain create options are not allowed */
618 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
619 !(access_mask & STD_RIGHT_DELETE_ACCESS)) {
620 return NT_STATUS_INVALID_PARAMETER;
623 switch (io->generic.in.open_disposition) {
624 case NTCREATEX_DISP_SUPERSEDE:
628 case NTCREATEX_DISP_OVERWRITE_IF:
632 case NTCREATEX_DISP_OPEN:
634 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
639 case NTCREATEX_DISP_OVERWRITE:
641 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
646 case NTCREATEX_DISP_CREATE:
648 return NT_STATUS_OBJECT_NAME_COLLISION;
653 case NTCREATEX_DISP_OPEN_IF:
658 return NT_STATUS_INVALID_PARAMETER;
661 if ((access_mask & SA_RIGHT_FILE_READ_EXEC) &&
662 (access_mask & SA_RIGHT_FILE_WRITE_APPEND)) {
664 } else if (access_mask & SA_RIGHT_FILE_WRITE_APPEND) {
670 /* handle creating a new file separately */
672 status = pvfs_create_file(pvfs, req, name, io);
673 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
677 /* we've hit a race - the file was created during this call */
678 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
682 /* try re-resolving the name */
683 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
684 PVFS_RESOLVE_NO_WILDCARD, &name);
685 if (!NT_STATUS_IS_OK(status)) {
688 /* fall through to a normal open */
691 if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
692 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
693 return NT_STATUS_CANNOT_DELETE;
696 f = talloc_p(req, struct pvfs_file);
698 return NT_STATUS_NO_MEMORY;
701 /* allocate a fnum */
702 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
704 return NT_STATUS_TOO_MANY_OPENED_FILES;
709 f->name = talloc_steal(f, name);
710 f->session = req->session;
711 f->smbpid = req->smbpid;
713 f->pending_list = NULL;
715 f->create_options = io->generic.in.create_options;
716 f->share_access = io->generic.in.share_access;
717 f->access_mask = access_mask;
720 f->have_opendb_entry = False;
722 /* form the lock context used for byte range locking and
724 status = pvfs_locking_key(name, f, &f->locking_key);
725 if (!NT_STATUS_IS_OK(status)) {
726 idr_remove(pvfs->idtree_fnum, f->fnum);
730 /* get a lock on this file before the actual open */
731 lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
733 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
735 /* we were supposed to do a blocking lock, so something
737 idr_remove(pvfs->idtree_fnum, fnum);
738 return NT_STATUS_INTERNAL_DB_CORRUPTION;
741 DLIST_ADD(pvfs->open_files, f);
743 /* setup a destructor to avoid file descriptor leaks on
744 abnormal termination */
745 talloc_set_destructor(f, pvfs_fd_destructor);
748 /* see if we are allowed to open at the same time as existing opens */
749 status = odb_open_file(lck, f->fnum, share_access, create_options, access_mask);
751 /* on a sharing violation we need to retry when the file is closed by
752 the other user, or after 1 second */
753 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) &&
754 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
755 return pvfs_open_setup_retry(req, io, f, lck, flags);
758 if (!NT_STATUS_IS_OK(status)) {
762 f->have_opendb_entry = True;
764 /* do the rest of the open work */
765 return pvfs_open_existing(f, io, flags);
772 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
773 struct smbsrv_request *req, union smb_close *io)
775 struct pvfs_state *pvfs = ntvfs->private_data;
778 struct utimbuf unix_times;
780 if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
781 return NT_STATUS_UNSUCCESSFUL;
784 if (io->generic.level != RAW_CLOSE_CLOSE) {
785 return ntvfs_map_close(req, io, ntvfs);
788 f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
790 return NT_STATUS_INVALID_HANDLE;
793 if (!null_time(io->close.in.write_time)) {
794 unix_times.actime = 0;
795 unix_times.modtime = io->close.in.write_time;
796 utime(f->name->full_name, &unix_times);
800 close(f->fd) == -1) {
801 status = pvfs_map_errno(pvfs, errno);
803 status = NT_STATUS_OK;
807 /* the destructor takes care of the rest */
815 logoff - close all file descriptors open by a vuid
817 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
818 struct smbsrv_request *req)
820 struct pvfs_state *pvfs = ntvfs->private_data;
821 struct pvfs_file *f, *next;
823 for (f=pvfs->open_files;f;f=next) {
825 if (f->session == req->session) {
835 exit - close files for the current pid
837 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
838 struct smbsrv_request *req)
840 struct pvfs_state *pvfs = ntvfs->private_data;
841 struct pvfs_file *f, *next;
843 for (f=pvfs->open_files;f;f=next) {
845 if (f->smbpid == req->smbpid) {
855 change the create options on an already open file
857 NTSTATUS pvfs_change_create_options(struct pvfs_state *pvfs,
858 struct smbsrv_request *req,
859 struct pvfs_file *f, uint32_t create_options)
861 struct odb_lock *lck;
864 if (f->create_options == create_options) {
868 if ((f->name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
869 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
870 return NT_STATUS_CANNOT_DELETE;
873 lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
875 return NT_STATUS_INTERNAL_DB_CORRUPTION;
878 status = odb_set_create_options(lck, f->fnum, create_options);
879 if (NT_STATUS_IS_OK(status)) {
880 f->create_options = create_options;
888 determine if a file can be deleted, or if it is prevented by an
891 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs, struct pvfs_filename *name)
896 status = pvfs_locking_key(name, name, &key);
897 if (!NT_STATUS_IS_OK(status)) {
898 return NT_STATUS_NO_MEMORY;
901 status = odb_can_open(pvfs->odb_context, &key,
902 NTCREATEX_SHARE_ACCESS_READ |
903 NTCREATEX_SHARE_ACCESS_WRITE |
904 NTCREATEX_SHARE_ACCESS_DELETE,
905 0, STD_RIGHT_DELETE_ACCESS);