r3180: - basic support for SEC_RIGHT_MAXIMUM_ALLOWED in pvfs
[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         if (access_mask & SEC_RIGHT_MAXIMUM_ALLOWED) {
287                 access_mask = GENERIC_RIGHTS_FILE_READ | GENERIC_RIGHTS_FILE_WRITE;
288         }
289
290         flags = O_RDWR;
291
292         f = talloc_p(req, struct pvfs_file);
293         if (f == NULL) {
294                 return NT_STATUS_NO_MEMORY;
295         }
296
297         fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_NEW_FNUM, UINT16_MAX);
298         if (fnum == -1) {
299                 return NT_STATUS_TOO_MANY_OPENED_FILES;
300         }
301
302         if (io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) {
303                 mode = 0444;
304         } else {
305                 mode = 0644;
306         }
307
308         /* create the file */
309         fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
310         if (fd == -1) {
311                 idr_remove(pvfs->idtree_fnum, fnum);
312                 return pvfs_map_errno(pvfs, errno);
313         }
314
315         /* re-resolve the open fd */
316         status = pvfs_resolve_name_fd(pvfs, fd, name);
317         if (!NT_STATUS_IS_OK(status)) {
318                 idr_remove(pvfs->idtree_fnum, fnum);
319                 close(fd);
320                 return status;
321         }
322
323         /* form the lock context used for byte range locking and
324            opendb locking */
325         status = pvfs_locking_key(name, f, &f->locking_key);
326         if (!NT_STATUS_IS_OK(status)) {
327                 idr_remove(pvfs->idtree_fnum, fnum);
328                 close(fd);
329                 return status;
330         }
331
332         /* grab a lock on the open file record */
333         lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
334         if (lck == NULL) {
335                 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
336                          name->full_name));
337                 /* we were supposed to do a blocking lock, so something
338                    is badly wrong! */
339                 idr_remove(pvfs->idtree_fnum, fnum);
340                 close(fd);
341                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
342         }
343
344         status = odb_open_file(lck, fnum, share_access, create_options, access_mask);
345         if (!NT_STATUS_IS_OK(status)) {
346                 /* bad news, we must have hit a race */
347                 idr_remove(pvfs->idtree_fnum, fnum);
348                 close(fd);
349                 return status;
350         }
351
352         f->fnum = fnum;
353         f->fd = fd;
354         f->name = talloc_steal(f, name);
355         f->session = req->session;
356         f->smbpid = req->smbpid;
357         f->pvfs = pvfs;
358         f->pending_list = NULL;
359         f->lock_count = 0;
360         f->create_options = io->generic.in.create_options;
361         f->share_access = io->generic.in.share_access;
362         f->access_mask = access_mask;
363         f->seek_offset = 0;
364         f->position = 0;
365
366         DLIST_ADD(pvfs->open_files, f);
367
368         /* setup a destructor to avoid file descriptor leaks on
369            abnormal termination */
370         talloc_set_destructor(f, pvfs_fd_destructor);
371
372         io->generic.out.oplock_level  = NO_OPLOCK;
373         io->generic.out.fnum          = f->fnum;
374         io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
375         io->generic.out.create_time   = name->dos.create_time;
376         io->generic.out.access_time   = name->dos.access_time;
377         io->generic.out.write_time    = name->dos.write_time;
378         io->generic.out.change_time   = name->dos.change_time;
379         io->generic.out.attrib        = name->dos.attrib;
380         io->generic.out.alloc_size    = name->dos.alloc_size;
381         io->generic.out.size          = name->st.st_size;
382         io->generic.out.file_type     = FILE_TYPE_DISK;
383         io->generic.out.ipc_state     = 0;
384         io->generic.out.is_directory  = 0;
385
386         /* success - keep the file handle */
387         talloc_steal(pvfs, f);
388
389         return NT_STATUS_OK;
390 }
391
392
393 /*
394   open a file
395 */
396 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
397                    struct smbsrv_request *req, union smb_open *io)
398 {
399         struct pvfs_state *pvfs = ntvfs->private_data;
400         int fd, flags;
401         struct pvfs_filename *name;
402         struct pvfs_file *f;
403         NTSTATUS status;
404         int fnum;
405         struct odb_lock *lck;
406         uint32_t create_options;
407         uint32_t share_access;
408         uint32_t access_mask;
409
410         /* use the generic mapping code to avoid implementing all the
411            different open calls. This won't allow openx to work
412            perfectly as the mapping code has no way of knowing if two
413            opens are on the same connection, so this will need to
414            change eventually */    
415         if (io->generic.level != RAW_OPEN_GENERIC) {
416                 return ntvfs_map_open(req, io, ntvfs);
417         }
418
419         /* resolve the cifs name to a posix name */
420         status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
421                                    PVFS_RESOLVE_NO_WILDCARD, &name);
422         if (!NT_STATUS_IS_OK(status)) {
423                 return status;
424         }
425
426         /* directory opens are handled separately */
427         if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
428             (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
429                 return pvfs_open_directory(pvfs, req, name, io);
430         }
431
432         create_options = io->generic.in.create_options;
433         share_access   = io->generic.in.share_access;
434         access_mask    = io->generic.in.access_mask;
435
436         if (access_mask & SEC_RIGHT_MAXIMUM_ALLOWED) {
437                 if (name->dos.attrib & FILE_ATTRIBUTE_READONLY) {
438                         access_mask = GENERIC_RIGHTS_FILE_READ;
439                 } else {
440                         access_mask = GENERIC_RIGHTS_FILE_READ | GENERIC_RIGHTS_FILE_WRITE;
441                 }
442         }
443
444         /* certain create options are not allowed */
445         if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
446             !(access_mask & STD_RIGHT_DELETE_ACCESS)) {
447                 return NT_STATUS_INVALID_PARAMETER;
448         }
449
450         switch (io->generic.in.open_disposition) {
451         case NTCREATEX_DISP_SUPERSEDE:
452                 if (!name->exists) {
453                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
454                 }
455                 flags = O_TRUNC;
456                 break;
457
458         case NTCREATEX_DISP_OVERWRITE_IF:
459                 flags = O_TRUNC;
460                 break;
461
462         case NTCREATEX_DISP_OPEN:
463                 if (!name->exists) {
464                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
465                 }
466                 flags = 0;
467                 break;
468
469         case NTCREATEX_DISP_OVERWRITE:
470                 if (!name->exists) {
471                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
472                 }
473                 flags = O_TRUNC;
474                 break;
475
476         case NTCREATEX_DISP_CREATE:
477                 if (name->exists) {
478                         return NT_STATUS_OBJECT_NAME_COLLISION;
479                 }
480                 flags = 0;
481                 break;
482
483         case NTCREATEX_DISP_OPEN_IF:
484                 flags = 0;
485                 break;
486
487         default:
488                 return NT_STATUS_INVALID_PARAMETER;
489         }
490
491         flags |= O_RDWR;
492
493         /* handle creating a new file separately */
494         if (!name->exists) {
495                 status = pvfs_create_file(pvfs, req, name, io);
496                 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
497                         return status;
498                 }
499
500                 /* we've hit a race - the file was created during this call */
501                 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
502                         return status;
503                 }
504
505                 /* try re-resolving the name */
506                 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
507                                            PVFS_RESOLVE_NO_WILDCARD, &name);
508                 if (!NT_STATUS_IS_OK(status)) {
509                         return status;
510                 }
511                 /* fall through to a normal open */
512         }
513
514         if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
515             (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
516                 return NT_STATUS_CANNOT_DELETE;
517         }
518
519         f = talloc_p(req, struct pvfs_file);
520         if (f == NULL) {
521                 return NT_STATUS_NO_MEMORY;
522         }
523
524         /* allocate a fnum */
525         fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
526         if (fnum == -1) {
527                 return NT_STATUS_TOO_MANY_OPENED_FILES;
528         }
529
530         /* form the lock context used for byte range locking and
531            opendb locking */
532         status = pvfs_locking_key(name, f, &f->locking_key);
533         if (!NT_STATUS_IS_OK(status)) {
534                 idr_remove(pvfs->idtree_fnum, fnum);
535                 return status;
536         }
537
538         /* get a lock on this file before the actual open */
539         lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
540         if (lck == NULL) {
541                 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
542                          name->full_name));
543                 /* we were supposed to do a blocking lock, so something
544                    is badly wrong! */
545                 idr_remove(pvfs->idtree_fnum, fnum);
546                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
547         }
548
549         /* see if we are allowed to open at the same time as existing opens */
550         status = odb_open_file(lck, fnum, share_access, create_options, access_mask);
551         if (!NT_STATUS_IS_OK(status)) {
552                 idr_remove(pvfs->idtree_fnum, fnum);
553                 return status;
554         }
555
556         f->fnum = fnum;
557         f->fd = -1;
558         f->name = talloc_steal(f, name);
559         f->session = req->session;
560         f->smbpid = req->smbpid;
561         f->pvfs = pvfs;
562         f->pending_list = NULL;
563         f->lock_count = 0;
564         f->create_options = io->generic.in.create_options;
565         f->share_access = io->generic.in.share_access;
566         f->access_mask = access_mask;
567         f->seek_offset = 0;
568         f->position = 0;
569
570         DLIST_ADD(pvfs->open_files, f);
571
572         /* setup a destructor to avoid file descriptor leaks on
573            abnormal termination */
574         talloc_set_destructor(f, pvfs_fd_destructor);
575
576         /* do the actual open */
577         fd = open(name->full_name, flags);
578         if (fd == -1) {
579                 return pvfs_map_errno(pvfs, errno);
580         }
581
582         f->fd = fd;
583
584         /* re-resolve the open fd */
585         status = pvfs_resolve_name_fd(pvfs, fd, name);
586         if (!NT_STATUS_IS_OK(status)) {
587                 return status;
588         }
589
590         io->generic.out.oplock_level  = NO_OPLOCK;
591         io->generic.out.fnum          = f->fnum;
592         io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
593         io->generic.out.create_time   = name->dos.create_time;
594         io->generic.out.access_time   = name->dos.access_time;
595         io->generic.out.write_time    = name->dos.write_time;
596         io->generic.out.change_time   = name->dos.change_time;
597         io->generic.out.attrib        = name->dos.attrib;
598         io->generic.out.alloc_size    = name->dos.alloc_size;
599         io->generic.out.size          = name->st.st_size;
600         io->generic.out.file_type     = FILE_TYPE_DISK;
601         io->generic.out.ipc_state     = 0;
602         io->generic.out.is_directory  = 0;
603
604         /* success - keep the file handle */
605         talloc_steal(pvfs, f);
606
607         return NT_STATUS_OK;
608 }
609
610
611 /*
612   close a file
613 */
614 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
615                     struct smbsrv_request *req, union smb_close *io)
616 {
617         struct pvfs_state *pvfs = ntvfs->private_data;
618         struct pvfs_file *f;
619         NTSTATUS status;
620         struct utimbuf unix_times;
621
622         if (io->generic.level != RAW_CLOSE_CLOSE) {
623                 return ntvfs_map_close(req, io, ntvfs);
624         }
625
626         f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
627         if (!f) {
628                 return NT_STATUS_INVALID_HANDLE;
629         }
630
631         unix_times.actime = 0;
632         unix_times.modtime = io->close.in.write_time;
633         utime(f->name->full_name, &unix_times);
634         
635         if (f->fd != -1 && 
636             close(f->fd) == -1) {
637                 status = pvfs_map_errno(pvfs, errno);
638         } else {
639                 status = NT_STATUS_OK;
640         }
641         f->fd = -1;
642
643         /* the destructor takes care of the rest */
644         talloc_free(f);
645
646         return status;
647 }
648
649
650 /*
651   logoff - close all file descriptors open by a vuid
652 */
653 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
654                      struct smbsrv_request *req)
655 {
656         struct pvfs_state *pvfs = ntvfs->private_data;
657         struct pvfs_file *f, *next;
658
659         for (f=pvfs->open_files;f;f=next) {
660                 next = f->next;
661                 if (f->session == req->session) {
662                         DLIST_REMOVE(pvfs->open_files, f);
663                         talloc_free(f);
664                 }
665         }
666
667         return NT_STATUS_OK;
668 }
669
670
671 /*
672   exit - close files for the current pid
673 */
674 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
675                    struct smbsrv_request *req)
676 {
677         struct pvfs_state *pvfs = ntvfs->private_data;
678         struct pvfs_file *f, *next;
679
680         for (f=pvfs->open_files;f;f=next) {
681                 next = f->next;
682                 if (f->smbpid == req->smbpid) {
683                         DLIST_REMOVE(pvfs->open_files, f);
684                         talloc_free(f);
685                 }
686         }
687
688         return NT_STATUS_OK;
689 }
690
691
692 /*
693   change the create options on an already open file
694 */
695 NTSTATUS pvfs_change_create_options(struct pvfs_state *pvfs,
696                                     struct smbsrv_request *req, 
697                                     struct pvfs_file *f, uint32_t create_options)
698 {
699         struct odb_lock *lck;
700         NTSTATUS status;
701
702         if (f->create_options == create_options) {
703                 return NT_STATUS_OK;
704         }
705
706         if ((f->name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
707             (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
708                 return NT_STATUS_CANNOT_DELETE;
709         }
710
711         lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
712         if (lck == NULL) {
713                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
714         }
715
716         status = odb_set_create_options(lck, f->fnum, create_options);
717         if (NT_STATUS_IS_OK(status)) {
718                 f->create_options = create_options;
719         }
720
721         return status;
722 }
723
724
725 /*
726   determine if a file is open - used to prevent some operations on open files
727 */
728 BOOL pvfs_is_open(struct pvfs_state *pvfs, struct pvfs_filename *name)
729 {
730         NTSTATUS status;
731         DATA_BLOB key;
732
733         status = pvfs_locking_key(name, name, &key);
734         if (!NT_STATUS_IS_OK(status)) {
735                 return False;
736         }
737
738         return odb_is_open(pvfs->odb_context, &key);
739 }