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"
28 find open file handle given fnum
30 struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
31 struct smbsrv_request *req, uint16_t fnum)
35 f = idr_find(pvfs->idtree_fnum, fnum);
40 if (req->session != f->session) {
41 DEBUG(2,("pvfs_find_fd: attempt to use wrong session for fnum %d\n",
51 cleanup a open directory handle
53 static int pvfs_dir_fd_destructor(void *p)
55 struct pvfs_file *f = p;
56 DLIST_REMOVE(f->pvfs->open_files, f);
57 idr_remove(f->pvfs->idtree_fnum, f->fnum);
59 if (f->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
60 if (rmdir(f->name->full_name) != 0) {
61 DEBUG(0,("pvfs_close: failed to rmdir '%s'\n",
73 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
74 struct smbsrv_request *req,
75 struct pvfs_filename *name,
81 uint32_t create_action;
83 /* if the client says it must be a directory, and it isn't,
85 if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
86 return NT_STATUS_NOT_A_DIRECTORY;
89 switch (io->generic.in.open_disposition) {
90 case NTCREATEX_DISP_OPEN_IF:
93 case NTCREATEX_DISP_OPEN:
95 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
99 case NTCREATEX_DISP_CREATE:
101 return NT_STATUS_OBJECT_NAME_COLLISION;
105 case NTCREATEX_DISP_OVERWRITE_IF:
106 case NTCREATEX_DISP_OVERWRITE:
107 case NTCREATEX_DISP_SUPERSEDE:
109 return NT_STATUS_INVALID_PARAMETER;
112 f = talloc_p(req, struct pvfs_file);
114 return NT_STATUS_NO_MEMORY;
117 fnum = idr_get_new(pvfs->idtree_fnum, f, UINT16_MAX);
120 return NT_STATUS_TOO_MANY_OPENED_FILES;
125 f->name = talloc_steal(f, name);
126 f->session = req->session;
127 f->smbpid = req->smbpid;
129 f->pending_list = NULL;
131 f->locking_key = data_blob(NULL, 0);
132 f->create_options = io->generic.in.create_options;
133 f->share_access = io->generic.in.share_access;
135 DLIST_ADD(pvfs->open_files, f);
137 /* TODO: should we check in the opendb? Do directory opens
138 follow the share_access rules? */
141 /* setup a destructor to avoid leaks on abnormal termination */
142 talloc_set_destructor(f, pvfs_dir_fd_destructor);
145 if (mkdir(name->full_name, 0755) == -1) {
146 return pvfs_map_errno(pvfs,errno);
148 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
149 PVFS_RESOLVE_NO_WILDCARD, &name);
150 if (!NT_STATUS_IS_OK(status)) {
153 create_action = NTCREATEX_ACTION_CREATED;
155 create_action = NTCREATEX_ACTION_EXISTED;
159 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
162 /* the open succeeded, keep this handle permanently */
163 talloc_steal(pvfs, f);
165 io->generic.out.oplock_level = NO_OPLOCK;
166 io->generic.out.fnum = f->fnum;
167 io->generic.out.create_action = create_action;
168 io->generic.out.create_time = name->dos.create_time;
169 io->generic.out.access_time = name->dos.access_time;
170 io->generic.out.write_time = name->dos.write_time;
171 io->generic.out.change_time = name->dos.change_time;
172 io->generic.out.attrib = name->dos.attrib;
173 io->generic.out.alloc_size = 0;
174 io->generic.out.size = 0;
175 io->generic.out.file_type = FILE_TYPE_DISK;
176 io->generic.out.ipc_state = 0;
177 io->generic.out.is_directory = 1;
184 by using a destructor we make sure that abnormal cleanup will not
185 leak file descriptors (assuming at least the top level pointer is freed, which
186 will cascade down to here)
188 static int pvfs_fd_destructor(void *p)
190 struct pvfs_file *f = p;
191 struct odb_lock *lck;
194 DLIST_REMOVE(f->pvfs->open_files, f);
196 pvfs_lock_close(f->pvfs, f);
203 idr_remove(f->pvfs->idtree_fnum, f->fnum);
205 lck = odb_lock(f, f->pvfs->odb_context, &f->locking_key);
207 DEBUG(0,("Unable to lock opendb for close\n"));
211 status = odb_close_file(lck, f->fnum);
212 if (!NT_STATUS_IS_OK(status)) {
213 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
214 f->name->full_name, nt_errstr(status)));
217 if (f->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
218 if (unlink(f->name->full_name) != 0) {
219 DEBUG(0,("pvfs_close: failed to delete '%s'\n",
220 f->name->full_name));
229 form the lock context used for byte range locking and opendb
230 locking. Note that we must zero here to take account of
231 possible padding on some architectures
233 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
234 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
240 ZERO_STRUCT(lock_context);
242 lock_context.device = name->st.st_dev;
243 lock_context.inode = name->st.st_ino;
245 *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
246 if (key->data == NULL) {
247 return NT_STATUS_NO_MEMORY;
257 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
258 struct smbsrv_request *req,
259 struct pvfs_filename *name,
265 struct odb_lock *lck;
266 uint32_t create_options = io->generic.in.create_options;
267 uint32_t share_access = io->generic.in.share_access;
268 uint32_t access_mask = io->generic.in.access_mask;
271 if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
272 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
273 return NT_STATUS_CANNOT_DELETE;
278 f = talloc_p(req, struct pvfs_file);
280 return NT_STATUS_NO_MEMORY;
283 fnum = idr_get_new(pvfs->idtree_fnum, f, UINT16_MAX);
285 return NT_STATUS_TOO_MANY_OPENED_FILES;
288 if (io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) {
294 /* create the file */
295 fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
297 idr_remove(pvfs->idtree_fnum, fnum);
298 return pvfs_map_errno(pvfs, errno);
301 /* re-resolve the open fd */
302 status = pvfs_resolve_name_fd(pvfs, fd, name);
303 if (!NT_STATUS_IS_OK(status)) {
304 idr_remove(pvfs->idtree_fnum, fnum);
309 /* form the lock context used for byte range locking and
311 status = pvfs_locking_key(name, f, &f->locking_key);
312 if (!NT_STATUS_IS_OK(status)) {
313 idr_remove(pvfs->idtree_fnum, fnum);
318 /* grab a lock on the open file record */
319 lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
321 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
323 /* we were supposed to do a blocking lock, so something
325 idr_remove(pvfs->idtree_fnum, fnum);
327 return NT_STATUS_INTERNAL_DB_CORRUPTION;
330 status = odb_open_file(lck, fnum, share_access, create_options, access_mask);
331 if (!NT_STATUS_IS_OK(status)) {
332 /* bad news, we must have hit a race */
333 idr_remove(pvfs->idtree_fnum, fnum);
340 f->name = talloc_steal(f, name);
341 f->session = req->session;
342 f->smbpid = req->smbpid;
344 f->pending_list = NULL;
346 f->create_options = io->generic.in.create_options;
347 f->share_access = io->generic.in.share_access;
348 f->access_mask = io->generic.in.access_mask;
350 DLIST_ADD(pvfs->open_files, f);
352 /* setup a destructor to avoid file descriptor leaks on
353 abnormal termination */
354 talloc_set_destructor(f, pvfs_fd_destructor);
356 io->generic.out.oplock_level = NO_OPLOCK;
357 io->generic.out.fnum = f->fnum;
358 io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
359 io->generic.out.create_time = name->dos.create_time;
360 io->generic.out.access_time = name->dos.access_time;
361 io->generic.out.write_time = name->dos.write_time;
362 io->generic.out.change_time = name->dos.change_time;
363 io->generic.out.attrib = name->dos.attrib;
364 io->generic.out.alloc_size = name->dos.alloc_size;
365 io->generic.out.size = name->st.st_size;
366 io->generic.out.file_type = FILE_TYPE_DISK;
367 io->generic.out.ipc_state = 0;
368 io->generic.out.is_directory = 0;
370 /* success - keep the file handle */
371 talloc_steal(pvfs, f);
380 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
381 struct smbsrv_request *req, union smb_open *io)
383 struct pvfs_state *pvfs = ntvfs->private_data;
385 struct pvfs_filename *name;
389 struct odb_lock *lck;
390 uint32_t create_options;
391 uint32_t share_access;
392 uint32_t access_mask;
394 /* use the generic mapping code to avoid implementing all the
395 different open calls. This won't allow openx to work
396 perfectly as the mapping code has no way of knowing if two
397 opens are on the same connection, so this will need to
399 if (io->generic.level != RAW_OPEN_GENERIC) {
400 return ntvfs_map_open(req, io, ntvfs);
403 /* resolve the cifs name to a posix name */
404 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
405 PVFS_RESOLVE_NO_WILDCARD, &name);
406 if (!NT_STATUS_IS_OK(status)) {
410 /* directory opens are handled separately */
411 if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
412 (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
413 return pvfs_open_directory(pvfs, req, name, io);
416 create_options = io->generic.in.create_options;
417 share_access = io->generic.in.share_access;
418 access_mask = io->generic.in.access_mask;
420 /* certain create options are not allowed */
421 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
422 !(access_mask & STD_RIGHT_DELETE_ACCESS)) {
423 return NT_STATUS_INVALID_PARAMETER;
426 switch (io->generic.in.open_disposition) {
427 case NTCREATEX_DISP_SUPERSEDE:
429 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
434 case NTCREATEX_DISP_OVERWRITE_IF:
438 case NTCREATEX_DISP_OPEN:
440 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
445 case NTCREATEX_DISP_OVERWRITE:
447 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
452 case NTCREATEX_DISP_CREATE:
454 return NT_STATUS_OBJECT_NAME_COLLISION;
459 case NTCREATEX_DISP_OPEN_IF:
464 return NT_STATUS_INVALID_PARAMETER;
469 /* handle creating a new file separately */
471 status = pvfs_create_file(pvfs, req, name, io);
472 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
476 /* we've hit a race - the file was created during this call */
477 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
481 /* try re-resolving the name */
482 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
483 PVFS_RESOLVE_NO_WILDCARD, &name);
484 if (!NT_STATUS_IS_OK(status)) {
487 /* fall through to a normal open */
490 if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
491 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
492 return NT_STATUS_CANNOT_DELETE;
495 f = talloc_p(req, struct pvfs_file);
497 return NT_STATUS_NO_MEMORY;
500 /* allocate a fnum */
501 fnum = idr_get_new(pvfs->idtree_fnum, f, UINT16_MAX);
503 return NT_STATUS_TOO_MANY_OPENED_FILES;
506 /* form the lock context used for byte range locking and
508 status = pvfs_locking_key(name, f, &f->locking_key);
509 if (!NT_STATUS_IS_OK(status)) {
510 idr_remove(pvfs->idtree_fnum, fnum);
514 /* get a lock on this file before the actual open */
515 lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
517 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
519 /* we were supposed to do a blocking lock, so something
521 idr_remove(pvfs->idtree_fnum, fnum);
522 return NT_STATUS_INTERNAL_DB_CORRUPTION;
525 /* see if we are allowed to open at the same time as existing opens */
526 status = odb_open_file(lck, fnum, share_access, create_options, access_mask);
527 if (!NT_STATUS_IS_OK(status)) {
528 idr_remove(pvfs->idtree_fnum, fnum);
534 f->name = talloc_steal(f, name);
535 f->session = req->session;
536 f->smbpid = req->smbpid;
538 f->pending_list = NULL;
540 f->create_options = io->generic.in.create_options;
541 f->share_access = io->generic.in.share_access;
542 f->access_mask = io->generic.in.access_mask;
544 DLIST_ADD(pvfs->open_files, f);
546 /* setup a destructor to avoid file descriptor leaks on
547 abnormal termination */
548 talloc_set_destructor(f, pvfs_fd_destructor);
550 /* do the actual open */
551 fd = open(name->full_name, flags);
553 return pvfs_map_errno(pvfs, errno);
558 /* re-resolve the open fd */
559 status = pvfs_resolve_name_fd(pvfs, fd, name);
560 if (!NT_STATUS_IS_OK(status)) {
564 io->generic.out.oplock_level = NO_OPLOCK;
565 io->generic.out.fnum = f->fnum;
566 io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
567 io->generic.out.create_time = name->dos.create_time;
568 io->generic.out.access_time = name->dos.access_time;
569 io->generic.out.write_time = name->dos.write_time;
570 io->generic.out.change_time = name->dos.change_time;
571 io->generic.out.attrib = name->dos.attrib;
572 io->generic.out.alloc_size = name->dos.alloc_size;
573 io->generic.out.size = name->st.st_size;
574 io->generic.out.file_type = FILE_TYPE_DISK;
575 io->generic.out.ipc_state = 0;
576 io->generic.out.is_directory = 0;
578 /* success - keep the file handle */
579 talloc_steal(pvfs, f);
588 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
589 struct smbsrv_request *req, union smb_close *io)
591 struct pvfs_state *pvfs = ntvfs->private_data;
595 if (io->generic.level != RAW_CLOSE_CLOSE) {
596 return ntvfs_map_close(req, io, ntvfs);
599 f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
601 return NT_STATUS_INVALID_HANDLE;
605 close(f->fd) == -1) {
606 status = pvfs_map_errno(pvfs, errno);
608 status = NT_STATUS_OK;
612 /* the destructor takes care of the rest */
620 logoff - close all file descriptors open by a vuid
622 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
623 struct smbsrv_request *req)
625 struct pvfs_state *pvfs = ntvfs->private_data;
626 struct pvfs_file *f, *next;
628 for (f=pvfs->open_files;f;f=next) {
630 if (f->session == req->session) {
631 DLIST_REMOVE(pvfs->open_files, f);
641 exit - close files for the current pid
643 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
644 struct smbsrv_request *req)
646 struct pvfs_state *pvfs = ntvfs->private_data;
647 struct pvfs_file *f, *next;
649 for (f=pvfs->open_files;f;f=next) {
651 if (f->smbpid == req->smbpid) {
652 DLIST_REMOVE(pvfs->open_files, f);
662 change the create options on an already open file
664 NTSTATUS pvfs_change_create_options(struct pvfs_state *pvfs,
665 struct smbsrv_request *req,
666 struct pvfs_file *f, uint32_t create_options)
668 struct odb_lock *lck;
671 if (f->create_options == create_options) {
675 if ((f->name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
676 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
677 return NT_STATUS_CANNOT_DELETE;
680 lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
682 return NT_STATUS_INTERNAL_DB_CORRUPTION;
685 status = odb_set_create_options(lck, f->fnum, create_options);
686 if (NT_STATUS_IS_OK(status)) {
687 f->create_options = create_options;