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"
27 create file handles with convenient numbers for sniffers
29 #define PVFS_MIN_FILE_FNUM 0x100
30 #define PVFS_MIN_NEW_FNUM 0x200
31 #define PVFS_MIN_DIR_FNUM 0x1000
34 find open file handle given fnum
36 struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
37 struct smbsrv_request *req, uint16_t fnum)
41 f = idr_find(pvfs->idtree_fnum, fnum);
46 if (req->session != f->session) {
47 DEBUG(2,("pvfs_find_fd: attempt to use wrong session for fnum %d\n",
57 cleanup a open directory handle
59 static int pvfs_dir_fd_destructor(void *p)
61 struct pvfs_file *f = p;
62 DLIST_REMOVE(f->pvfs->open_files, f);
63 idr_remove(f->pvfs->idtree_fnum, f->fnum);
65 if (f->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
66 if (rmdir(f->name->full_name) != 0) {
67 DEBUG(0,("pvfs_close: failed to rmdir '%s'\n",
79 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
80 struct smbsrv_request *req,
81 struct pvfs_filename *name,
87 uint32_t create_action;
89 /* if the client says it must be a directory, and it isn't,
91 if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
92 return NT_STATUS_NOT_A_DIRECTORY;
95 switch (io->generic.in.open_disposition) {
96 case NTCREATEX_DISP_OPEN_IF:
99 case NTCREATEX_DISP_OPEN:
101 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
105 case NTCREATEX_DISP_CREATE:
107 return NT_STATUS_OBJECT_NAME_COLLISION;
111 case NTCREATEX_DISP_OVERWRITE_IF:
112 case NTCREATEX_DISP_OVERWRITE:
113 case NTCREATEX_DISP_SUPERSEDE:
115 return NT_STATUS_INVALID_PARAMETER;
118 f = talloc_p(req, struct pvfs_file);
120 return NT_STATUS_NO_MEMORY;
123 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_DIR_FNUM, UINT16_MAX);
126 return NT_STATUS_TOO_MANY_OPENED_FILES;
131 f->name = talloc_steal(f, name);
132 f->session = req->session;
133 f->smbpid = req->smbpid;
135 f->pending_list = NULL;
137 f->locking_key = data_blob(NULL, 0);
138 f->create_options = io->generic.in.create_options;
139 f->share_access = io->generic.in.share_access;
141 DLIST_ADD(pvfs->open_files, f);
143 /* TODO: should we check in the opendb? Do directory opens
144 follow the share_access rules? */
147 /* setup a destructor to avoid leaks on abnormal termination */
148 talloc_set_destructor(f, pvfs_dir_fd_destructor);
151 if (mkdir(name->full_name, 0755) == -1) {
152 return pvfs_map_errno(pvfs,errno);
154 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
155 PVFS_RESOLVE_NO_WILDCARD, &name);
156 if (!NT_STATUS_IS_OK(status)) {
159 create_action = NTCREATEX_ACTION_CREATED;
161 create_action = NTCREATEX_ACTION_EXISTED;
165 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
168 /* the open succeeded, keep this handle permanently */
169 talloc_steal(pvfs, f);
171 io->generic.out.oplock_level = NO_OPLOCK;
172 io->generic.out.fnum = f->fnum;
173 io->generic.out.create_action = create_action;
174 io->generic.out.create_time = name->dos.create_time;
175 io->generic.out.access_time = name->dos.access_time;
176 io->generic.out.write_time = name->dos.write_time;
177 io->generic.out.change_time = name->dos.change_time;
178 io->generic.out.attrib = name->dos.attrib;
179 io->generic.out.alloc_size = 0;
180 io->generic.out.size = 0;
181 io->generic.out.file_type = FILE_TYPE_DISK;
182 io->generic.out.ipc_state = 0;
183 io->generic.out.is_directory = 1;
190 by using a destructor we make sure that abnormal cleanup will not
191 leak file descriptors (assuming at least the top level pointer is freed, which
192 will cascade down to here)
194 static int pvfs_fd_destructor(void *p)
196 struct pvfs_file *f = p;
197 struct odb_lock *lck;
200 DLIST_REMOVE(f->pvfs->open_files, f);
202 pvfs_lock_close(f->pvfs, f);
209 idr_remove(f->pvfs->idtree_fnum, f->fnum);
211 if (f->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
212 if (unlink(f->name->full_name) != 0) {
213 DEBUG(0,("pvfs_close: failed to delete '%s'\n",
214 f->name->full_name));
218 lck = odb_lock(f, f->pvfs->odb_context, &f->locking_key);
220 DEBUG(0,("Unable to lock opendb for close\n"));
224 status = odb_close_file(lck, f->fnum);
225 if (!NT_STATUS_IS_OK(status)) {
226 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
227 f->name->full_name, nt_errstr(status)));
237 form the lock context used for byte range locking and opendb
238 locking. Note that we must zero here to take account of
239 possible padding on some architectures
241 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
242 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
248 ZERO_STRUCT(lock_context);
250 lock_context.device = name->st.st_dev;
251 lock_context.inode = name->st.st_ino;
253 *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
254 if (key->data == NULL) {
255 return NT_STATUS_NO_MEMORY;
265 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
266 struct smbsrv_request *req,
267 struct pvfs_filename *name,
273 struct odb_lock *lck;
274 uint32_t create_options = io->generic.in.create_options;
275 uint32_t share_access = io->generic.in.share_access;
276 uint32_t access_mask = io->generic.in.access_mask;
279 if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
280 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
281 return NT_STATUS_CANNOT_DELETE;
286 f = talloc_p(req, struct pvfs_file);
288 return NT_STATUS_NO_MEMORY;
291 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_NEW_FNUM, UINT16_MAX);
293 return NT_STATUS_TOO_MANY_OPENED_FILES;
296 if (io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) {
302 /* create the file */
303 fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
305 idr_remove(pvfs->idtree_fnum, fnum);
306 return pvfs_map_errno(pvfs, errno);
309 /* re-resolve the open fd */
310 status = pvfs_resolve_name_fd(pvfs, fd, name);
311 if (!NT_STATUS_IS_OK(status)) {
312 idr_remove(pvfs->idtree_fnum, fnum);
317 /* form the lock context used for byte range locking and
319 status = pvfs_locking_key(name, f, &f->locking_key);
320 if (!NT_STATUS_IS_OK(status)) {
321 idr_remove(pvfs->idtree_fnum, fnum);
326 /* grab a lock on the open file record */
327 lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
329 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
331 /* we were supposed to do a blocking lock, so something
333 idr_remove(pvfs->idtree_fnum, fnum);
335 return NT_STATUS_INTERNAL_DB_CORRUPTION;
338 status = odb_open_file(lck, fnum, share_access, create_options, access_mask);
339 if (!NT_STATUS_IS_OK(status)) {
340 /* bad news, we must have hit a race */
341 idr_remove(pvfs->idtree_fnum, fnum);
348 f->name = talloc_steal(f, name);
349 f->session = req->session;
350 f->smbpid = req->smbpid;
352 f->pending_list = NULL;
354 f->create_options = io->generic.in.create_options;
355 f->share_access = io->generic.in.share_access;
356 f->access_mask = io->generic.in.access_mask;
358 DLIST_ADD(pvfs->open_files, f);
360 /* setup a destructor to avoid file descriptor leaks on
361 abnormal termination */
362 talloc_set_destructor(f, pvfs_fd_destructor);
364 io->generic.out.oplock_level = NO_OPLOCK;
365 io->generic.out.fnum = f->fnum;
366 io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
367 io->generic.out.create_time = name->dos.create_time;
368 io->generic.out.access_time = name->dos.access_time;
369 io->generic.out.write_time = name->dos.write_time;
370 io->generic.out.change_time = name->dos.change_time;
371 io->generic.out.attrib = name->dos.attrib;
372 io->generic.out.alloc_size = name->dos.alloc_size;
373 io->generic.out.size = name->st.st_size;
374 io->generic.out.file_type = FILE_TYPE_DISK;
375 io->generic.out.ipc_state = 0;
376 io->generic.out.is_directory = 0;
378 /* success - keep the file handle */
379 talloc_steal(pvfs, f);
388 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
389 struct smbsrv_request *req, union smb_open *io)
391 struct pvfs_state *pvfs = ntvfs->private_data;
393 struct pvfs_filename *name;
397 struct odb_lock *lck;
398 uint32_t create_options;
399 uint32_t share_access;
400 uint32_t access_mask;
402 /* use the generic mapping code to avoid implementing all the
403 different open calls. This won't allow openx to work
404 perfectly as the mapping code has no way of knowing if two
405 opens are on the same connection, so this will need to
407 if (io->generic.level != RAW_OPEN_GENERIC) {
408 return ntvfs_map_open(req, io, ntvfs);
411 /* resolve the cifs name to a posix name */
412 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
413 PVFS_RESOLVE_NO_WILDCARD, &name);
414 if (!NT_STATUS_IS_OK(status)) {
418 /* directory opens are handled separately */
419 if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
420 (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
421 return pvfs_open_directory(pvfs, req, name, io);
424 create_options = io->generic.in.create_options;
425 share_access = io->generic.in.share_access;
426 access_mask = io->generic.in.access_mask;
428 /* certain create options are not allowed */
429 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
430 !(access_mask & STD_RIGHT_DELETE_ACCESS)) {
431 return NT_STATUS_INVALID_PARAMETER;
434 switch (io->generic.in.open_disposition) {
435 case NTCREATEX_DISP_SUPERSEDE:
437 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
442 case NTCREATEX_DISP_OVERWRITE_IF:
446 case NTCREATEX_DISP_OPEN:
448 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
453 case NTCREATEX_DISP_OVERWRITE:
455 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
460 case NTCREATEX_DISP_CREATE:
462 return NT_STATUS_OBJECT_NAME_COLLISION;
467 case NTCREATEX_DISP_OPEN_IF:
472 return NT_STATUS_INVALID_PARAMETER;
477 /* handle creating a new file separately */
479 status = pvfs_create_file(pvfs, req, name, io);
480 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
484 /* we've hit a race - the file was created during this call */
485 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
489 /* try re-resolving the name */
490 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
491 PVFS_RESOLVE_NO_WILDCARD, &name);
492 if (!NT_STATUS_IS_OK(status)) {
495 /* fall through to a normal open */
498 if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
499 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
500 return NT_STATUS_CANNOT_DELETE;
503 f = talloc_p(req, struct pvfs_file);
505 return NT_STATUS_NO_MEMORY;
508 /* allocate a fnum */
509 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
511 return NT_STATUS_TOO_MANY_OPENED_FILES;
514 /* form the lock context used for byte range locking and
516 status = pvfs_locking_key(name, f, &f->locking_key);
517 if (!NT_STATUS_IS_OK(status)) {
518 idr_remove(pvfs->idtree_fnum, fnum);
522 /* get a lock on this file before the actual open */
523 lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
525 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
527 /* we were supposed to do a blocking lock, so something
529 idr_remove(pvfs->idtree_fnum, fnum);
530 return NT_STATUS_INTERNAL_DB_CORRUPTION;
533 /* see if we are allowed to open at the same time as existing opens */
534 status = odb_open_file(lck, fnum, share_access, create_options, access_mask);
535 if (!NT_STATUS_IS_OK(status)) {
536 idr_remove(pvfs->idtree_fnum, fnum);
542 f->name = talloc_steal(f, name);
543 f->session = req->session;
544 f->smbpid = req->smbpid;
546 f->pending_list = NULL;
548 f->create_options = io->generic.in.create_options;
549 f->share_access = io->generic.in.share_access;
550 f->access_mask = io->generic.in.access_mask;
552 DLIST_ADD(pvfs->open_files, f);
554 /* setup a destructor to avoid file descriptor leaks on
555 abnormal termination */
556 talloc_set_destructor(f, pvfs_fd_destructor);
558 /* do the actual open */
559 fd = open(name->full_name, flags);
561 return pvfs_map_errno(pvfs, errno);
566 /* re-resolve the open fd */
567 status = pvfs_resolve_name_fd(pvfs, fd, name);
568 if (!NT_STATUS_IS_OK(status)) {
572 io->generic.out.oplock_level = NO_OPLOCK;
573 io->generic.out.fnum = f->fnum;
574 io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
575 io->generic.out.create_time = name->dos.create_time;
576 io->generic.out.access_time = name->dos.access_time;
577 io->generic.out.write_time = name->dos.write_time;
578 io->generic.out.change_time = name->dos.change_time;
579 io->generic.out.attrib = name->dos.attrib;
580 io->generic.out.alloc_size = name->dos.alloc_size;
581 io->generic.out.size = name->st.st_size;
582 io->generic.out.file_type = FILE_TYPE_DISK;
583 io->generic.out.ipc_state = 0;
584 io->generic.out.is_directory = 0;
586 /* success - keep the file handle */
587 talloc_steal(pvfs, f);
596 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
597 struct smbsrv_request *req, union smb_close *io)
599 struct pvfs_state *pvfs = ntvfs->private_data;
603 if (io->generic.level != RAW_CLOSE_CLOSE) {
604 return ntvfs_map_close(req, io, ntvfs);
607 f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
609 return NT_STATUS_INVALID_HANDLE;
613 close(f->fd) == -1) {
614 status = pvfs_map_errno(pvfs, errno);
616 status = NT_STATUS_OK;
620 /* the destructor takes care of the rest */
628 logoff - close all file descriptors open by a vuid
630 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
631 struct smbsrv_request *req)
633 struct pvfs_state *pvfs = ntvfs->private_data;
634 struct pvfs_file *f, *next;
636 for (f=pvfs->open_files;f;f=next) {
638 if (f->session == req->session) {
639 DLIST_REMOVE(pvfs->open_files, f);
649 exit - close files for the current pid
651 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
652 struct smbsrv_request *req)
654 struct pvfs_state *pvfs = ntvfs->private_data;
655 struct pvfs_file *f, *next;
657 for (f=pvfs->open_files;f;f=next) {
659 if (f->smbpid == req->smbpid) {
660 DLIST_REMOVE(pvfs->open_files, f);
670 change the create options on an already open file
672 NTSTATUS pvfs_change_create_options(struct pvfs_state *pvfs,
673 struct smbsrv_request *req,
674 struct pvfs_file *f, uint32_t create_options)
676 struct odb_lock *lck;
679 if (f->create_options == create_options) {
683 if ((f->name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
684 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
685 return NT_STATUS_CANNOT_DELETE;
688 lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
690 return NT_STATUS_INTERNAL_DB_CORRUPTION;
693 status = odb_set_create_options(lck, f->fnum, create_options);
694 if (NT_STATUS_IS_OK(status)) {
695 f->create_options = create_options;