ac2742d357939b00555bd3dbb97d417332ad85b2
[gd/samba-autobuild/.git] / source4 / ntvfs / posix / pvfs_open.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    POSIX NTVFS backend - open and close
5
6    Copyright (C) Andrew Tridgell 2004
7
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.
12    
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.
17    
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.
21 */
22
23 #include "include/includes.h"
24 #include "vfs_posix.h"
25
26 /*
27   create file handles with convenient numbers for sniffers
28 */
29 #define PVFS_MIN_FILE_FNUM 0x100
30 #define PVFS_MIN_NEW_FNUM 0x200
31 #define PVFS_MIN_DIR_FNUM 0x1000
32
33 /*
34   find open file handle given fnum
35 */
36 struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
37                                struct smbsrv_request *req, uint16_t fnum)
38 {
39         struct pvfs_file *f;
40
41         f = idr_find(pvfs->idtree_fnum, fnum);
42         if (f == NULL) {
43                 return NULL;
44         }
45
46         if (req->session != f->session) {
47                 DEBUG(2,("pvfs_find_fd: attempt to use wrong session for fnum %d\n", 
48                          fnum));
49                 return NULL;
50         }
51
52         return f;
53 }
54
55
56 /*
57   cleanup a open directory handle
58 */
59 static int pvfs_dir_fd_destructor(void *p)
60 {
61         struct pvfs_file *f = p;
62         DLIST_REMOVE(f->pvfs->open_files, f);
63         idr_remove(f->pvfs->idtree_fnum, f->fnum);
64
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", 
68                                  f->name->full_name));
69                 }
70         }
71
72         return 0;
73 }
74
75
76 /*
77   open a directory
78 */
79 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, 
80                                     struct smbsrv_request *req, 
81                                     struct pvfs_filename *name, 
82                                     union smb_open *io)
83 {
84         struct pvfs_file *f;
85         int fnum;
86         NTSTATUS status;
87         uint32_t create_action;
88
89         /* if the client says it must be a directory, and it isn't,
90            then fail */
91         if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
92                 return NT_STATUS_NOT_A_DIRECTORY;
93         }
94
95         switch (io->generic.in.open_disposition) {
96         case NTCREATEX_DISP_OPEN_IF:
97                 break;
98
99         case NTCREATEX_DISP_OPEN:
100                 if (!name->exists) {
101                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
102                 }
103                 break;
104
105         case NTCREATEX_DISP_CREATE:
106                 if (name->exists) {
107                         return NT_STATUS_OBJECT_NAME_COLLISION;
108                 }
109                 break;
110
111         case NTCREATEX_DISP_OVERWRITE_IF:
112         case NTCREATEX_DISP_OVERWRITE:
113         case NTCREATEX_DISP_SUPERSEDE:
114         default:
115                 return NT_STATUS_INVALID_PARAMETER;
116         }
117
118         f = talloc_p(req, struct pvfs_file);
119         if (f == NULL) {
120                 return NT_STATUS_NO_MEMORY;
121         }
122
123         fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_DIR_FNUM, UINT16_MAX);
124         if (fnum == -1) {
125                 talloc_free(f);
126                 return NT_STATUS_TOO_MANY_OPENED_FILES;
127         }
128
129         f->fnum = fnum;
130         f->fd = -1;
131         f->name = talloc_steal(f, name);
132         f->session = req->session;
133         f->smbpid = req->smbpid;
134         f->pvfs = pvfs;
135         f->pending_list = NULL;
136         f->lock_count = 0;
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;
140         f->seek_offset = 0;
141         f->position = 0;
142
143         DLIST_ADD(pvfs->open_files, f);
144
145         /* TODO: should we check in the opendb? Do directory opens 
146            follow the share_access rules? */
147
148
149         /* setup a destructor to avoid leaks on abnormal termination */
150         talloc_set_destructor(f, pvfs_dir_fd_destructor);
151
152         if (!name->exists) {
153                 if (mkdir(name->full_name, 0755) == -1) {
154                         return pvfs_map_errno(pvfs,errno);
155                 }
156                 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
157                                            PVFS_RESOLVE_NO_WILDCARD, &name);
158                 if (!NT_STATUS_IS_OK(status)) {
159                         return status;
160                 }
161                 create_action = NTCREATEX_ACTION_CREATED;
162         } else {
163                 create_action = NTCREATEX_ACTION_EXISTED;
164         }
165
166         if (!name->exists) {
167                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
168         }
169
170         /* the open succeeded, keep this handle permanently */
171         talloc_steal(pvfs, f);
172
173         io->generic.out.oplock_level  = NO_OPLOCK;
174         io->generic.out.fnum          = f->fnum;
175         io->generic.out.create_action = create_action;
176         io->generic.out.create_time   = name->dos.create_time;
177         io->generic.out.access_time   = name->dos.access_time;
178         io->generic.out.write_time    = name->dos.write_time;
179         io->generic.out.change_time   = name->dos.change_time;
180         io->generic.out.attrib        = name->dos.attrib;
181         io->generic.out.alloc_size    = 0;
182         io->generic.out.size          = 0;
183         io->generic.out.file_type     = FILE_TYPE_DISK;
184         io->generic.out.ipc_state     = 0;
185         io->generic.out.is_directory  = 1;
186
187         return NT_STATUS_OK;
188 }
189
190
191 /*
192   by using a destructor we make sure that abnormal cleanup will not 
193   leak file descriptors (assuming at least the top level pointer is freed, which
194   will cascade down to here)
195 */
196 static int pvfs_fd_destructor(void *p)
197 {
198         struct pvfs_file *f = p;
199         struct odb_lock *lck;
200         NTSTATUS status;
201
202         DLIST_REMOVE(f->pvfs->open_files, f);
203
204         pvfs_lock_close(f->pvfs, f);
205
206         if (f->fd != -1) {
207                 close(f->fd);
208                 f->fd = -1;
209         }
210
211         idr_remove(f->pvfs->idtree_fnum, f->fnum);
212
213         if (f->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
214                 if (unlink(f->name->full_name) != 0) {
215                         DEBUG(0,("pvfs_close: failed to delete '%s'\n", 
216                                  f->name->full_name));
217                 }
218         }
219
220         lck = odb_lock(f, f->pvfs->odb_context, &f->locking_key);
221         if (lck == NULL) {
222                 DEBUG(0,("Unable to lock opendb for close\n"));
223                 return 0;
224         }
225
226         status = odb_close_file(lck, f->fnum);
227         if (!NT_STATUS_IS_OK(status)) {
228                 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n", 
229                          f->name->full_name, nt_errstr(status)));
230         }
231
232         talloc_free(lck);
233
234         return 0;
235 }
236
237
238 /*
239   form the lock context used for byte range locking and opendb
240   locking. Note that we must zero here to take account of
241   possible padding on some architectures
242 */
243 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name, 
244                                  TALLOC_CTX *mem_ctx, DATA_BLOB *key)
245 {
246         struct {
247                 dev_t device;
248                 ino_t inode;
249         } lock_context;
250         ZERO_STRUCT(lock_context);
251
252         lock_context.device = name->st.st_dev;
253         lock_context.inode = name->st.st_ino;
254
255         *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
256         if (key->data == NULL) {
257                 return NT_STATUS_NO_MEMORY;
258         }
259         
260         return NT_STATUS_OK;
261 }
262
263
264 /*
265   create a new file
266 */
267 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, 
268                                  struct smbsrv_request *req, 
269                                  struct pvfs_filename *name, 
270                                  union smb_open *io)
271 {
272         struct pvfs_file *f;
273         NTSTATUS status;
274         int flags, fnum, fd;
275         struct odb_lock *lck;
276         uint32_t create_options = io->generic.in.create_options;
277         uint32_t share_access = io->generic.in.share_access;
278         uint32_t access_mask = io->generic.in.access_mask;
279         mode_t mode;
280
281         if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
282             (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
283                 return NT_STATUS_CANNOT_DELETE;
284         }
285         
286         flags = O_RDWR;
287
288         f = talloc_p(req, struct pvfs_file);
289         if (f == NULL) {
290                 return NT_STATUS_NO_MEMORY;
291         }
292
293         fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_NEW_FNUM, UINT16_MAX);
294         if (fnum == -1) {
295                 return NT_STATUS_TOO_MANY_OPENED_FILES;
296         }
297
298         if (io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) {
299                 mode = 0444;
300         } else {
301                 mode = 0644;
302         }
303
304         /* create the file */
305         fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
306         if (fd == -1) {
307                 idr_remove(pvfs->idtree_fnum, fnum);
308                 return pvfs_map_errno(pvfs, errno);
309         }
310
311         /* re-resolve the open fd */
312         status = pvfs_resolve_name_fd(pvfs, fd, name);
313         if (!NT_STATUS_IS_OK(status)) {
314                 idr_remove(pvfs->idtree_fnum, fnum);
315                 close(fd);
316                 return status;
317         }
318
319         /* form the lock context used for byte range locking and
320            opendb locking */
321         status = pvfs_locking_key(name, f, &f->locking_key);
322         if (!NT_STATUS_IS_OK(status)) {
323                 idr_remove(pvfs->idtree_fnum, fnum);
324                 close(fd);
325                 return status;
326         }
327
328         /* grab a lock on the open file record */
329         lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
330         if (lck == NULL) {
331                 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
332                          name->full_name));
333                 /* we were supposed to do a blocking lock, so something
334                    is badly wrong! */
335                 idr_remove(pvfs->idtree_fnum, fnum);
336                 close(fd);
337                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
338         }
339
340         status = odb_open_file(lck, fnum, share_access, create_options, access_mask);
341         if (!NT_STATUS_IS_OK(status)) {
342                 /* bad news, we must have hit a race */
343                 idr_remove(pvfs->idtree_fnum, fnum);
344                 close(fd);
345                 return status;
346         }
347
348         f->fnum = fnum;
349         f->fd = fd;
350         f->name = talloc_steal(f, name);
351         f->session = req->session;
352         f->smbpid = req->smbpid;
353         f->pvfs = pvfs;
354         f->pending_list = NULL;
355         f->lock_count = 0;
356         f->create_options = io->generic.in.create_options;
357         f->share_access = io->generic.in.share_access;
358         f->access_mask = io->generic.in.access_mask;
359         f->seek_offset = 0;
360         f->position = 0;
361
362         DLIST_ADD(pvfs->open_files, f);
363
364         /* setup a destructor to avoid file descriptor leaks on
365            abnormal termination */
366         talloc_set_destructor(f, pvfs_fd_destructor);
367
368         io->generic.out.oplock_level  = NO_OPLOCK;
369         io->generic.out.fnum          = f->fnum;
370         io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
371         io->generic.out.create_time   = name->dos.create_time;
372         io->generic.out.access_time   = name->dos.access_time;
373         io->generic.out.write_time    = name->dos.write_time;
374         io->generic.out.change_time   = name->dos.change_time;
375         io->generic.out.attrib        = name->dos.attrib;
376         io->generic.out.alloc_size    = name->dos.alloc_size;
377         io->generic.out.size          = name->st.st_size;
378         io->generic.out.file_type     = FILE_TYPE_DISK;
379         io->generic.out.ipc_state     = 0;
380         io->generic.out.is_directory  = 0;
381
382         /* success - keep the file handle */
383         talloc_steal(pvfs, f);
384
385         return NT_STATUS_OK;
386 }
387
388
389 /*
390   open a file
391 */
392 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
393                    struct smbsrv_request *req, union smb_open *io)
394 {
395         struct pvfs_state *pvfs = ntvfs->private_data;
396         int fd, flags;
397         struct pvfs_filename *name;
398         struct pvfs_file *f;
399         NTSTATUS status;
400         int fnum;
401         struct odb_lock *lck;
402         uint32_t create_options;
403         uint32_t share_access;
404         uint32_t access_mask;
405
406         /* use the generic mapping code to avoid implementing all the
407            different open calls. This won't allow openx to work
408            perfectly as the mapping code has no way of knowing if two
409            opens are on the same connection, so this will need to
410            change eventually */    
411         if (io->generic.level != RAW_OPEN_GENERIC) {
412                 return ntvfs_map_open(req, io, ntvfs);
413         }
414
415         /* resolve the cifs name to a posix name */
416         status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
417                                    PVFS_RESOLVE_NO_WILDCARD, &name);
418         if (!NT_STATUS_IS_OK(status)) {
419                 return status;
420         }
421
422         /* directory opens are handled separately */
423         if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
424             (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
425                 return pvfs_open_directory(pvfs, req, name, io);
426         }
427
428         create_options = io->generic.in.create_options;
429         share_access   = io->generic.in.share_access;
430         access_mask    = io->generic.in.access_mask;
431
432         /* certain create options are not allowed */
433         if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
434             !(access_mask & STD_RIGHT_DELETE_ACCESS)) {
435                 return NT_STATUS_INVALID_PARAMETER;
436         }
437
438         switch (io->generic.in.open_disposition) {
439         case NTCREATEX_DISP_SUPERSEDE:
440                 if (!name->exists) {
441                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
442                 }
443                 flags = O_TRUNC;
444                 break;
445
446         case NTCREATEX_DISP_OVERWRITE_IF:
447                 flags = O_TRUNC;
448                 break;
449
450         case NTCREATEX_DISP_OPEN:
451                 if (!name->exists) {
452                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
453                 }
454                 flags = 0;
455                 break;
456
457         case NTCREATEX_DISP_OVERWRITE:
458                 if (!name->exists) {
459                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
460                 }
461                 flags = O_TRUNC;
462                 break;
463
464         case NTCREATEX_DISP_CREATE:
465                 if (name->exists) {
466                         return NT_STATUS_OBJECT_NAME_COLLISION;
467                 }
468                 flags = 0;
469                 break;
470
471         case NTCREATEX_DISP_OPEN_IF:
472                 flags = 0;
473                 break;
474
475         default:
476                 return NT_STATUS_INVALID_PARAMETER;
477         }
478
479         flags |= O_RDWR;
480
481         /* handle creating a new file separately */
482         if (!name->exists) {
483                 status = pvfs_create_file(pvfs, req, name, io);
484                 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
485                         return status;
486                 }
487
488                 /* we've hit a race - the file was created during this call */
489                 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
490                         return status;
491                 }
492
493                 /* try re-resolving the name */
494                 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
495                                            PVFS_RESOLVE_NO_WILDCARD, &name);
496                 if (!NT_STATUS_IS_OK(status)) {
497                         return status;
498                 }
499                 /* fall through to a normal open */
500         }
501
502         if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
503             (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
504                 return NT_STATUS_CANNOT_DELETE;
505         }
506
507         f = talloc_p(req, struct pvfs_file);
508         if (f == NULL) {
509                 return NT_STATUS_NO_MEMORY;
510         }
511
512         /* allocate a fnum */
513         fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
514         if (fnum == -1) {
515                 return NT_STATUS_TOO_MANY_OPENED_FILES;
516         }
517
518         /* form the lock context used for byte range locking and
519            opendb locking */
520         status = pvfs_locking_key(name, f, &f->locking_key);
521         if (!NT_STATUS_IS_OK(status)) {
522                 idr_remove(pvfs->idtree_fnum, fnum);
523                 return status;
524         }
525
526         /* get a lock on this file before the actual open */
527         lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
528         if (lck == NULL) {
529                 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
530                          name->full_name));
531                 /* we were supposed to do a blocking lock, so something
532                    is badly wrong! */
533                 idr_remove(pvfs->idtree_fnum, fnum);
534                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
535         }
536
537         /* see if we are allowed to open at the same time as existing opens */
538         status = odb_open_file(lck, fnum, share_access, create_options, access_mask);
539         if (!NT_STATUS_IS_OK(status)) {
540                 idr_remove(pvfs->idtree_fnum, fnum);
541                 return status;
542         }
543
544         f->fnum = fnum;
545         f->fd = -1;
546         f->name = talloc_steal(f, name);
547         f->session = req->session;
548         f->smbpid = req->smbpid;
549         f->pvfs = pvfs;
550         f->pending_list = NULL;
551         f->lock_count = 0;
552         f->create_options = io->generic.in.create_options;
553         f->share_access = io->generic.in.share_access;
554         f->access_mask = io->generic.in.access_mask;
555         f->seek_offset = 0;
556         f->position = 0;
557
558         DLIST_ADD(pvfs->open_files, f);
559
560         /* setup a destructor to avoid file descriptor leaks on
561            abnormal termination */
562         talloc_set_destructor(f, pvfs_fd_destructor);
563
564         /* do the actual open */
565         fd = open(name->full_name, flags);
566         if (fd == -1) {
567                 return pvfs_map_errno(pvfs, errno);
568         }
569
570         f->fd = fd;
571
572         /* re-resolve the open fd */
573         status = pvfs_resolve_name_fd(pvfs, fd, name);
574         if (!NT_STATUS_IS_OK(status)) {
575                 return status;
576         }
577
578         io->generic.out.oplock_level  = NO_OPLOCK;
579         io->generic.out.fnum          = f->fnum;
580         io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
581         io->generic.out.create_time   = name->dos.create_time;
582         io->generic.out.access_time   = name->dos.access_time;
583         io->generic.out.write_time    = name->dos.write_time;
584         io->generic.out.change_time   = name->dos.change_time;
585         io->generic.out.attrib        = name->dos.attrib;
586         io->generic.out.alloc_size    = name->dos.alloc_size;
587         io->generic.out.size          = name->st.st_size;
588         io->generic.out.file_type     = FILE_TYPE_DISK;
589         io->generic.out.ipc_state     = 0;
590         io->generic.out.is_directory  = 0;
591
592         /* success - keep the file handle */
593         talloc_steal(pvfs, f);
594
595         return NT_STATUS_OK;
596 }
597
598
599 /*
600   close a file
601 */
602 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
603                     struct smbsrv_request *req, union smb_close *io)
604 {
605         struct pvfs_state *pvfs = ntvfs->private_data;
606         struct pvfs_file *f;
607         NTSTATUS status;
608         struct utimbuf unix_times;
609
610         if (io->generic.level != RAW_CLOSE_CLOSE) {
611                 return ntvfs_map_close(req, io, ntvfs);
612         }
613
614         f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
615         if (!f) {
616                 return NT_STATUS_INVALID_HANDLE;
617         }
618
619         unix_times.actime = 0;
620         unix_times.modtime = io->close.in.write_time;
621         utime(f->name->full_name, &unix_times);
622         
623         if (f->fd != -1 && 
624             close(f->fd) == -1) {
625                 status = pvfs_map_errno(pvfs, errno);
626         } else {
627                 status = NT_STATUS_OK;
628         }
629         f->fd = -1;
630
631         /* the destructor takes care of the rest */
632         talloc_free(f);
633
634         return status;
635 }
636
637
638 /*
639   logoff - close all file descriptors open by a vuid
640 */
641 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
642                      struct smbsrv_request *req)
643 {
644         struct pvfs_state *pvfs = ntvfs->private_data;
645         struct pvfs_file *f, *next;
646
647         for (f=pvfs->open_files;f;f=next) {
648                 next = f->next;
649                 if (f->session == req->session) {
650                         DLIST_REMOVE(pvfs->open_files, f);
651                         talloc_free(f);
652                 }
653         }
654
655         return NT_STATUS_OK;
656 }
657
658
659 /*
660   exit - close files for the current pid
661 */
662 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
663                    struct smbsrv_request *req)
664 {
665         struct pvfs_state *pvfs = ntvfs->private_data;
666         struct pvfs_file *f, *next;
667
668         for (f=pvfs->open_files;f;f=next) {
669                 next = f->next;
670                 if (f->smbpid == req->smbpid) {
671                         DLIST_REMOVE(pvfs->open_files, f);
672                         talloc_free(f);
673                 }
674         }
675
676         return NT_STATUS_OK;
677 }
678
679
680 /*
681   change the create options on an already open file
682 */
683 NTSTATUS pvfs_change_create_options(struct pvfs_state *pvfs,
684                                     struct smbsrv_request *req, 
685                                     struct pvfs_file *f, uint32_t create_options)
686 {
687         struct odb_lock *lck;
688         NTSTATUS status;
689
690         if (f->create_options == create_options) {
691                 return NT_STATUS_OK;
692         }
693
694         if ((f->name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
695             (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
696                 return NT_STATUS_CANNOT_DELETE;
697         }
698
699         lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
700         if (lck == NULL) {
701                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
702         }
703
704         status = odb_set_create_options(lck, f->fnum, create_options);
705         if (NT_STATUS_IS_OK(status)) {
706                 f->create_options = create_options;
707         }
708
709         return status;
710 }
711
712
713 /*
714   determine if a file is open - used to prevent some operations on open files
715 */
716 BOOL pvfs_is_open(struct pvfs_state *pvfs, struct pvfs_filename *name)
717 {
718         NTSTATUS status;
719         DATA_BLOB key;
720
721         status = pvfs_locking_key(name, name, &key);
722         if (!NT_STATUS_IS_OK(status)) {
723                 return False;
724         }
725
726         return odb_is_open(pvfs->odb_context, &key);
727 }