r4011: get rid of rpc_secdes.h and replace it with a single sane set of
[samba.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 "includes.h"
24 #include "vfs_posix.h"
25 #include "system/time.h"
26 #include "system/filesys.h"
27 #include "dlinklist.h"
28 #include "messages.h"
29 #include "librpc/gen_ndr/ndr_xattr.h"
30
31 /*
32   create file handles with convenient numbers for sniffers
33 */
34 #define PVFS_MIN_FILE_FNUM 0x100
35 #define PVFS_MIN_NEW_FNUM  0x200
36 #define PVFS_MIN_DIR_FNUM  0x300
37
38 /*
39   find open file handle given fnum
40 */
41 struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
42                                struct smbsrv_request *req, uint16_t fnum)
43 {
44         struct pvfs_file *f;
45
46         f = idr_find(pvfs->idtree_fnum, fnum);
47         if (f == NULL) {
48                 return NULL;
49         }
50
51         if (f->fnum != fnum) {
52                 smb_panic("pvfs_find_fd: idtree_fnum corruption\n");
53         }
54
55         if (req->session != f->session) {
56                 DEBUG(2,("pvfs_find_fd: attempt to use wrong session for fnum %d\n", 
57                          fnum));
58                 return NULL;
59         }
60
61         return f;
62 }
63
64
65 /*
66   cleanup a open directory handle
67 */
68 static int pvfs_dir_handle_destructor(void *p)
69 {
70         struct pvfs_file_handle *h = p;
71
72         if (h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
73                 if (rmdir(h->name->full_name) != 0) {
74                         DEBUG(0,("pvfs_close: failed to rmdir '%s' - %s\n", 
75                                  h->name->full_name, strerror(errno)));
76                 }
77         }
78
79         return 0;
80 }
81
82 /*
83   cleanup a open directory fnum
84 */
85 static int pvfs_dir_fnum_destructor(void *p)
86 {
87         struct pvfs_file *f = p;
88         DLIST_REMOVE(f->pvfs->open_files, f);
89         idr_remove(f->pvfs->idtree_fnum, f->fnum);
90         return 0;
91 }
92
93
94 /*
95   open a directory
96 */
97 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, 
98                                     struct smbsrv_request *req, 
99                                     struct pvfs_filename *name, 
100                                     union smb_open *io)
101 {
102         struct pvfs_file *f;
103         int fnum;
104         NTSTATUS status;
105         uint32_t create_action;
106
107         if (name->stream_name) {
108                 return NT_STATUS_NOT_A_DIRECTORY;
109         }
110
111         /* if the client says it must be a directory, and it isn't,
112            then fail */
113         if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
114                 return NT_STATUS_NOT_A_DIRECTORY;
115         }
116
117         switch (io->generic.in.open_disposition) {
118         case NTCREATEX_DISP_OPEN_IF:
119                 break;
120
121         case NTCREATEX_DISP_OPEN:
122                 if (!name->exists) {
123                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
124                 }
125                 break;
126
127         case NTCREATEX_DISP_CREATE:
128                 if (name->exists) {
129                         return NT_STATUS_OBJECT_NAME_COLLISION;
130                 }
131                 break;
132
133         case NTCREATEX_DISP_OVERWRITE_IF:
134         case NTCREATEX_DISP_OVERWRITE:
135         case NTCREATEX_DISP_SUPERSEDE:
136         default:
137                 return NT_STATUS_INVALID_PARAMETER;
138         }
139
140         f = talloc_p(req, struct pvfs_file);
141         if (f == NULL) {
142                 return NT_STATUS_NO_MEMORY;
143         }
144
145         f->handle = talloc_p(f, struct pvfs_file_handle);
146         if (f->handle == NULL) {
147                 return NT_STATUS_NO_MEMORY;
148         }
149
150         fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_DIR_FNUM, UINT16_MAX);
151         if (fnum == -1) {
152                 return NT_STATUS_TOO_MANY_OPENED_FILES;
153         }
154
155         f->fnum          = fnum;
156         f->session       = req->session;
157         f->smbpid        = req->smbpid;
158         f->pvfs          = pvfs;
159         f->pending_list  = NULL;
160         f->lock_count    = 0;
161         f->share_access  = io->generic.in.share_access;
162         f->impersonation = io->generic.in.impersonation;
163
164         f->handle->pvfs              = pvfs;
165         f->handle->name              = talloc_steal(f->handle, name);
166         f->handle->fd                = -1;
167         f->handle->odb_locking_key   = data_blob(NULL, 0);
168         f->handle->brl_locking_key   = data_blob(NULL, 0);
169         f->handle->create_options    = io->generic.in.create_options;
170         f->handle->seek_offset       = 0;
171         f->handle->position          = 0;
172         f->handle->mode              = 0;
173         f->handle->sticky_write_time = False;
174
175         DLIST_ADD(pvfs->open_files, f);
176
177         /* TODO: should we check in the opendb? Do directory opens 
178            follow the share_access rules? */
179
180         /* setup destructors to avoid leaks on abnormal termination */
181         talloc_set_destructor(f->handle, pvfs_dir_handle_destructor);
182         talloc_set_destructor(f, pvfs_dir_fnum_destructor);
183
184         if (!name->exists) {
185                 uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
186                 mode_t mode = pvfs_fileperms(pvfs, attrib);
187                 if (mkdir(name->full_name, mode) == -1) {
188                         return pvfs_map_errno(pvfs,errno);
189                 }
190                 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
191                 if (!NT_STATUS_IS_OK(status)) {
192                         return status;
193                 }
194                 create_action = NTCREATEX_ACTION_CREATED;
195         } else {
196                 create_action = NTCREATEX_ACTION_EXISTED;
197         }
198
199         if (!name->exists) {
200                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
201         }
202
203         /* the open succeeded, keep this handle permanently */
204         talloc_steal(pvfs, f);
205
206         io->generic.out.oplock_level  = OPLOCK_NONE;
207         io->generic.out.fnum          = f->fnum;
208         io->generic.out.create_action = create_action;
209         io->generic.out.create_time   = name->dos.create_time;
210         io->generic.out.access_time   = name->dos.access_time;
211         io->generic.out.write_time    = name->dos.write_time;
212         io->generic.out.change_time   = name->dos.change_time;
213         io->generic.out.attrib        = name->dos.attrib;
214         io->generic.out.alloc_size    = name->dos.alloc_size;
215         io->generic.out.size          = name->st.st_size;
216         io->generic.out.file_type     = FILE_TYPE_DISK;
217         io->generic.out.ipc_state     = 0;
218         io->generic.out.is_directory  = 1;
219
220         return NT_STATUS_OK;
221 }
222
223 /*
224   destroy a struct pvfs_file_handle
225 */
226 static int pvfs_handle_destructor(void *p)
227 {
228         struct pvfs_file_handle *h = p;
229
230         /* the write time is no longer sticky */
231         if (h->sticky_write_time) {
232                 NTSTATUS status;
233                 status = pvfs_dosattrib_load(h->pvfs, h->name, h->fd);
234                 if (NT_STATUS_IS_OK(status)) {
235                         h->name->dos.flags &= ~XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME;
236                         pvfs_dosattrib_save(h->pvfs, h->name, h->fd);
237                 }
238         }
239         
240         if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
241             h->name->stream_name) {
242                 NTSTATUS status;
243                 status = pvfs_stream_delete(h->pvfs, h->name, h->fd);
244                 if (!NT_STATUS_IS_OK(status)) {
245                         DEBUG(0,("Failed to delete stream '%s' on close of '%s'\n",
246                                  h->name->stream_name, h->name->full_name));
247                 }
248         }
249
250         if (h->fd != -1) {
251                 if (close(h->fd) != 0) {
252                         DEBUG(0,("pvfs_handle_destructor: close(%d) failed for %s - %s\n",
253                                  h->fd, h->name->full_name, strerror(errno)));
254                 }
255                 h->fd = -1;
256         }
257
258         if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
259             h->name->stream_name == NULL) {
260                 if (unlink(h->name->full_name) != 0) {
261                         DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n", 
262                                  h->name->full_name, strerror(errno)));
263                 }
264         }
265
266         if (h->have_opendb_entry) {
267                 struct odb_lock *lck;
268                 NTSTATUS status;
269
270                 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
271                 if (lck == NULL) {
272                         DEBUG(0,("Unable to lock opendb for close\n"));
273                         return 0;
274                 }
275
276                 status = odb_close_file(lck, h);
277                 if (!NT_STATUS_IS_OK(status)) {
278                         DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n", 
279                                  h->name->full_name, nt_errstr(status)));
280                 }
281
282                 talloc_free(lck);
283         }
284
285         return 0;
286 }
287
288
289 /*
290   destroy a struct pvfs_file
291 */
292 static int pvfs_fnum_destructor(void *p)
293 {
294         struct pvfs_file *f = p;
295
296         DLIST_REMOVE(f->pvfs->open_files, f);
297         pvfs_lock_close(f->pvfs, f);
298         idr_remove(f->pvfs->idtree_fnum, f->fnum);
299
300         return 0;
301 }
302
303
304 /*
305   form the lock context used for opendb locking. Note that we must
306   zero here to take account of possible padding on some architectures
307 */
308 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name, 
309                                  TALLOC_CTX *mem_ctx, DATA_BLOB *key)
310 {
311         struct {
312                 dev_t device;
313                 ino_t inode;
314         } lock_context;
315         ZERO_STRUCT(lock_context);
316
317         lock_context.device = name->st.st_dev;
318         lock_context.inode = name->st.st_ino;
319
320         *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
321         if (key->data == NULL) {
322                 return NT_STATUS_NO_MEMORY;
323         }
324         
325         return NT_STATUS_OK;
326 }
327
328 /*
329   form the lock context used for byte range locking. This is separate
330   from the locking key used for opendb locking as it needs to take
331   account of file streams (each stream is a separate byte range
332   locking space)
333 */
334 static NTSTATUS pvfs_brl_locking_key(struct pvfs_filename *name, 
335                                      TALLOC_CTX *mem_ctx, DATA_BLOB *key)
336 {
337         DATA_BLOB odb_key;
338         NTSTATUS status;
339         status = pvfs_locking_key(name, mem_ctx, &odb_key);
340         if (!NT_STATUS_IS_OK(status)) {
341                 return status;
342         }
343         if (name->stream_name == NULL) {
344                 *key = odb_key;
345                 return NT_STATUS_OK;
346         }
347         *key = data_blob_talloc(mem_ctx, NULL, 
348                                 odb_key.length + strlen(name->stream_name) + 1);
349         if (key->data == NULL) {
350                 return NT_STATUS_NO_MEMORY;
351         }
352         memcpy(key->data, odb_key.data, odb_key.length);
353         memcpy(key->data + odb_key.length, 
354                name->stream_name, strlen(name->stream_name)+1);
355         data_blob_free(&odb_key);
356         return NT_STATUS_OK;
357 }
358
359
360 /*
361   create a new file
362 */
363 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, 
364                                  struct smbsrv_request *req, 
365                                  struct pvfs_filename *name, 
366                                  union smb_open *io)
367 {
368         struct pvfs_file *f;
369         NTSTATUS status;
370         int flags, fnum, fd;
371         struct odb_lock *lck;
372         uint32_t create_options = io->generic.in.create_options;
373         uint32_t share_access = io->generic.in.share_access;
374         uint32_t access_mask = io->generic.in.access_mask;
375         mode_t mode;
376         uint32_t attrib;
377
378         if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
379             (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
380                 return NT_STATUS_CANNOT_DELETE;
381         }
382         
383         if (access_mask & SEC_FLAG_MAXIMUM_ALLOWED) {
384                 access_mask = SEC_RIGHTS_FILE_READ | SEC_RIGHTS_FILE_WRITE;
385         }
386
387         if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
388                 flags = O_RDWR;
389         } else {
390                 flags = O_RDONLY;
391         }
392
393         f = talloc_p(req, struct pvfs_file);
394         if (f == NULL) {
395                 return NT_STATUS_NO_MEMORY;
396         }
397
398         f->handle = talloc_p(f, struct pvfs_file_handle);
399         if (f->handle == NULL) {
400                 return NT_STATUS_NO_MEMORY;
401         }
402
403         fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_NEW_FNUM, UINT16_MAX);
404         if (fnum == -1) {
405                 return NT_STATUS_TOO_MANY_OPENED_FILES;
406         }
407
408         attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
409         mode = pvfs_fileperms(pvfs, attrib);
410
411         /* create the file */
412         fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
413         if (fd == -1) {
414                 idr_remove(pvfs->idtree_fnum, fnum);
415                 return pvfs_map_errno(pvfs, errno);
416         }
417
418         /* if this was a stream create then create the stream as well */
419         if (name->stream_name) {
420                 status = pvfs_stream_create(pvfs, name, fd);
421                 if (!NT_STATUS_IS_OK(status)) {
422                         idr_remove(pvfs->idtree_fnum, fnum);
423                         close(fd);
424                         return status;
425                 }
426         }
427
428         /* re-resolve the open fd */
429         status = pvfs_resolve_name_fd(pvfs, fd, name);
430         if (!NT_STATUS_IS_OK(status)) {
431                 idr_remove(pvfs->idtree_fnum, fnum);
432                 close(fd);
433                 return status;
434         }
435
436         name->dos.attrib = attrib;
437         status = pvfs_dosattrib_save(pvfs, name, fd);
438         if (!NT_STATUS_IS_OK(status)) {
439                 idr_remove(pvfs->idtree_fnum, fnum);
440                 close(fd);
441                 return status;
442         }
443
444         /* setup any EAs that were asked for */
445         if (io->ntcreatex.in.ea_list) {
446                 int i;
447                 for (i=0;i<io->ntcreatex.in.ea_list->num_eas;i++) {
448                         status = pvfs_setfileinfo_ea_set(pvfs, name, fd, 
449                                                          &io->ntcreatex.in.ea_list->eas[i]);
450                         if (!NT_STATUS_IS_OK(status)) {
451                                 idr_remove(pvfs->idtree_fnum, fnum);
452                                 close(fd);
453                                 return status;
454                         }
455                 }
456         }
457
458         /* setup an initial sec_desc is required */
459         if (io->ntcreatex.in.sec_desc) {
460                 union smb_setfileinfo set;
461
462                 set.set_secdesc.file.fnum = fnum;
463                 set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
464                 set.set_secdesc.in.sd = io->ntcreatex.in.sec_desc;
465
466                 status = pvfs_acl_set(pvfs, req, name, fd, &set);
467                 if (!NT_STATUS_IS_OK(status)) {
468                         idr_remove(pvfs->idtree_fnum, fnum);
469                         close(fd);
470                         return status;
471                 }
472         }
473
474         /* form the lock context used for byte range locking and
475            opendb locking */
476         status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
477         if (!NT_STATUS_IS_OK(status)) {
478                 idr_remove(pvfs->idtree_fnum, fnum);
479                 close(fd);
480                 return status;
481         }
482
483         status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
484         if (!NT_STATUS_IS_OK(status)) {
485                 idr_remove(pvfs->idtree_fnum, fnum);
486                 close(fd);
487                 return status;
488         }
489
490         /* grab a lock on the open file record */
491         lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
492         if (lck == NULL) {
493                 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
494                          name->full_name));
495                 /* we were supposed to do a blocking lock, so something
496                    is badly wrong! */
497                 idr_remove(pvfs->idtree_fnum, fnum);
498                 close(fd);
499                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
500         }
501
502         status = odb_open_file(lck, f->handle, name->stream_id,
503                                share_access, create_options, access_mask);
504         talloc_free(lck);
505         if (!NT_STATUS_IS_OK(status)) {
506                 /* bad news, we must have hit a race */
507                 idr_remove(pvfs->idtree_fnum, fnum);
508                 close(fd);
509                 return status;
510         }
511
512         f->fnum              = fnum;
513         f->session           = req->session;
514         f->smbpid            = req->smbpid;
515         f->pvfs              = pvfs;
516         f->pending_list      = NULL;
517         f->lock_count        = 0;
518         f->share_access      = io->generic.in.share_access;
519         f->access_mask       = access_mask;
520         f->impersonation     = io->generic.in.impersonation;
521
522         f->handle->pvfs              = pvfs;
523         f->handle->name              = talloc_steal(f->handle, name);
524         f->handle->fd                = fd;
525         f->handle->create_options    = io->generic.in.create_options;
526         f->handle->seek_offset       = 0;
527         f->handle->position          = 0;
528         f->handle->mode              = 0;
529         f->handle->have_opendb_entry = True;
530         f->handle->sticky_write_time = False;
531
532         DLIST_ADD(pvfs->open_files, f);
533
534         /* setup a destructor to avoid file descriptor leaks on
535            abnormal termination */
536         talloc_set_destructor(f, pvfs_fnum_destructor);
537         talloc_set_destructor(f->handle, pvfs_handle_destructor);
538
539         
540         if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
541                 io->generic.out.oplock_level  = OPLOCK_EXCLUSIVE;
542         } else {
543                 io->generic.out.oplock_level  = OPLOCK_NONE;
544         }
545         io->generic.out.fnum          = f->fnum;
546         io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
547         io->generic.out.create_time   = name->dos.create_time;
548         io->generic.out.access_time   = name->dos.access_time;
549         io->generic.out.write_time    = name->dos.write_time;
550         io->generic.out.change_time   = name->dos.change_time;
551         io->generic.out.attrib        = name->dos.attrib;
552         io->generic.out.alloc_size    = name->dos.alloc_size;
553         io->generic.out.size          = name->st.st_size;
554         io->generic.out.file_type     = FILE_TYPE_DISK;
555         io->generic.out.ipc_state     = 0;
556         io->generic.out.is_directory  = 0;
557
558         /* success - keep the file handle */
559         talloc_steal(pvfs, f);
560
561         return NT_STATUS_OK;
562 }
563
564
565 /*
566   state of a pending open retry
567 */
568 struct pvfs_open_retry {
569         struct ntvfs_module_context *ntvfs;
570         struct smbsrv_request *req;
571         union smb_open *io;
572         void *wait_handle;
573         DATA_BLOB odb_locking_key;
574 };
575
576 /* destroy a pending open request */
577 static int pvfs_retry_destructor(void *ptr)
578 {
579         struct pvfs_open_retry *r = ptr;
580         struct pvfs_state *pvfs = r->ntvfs->private_data;
581         if (r->odb_locking_key.data) {
582                 struct odb_lock *lck;
583                 lck = odb_lock(r->req, pvfs->odb_context, &r->odb_locking_key);
584                 if (lck != NULL) {
585                         odb_remove_pending(lck, r);
586                 }
587                 talloc_free(lck);
588         }
589         return 0;
590 }
591
592 /*
593   retry an open
594 */
595 static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
596 {
597         struct pvfs_open_retry *r = private;
598         struct ntvfs_module_context *ntvfs = r->ntvfs;
599         struct smbsrv_request *req = r->req;
600         union smb_open *io = r->io;
601         NTSTATUS status;
602
603         /* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
604            just a bug in their server, but we better do the same */
605         if (reason == PVFS_WAIT_CANCEL) {
606                 return;
607         }
608
609         talloc_free(r->wait_handle);
610
611         if (reason == PVFS_WAIT_TIMEOUT) {
612                 /* if it timed out, then give the failure
613                    immediately */
614                 talloc_free(r);
615                 req->async_states->status = NT_STATUS_SHARING_VIOLATION;
616                 req->async_states->send_fn(req);
617                 return;
618         }
619
620         /* the pending odb entry is already removed. We use a null locking
621            key to indicate this */
622         data_blob_free(&r->odb_locking_key);
623         talloc_free(r);
624
625         /* try the open again, which could trigger another retry setup
626            if it wants to, so we have to unmark the async flag so we
627            will know if it does a second async reply */
628         req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
629
630         status = pvfs_open(ntvfs, req, io);
631         if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
632                 /* the 2nd try also replied async, so we don't send
633                    the reply yet */
634                 return;
635         }
636
637         /* re-mark it async, just in case someone up the chain does
638            paranoid checking */
639         req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
640
641         /* send the reply up the chain */
642         req->async_states->status = status;
643         req->async_states->send_fn(req);
644 }
645
646
647 /*
648   special handling for openx DENY_DOS semantics
649
650   This function attempts a reference open using an existing handle. If its allowed,
651   then it returns NT_STATUS_OK, otherwise it returns any other code and normal
652   open processing continues.
653 */
654 static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
655                                    struct smbsrv_request *req, union smb_open *io,
656                                    struct pvfs_file *f, struct odb_lock *lck)
657 {
658         struct pvfs_state *pvfs = ntvfs->private_data;
659         struct pvfs_file *f2;
660         struct pvfs_filename *name;
661
662         /* search for an existing open with the right parameters. Note
663            the magic ntcreatex options flag, which is set in the
664            generic mapping code. This might look ugly, but its
665            actually pretty much now w2k does it internally as well. 
666            
667            If you look at the BASE-DENYDOS test you will see that a
668            DENY_DOS is a very special case, and in the right
669            circumstances you actually get the _same_ handle back
670            twice, rather than a new handle.
671         */
672         for (f2=pvfs->open_files;f2;f2=f2->next) {
673                 if (f2 != f &&
674                     f2->session == req->session &&
675                     f2->smbpid == req->smbpid &&
676                     (f2->handle->create_options & 
677                      (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
678                       NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) &&
679                     (f2->access_mask & SEC_FILE_WRITE_DATA) &&
680                     StrCaseCmp(f2->handle->name->original_name, 
681                                io->generic.in.fname)==0) {
682                         break;
683                 }
684         }
685
686         if (!f2) {
687                 return NT_STATUS_SHARING_VIOLATION;
688         }
689
690         /* quite an insane set of semantics ... */
691         if (is_exe_filename(io->generic.in.fname) &&
692             (f2->handle->create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) {
693                 return NT_STATUS_SHARING_VIOLATION;
694         }
695
696         /*
697           setup a reference to the existing handle
698          */
699         talloc_free(f->handle);
700         f->handle = talloc_reference(f, f2->handle);
701
702         talloc_free(lck);
703
704         name = f->handle->name;
705
706         io->generic.out.oplock_level  = OPLOCK_NONE;
707         io->generic.out.fnum          = f->fnum;
708         io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
709         io->generic.out.create_time   = name->dos.create_time;
710         io->generic.out.access_time   = name->dos.access_time;
711         io->generic.out.write_time    = name->dos.write_time;
712         io->generic.out.change_time   = name->dos.change_time;
713         io->generic.out.attrib        = name->dos.attrib;
714         io->generic.out.alloc_size    = name->dos.alloc_size;
715         io->generic.out.size          = name->st.st_size;
716         io->generic.out.file_type     = FILE_TYPE_DISK;
717         io->generic.out.ipc_state     = 0;
718         io->generic.out.is_directory  = 0;
719
720         talloc_steal(f->pvfs, f);
721
722         return NT_STATUS_OK;
723 }
724
725
726
727 /*
728   setup for a open retry after a sharing violation
729 */
730 static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
731                                       struct smbsrv_request *req, 
732                                       union smb_open *io,
733                                       struct pvfs_file *f,
734                                       struct odb_lock *lck)
735 {
736         struct pvfs_state *pvfs = ntvfs->private_data;
737         struct pvfs_open_retry *r;
738         NTSTATUS status;
739         struct timeval end_time;
740
741         if (io->generic.in.create_options & 
742             (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
743                 /* see if we can satisfy the request using the special DENY_DOS
744                    code */
745                 status = pvfs_open_deny_dos(ntvfs, req, io, f, lck);
746                 if (NT_STATUS_IS_OK(status)) {
747                         return status;
748                 }
749         }
750
751         r = talloc_p(req, struct pvfs_open_retry);
752         if (r == NULL) {
753                 return NT_STATUS_NO_MEMORY;
754         }
755
756         r->ntvfs = ntvfs;
757         r->req = req;
758         r->io = io;
759         r->odb_locking_key = data_blob_talloc(r, 
760                                               f->handle->odb_locking_key.data, 
761                                               f->handle->odb_locking_key.length);
762
763         end_time = timeval_add(&req->request_time, 0, pvfs->sharing_violation_delay);
764
765         /* setup a pending lock */
766         status = odb_open_file_pending(lck, r);
767         if (!NT_STATUS_IS_OK(status)) {
768                 return status;
769         }
770
771         talloc_free(lck);
772         talloc_free(f);
773
774         talloc_set_destructor(r, pvfs_retry_destructor);
775
776         r->wait_handle = pvfs_wait_message(pvfs, req, MSG_PVFS_RETRY_OPEN, end_time, 
777                                            pvfs_open_retry, r);
778         if (r->wait_handle == NULL) {
779                 return NT_STATUS_NO_MEMORY;
780         }
781
782         talloc_steal(pvfs, r);
783
784         return NT_STATUS_OK;
785 }
786
787 /*
788   special handling for t2open
789 */
790 static NTSTATUS pvfs_open_t2open(struct ntvfs_module_context *ntvfs,
791                                  struct smbsrv_request *req, union smb_open *io)
792 {
793         struct pvfs_state *pvfs = ntvfs->private_data;
794         struct pvfs_filename *name;
795         NTSTATUS status;
796
797         status = pvfs_resolve_name(pvfs, req, io->t2open.in.fname, 0, &name);
798         if (!NT_STATUS_IS_OK(status)) {
799                 return status;
800         }
801
802         if (io->t2open.in.open_func & OPENX_OPEN_FUNC_CREATE) {
803                 if (!name->stream_exists) return NT_STATUS_ACCESS_DENIED;
804         }
805         if (io->t2open.in.open_func & OPENX_OPEN_FUNC_TRUNC) {
806                 if (name->stream_exists) return NT_STATUS_ACCESS_DENIED;
807                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
808         }
809         if ((io->t2open.in.open_func & 0xF) == OPENX_OPEN_FUNC_FAIL) {
810                 if (!name->stream_exists) return NT_STATUS_ACCESS_DENIED;
811                 return NT_STATUS_OBJECT_NAME_COLLISION;
812         }
813
814         talloc_free(name);
815
816         return ntvfs_map_open(req, io, ntvfs);
817 }
818
819 /*
820   open a file
821 */
822 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
823                    struct smbsrv_request *req, union smb_open *io)
824 {
825         struct pvfs_state *pvfs = ntvfs->private_data;
826         int flags;
827         struct pvfs_filename *name;
828         struct pvfs_file *f;
829         NTSTATUS status;
830         int fnum, fd;
831         struct odb_lock *lck;
832         uint32_t create_options;
833         uint32_t share_access;
834         uint32_t access_mask;
835         BOOL stream_existed;
836
837         if (io->generic.level == RAW_OPEN_T2OPEN) {
838                 return pvfs_open_t2open(ntvfs, req, io);
839         }
840
841         /* use the generic mapping code to avoid implementing all the
842            different open calls. */
843         if (io->generic.level != RAW_OPEN_GENERIC &&
844             io->generic.level != RAW_OPEN_NTTRANS_CREATE) {
845                 return ntvfs_map_open(req, io, ntvfs);
846         }
847
848         /* resolve the cifs name to a posix name */
849         status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 
850                                    PVFS_RESOLVE_STREAMS, &name);
851         if (!NT_STATUS_IS_OK(status)) {
852                 return status;
853         }
854
855         /* directory opens are handled separately */
856         if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
857             (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
858                 return pvfs_open_directory(pvfs, req, name, io);
859         }
860
861         create_options = io->generic.in.create_options;
862         share_access   = io->generic.in.share_access;
863         access_mask    = io->generic.in.access_mask;
864
865         if (access_mask & SEC_FLAG_MAXIMUM_ALLOWED) {
866                 if (name->exists && (name->dos.attrib & FILE_ATTRIBUTE_READONLY)) {
867                         access_mask = SEC_RIGHTS_FILE_READ;
868                 } else {
869                         access_mask = SEC_RIGHTS_FILE_READ | SEC_RIGHTS_FILE_WRITE;
870                 }
871         }
872
873         /* certain create options are not allowed */
874         if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
875             !(access_mask & SEC_STD_DELETE)) {
876                 return NT_STATUS_INVALID_PARAMETER;
877         }
878
879         switch (io->generic.in.open_disposition) {
880         case NTCREATEX_DISP_SUPERSEDE:
881                 flags = O_TRUNC;
882                 break;
883
884         case NTCREATEX_DISP_OVERWRITE_IF:
885                 flags = O_TRUNC;
886                 break;
887
888         case NTCREATEX_DISP_OPEN:
889                 if (!name->stream_exists) {
890                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
891                 }
892                 flags = 0;
893                 break;
894
895         case NTCREATEX_DISP_OVERWRITE:
896                 if (!name->stream_exists) {
897                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
898                 }
899                 flags = O_TRUNC;
900                 break;
901
902         case NTCREATEX_DISP_CREATE:
903                 if (name->stream_exists) {
904                         return NT_STATUS_OBJECT_NAME_COLLISION;
905                 }
906                 flags = 0;
907                 break;
908
909         case NTCREATEX_DISP_OPEN_IF:
910                 flags = 0;
911                 break;
912
913         default:
914                 return NT_STATUS_INVALID_PARAMETER;
915         }
916
917         if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
918                 flags |= O_RDWR;
919         } else {
920                 flags |= O_RDONLY;
921         }
922
923         if (io->generic.in.file_attr & FILE_ATTRIBUTE_DIRECTORY) {
924                 return NT_STATUS_INVALID_PARAMETER;
925         }
926
927         /* handle creating a new file separately */
928         if (!name->exists) {
929                 status = pvfs_create_file(pvfs, req, name, io);
930                 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
931                         return status;
932                 }
933
934                 /* we've hit a race - the file was created during this call */
935                 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
936                         return status;
937                 }
938
939                 /* try re-resolving the name */
940                 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
941                 if (!NT_STATUS_IS_OK(status)) {
942                         return status;
943                 }
944                 /* fall through to a normal open */
945         }
946
947         if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
948             (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
949                 return NT_STATUS_CANNOT_DELETE;
950         }
951
952         f = talloc_p(req, struct pvfs_file);
953         if (f == NULL) {
954                 return NT_STATUS_NO_MEMORY;
955         }
956
957         f->handle = talloc_p(f, struct pvfs_file_handle);
958         if (f->handle == NULL) {
959                 return NT_STATUS_NO_MEMORY;
960         }
961
962         /* allocate a fnum */
963         fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
964         if (fnum == -1) {
965                 return NT_STATUS_TOO_MANY_OPENED_FILES;
966         }
967
968         f->fnum          = fnum;
969         f->session       = req->session;
970         f->smbpid        = req->smbpid;
971         f->pvfs          = pvfs;
972         f->pending_list  = NULL;
973         f->lock_count    = 0;
974         f->share_access  = io->generic.in.share_access;
975         f->access_mask   = access_mask;
976         f->impersonation = io->generic.in.impersonation;
977
978         f->handle->pvfs              = pvfs;
979         f->handle->fd                = -1;
980         f->handle->name              = talloc_steal(f->handle, name);
981         f->handle->create_options    = io->generic.in.create_options;
982         f->handle->seek_offset       = 0;
983         f->handle->position          = 0;
984         f->handle->mode              = 0;
985         f->handle->have_opendb_entry = False;
986         f->handle->sticky_write_time = False;
987
988         /* form the lock context used for byte range locking and
989            opendb locking */
990         status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
991         if (!NT_STATUS_IS_OK(status)) {
992                 idr_remove(pvfs->idtree_fnum, f->fnum);
993                 return status;
994         }
995
996         status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
997         if (!NT_STATUS_IS_OK(status)) {
998                 idr_remove(pvfs->idtree_fnum, f->fnum);
999                 return status;
1000         }
1001
1002         /* get a lock on this file before the actual open */
1003         lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1004         if (lck == NULL) {
1005                 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
1006                          name->full_name));
1007                 /* we were supposed to do a blocking lock, so something
1008                    is badly wrong! */
1009                 idr_remove(pvfs->idtree_fnum, fnum);
1010                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1011         }
1012
1013         DLIST_ADD(pvfs->open_files, f);
1014
1015         /* setup a destructor to avoid file descriptor leaks on
1016            abnormal termination */
1017         talloc_set_destructor(f, pvfs_fnum_destructor);
1018         talloc_set_destructor(f->handle, pvfs_handle_destructor);
1019
1020
1021         /* see if we are allowed to open at the same time as existing opens */
1022         status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
1023                                share_access, create_options, access_mask);
1024
1025         /* on a sharing violation we need to retry when the file is closed by 
1026            the other user, or after 1 second */
1027         if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) &&
1028             (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
1029                 return pvfs_open_setup_retry(ntvfs, req, io, f, lck);
1030         }
1031
1032         if (!NT_STATUS_IS_OK(status)) {
1033                 talloc_free(lck);
1034                 return status;
1035         }
1036
1037         f->handle->have_opendb_entry = True;
1038
1039         /* do the actual open */
1040         fd = open(f->handle->name->full_name, flags);
1041         if (fd == -1) {
1042                 talloc_free(lck);
1043                 return pvfs_map_errno(f->pvfs, errno);
1044         }
1045
1046         f->handle->fd = fd;
1047
1048         stream_existed = name->stream_exists;
1049
1050         /* if this was a stream create then create the stream as well */
1051         if (!name->stream_exists) {
1052                 status = pvfs_stream_create(pvfs, f->handle->name, fd);
1053                 if (!NT_STATUS_IS_OK(status)) {
1054                         talloc_free(lck);
1055                         return status;
1056                 }
1057         }
1058
1059         /* re-resolve the open fd */
1060         status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name);
1061         if (!NT_STATUS_IS_OK(status)) {
1062                 talloc_free(lck);
1063                 return status;
1064         }
1065
1066         if (f->handle->name->stream_id == 0 &&
1067             (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE ||
1068              io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) {
1069                 /* for overwrite we need to replace file permissions */
1070                 uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
1071                 mode_t mode = pvfs_fileperms(pvfs, attrib);
1072                 if (fchmod(fd, mode) == -1) {
1073                         talloc_free(lck);
1074                         return pvfs_map_errno(pvfs, errno);
1075                 }
1076                 name->dos.attrib = attrib;
1077                 status = pvfs_dosattrib_save(pvfs, name, fd);
1078                 if (!NT_STATUS_IS_OK(status)) {
1079                         talloc_free(lck);
1080                         return status;
1081                 }
1082         }
1083             
1084         talloc_free(lck);
1085
1086         if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
1087                 io->generic.out.oplock_level  = OPLOCK_EXCLUSIVE;
1088         } else {
1089                 io->generic.out.oplock_level  = OPLOCK_NONE;
1090         }
1091         io->generic.out.fnum          = f->fnum;
1092         io->generic.out.create_action = stream_existed?
1093                 NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED;
1094         io->generic.out.create_time   = name->dos.create_time;
1095         io->generic.out.access_time   = name->dos.access_time;
1096         io->generic.out.write_time    = name->dos.write_time;
1097         io->generic.out.change_time   = name->dos.change_time;
1098         io->generic.out.attrib        = name->dos.attrib;
1099         io->generic.out.alloc_size    = name->dos.alloc_size;
1100         io->generic.out.size          = name->st.st_size;
1101         io->generic.out.file_type     = FILE_TYPE_DISK;
1102         io->generic.out.ipc_state     = 0;
1103         io->generic.out.is_directory  = 0;
1104
1105         /* success - keep the file handle */
1106         talloc_steal(f->pvfs, f);
1107
1108         return NT_STATUS_OK;
1109 }
1110
1111
1112 /*
1113   close a file
1114 */
1115 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
1116                     struct smbsrv_request *req, union smb_close *io)
1117 {
1118         struct pvfs_state *pvfs = ntvfs->private_data;
1119         struct pvfs_file *f;
1120         struct utimbuf unix_times;
1121
1122         if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
1123                 return NT_STATUS_UNSUCCESSFUL;
1124         }
1125
1126         if (io->generic.level != RAW_CLOSE_CLOSE) {
1127                 return ntvfs_map_close(req, io, ntvfs);
1128         }
1129
1130         f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
1131         if (!f) {
1132                 return NT_STATUS_INVALID_HANDLE;
1133         }
1134
1135         if (!null_time(io->close.in.write_time)) {
1136                 unix_times.actime = 0;
1137                 unix_times.modtime = io->close.in.write_time;
1138                 utime(f->handle->name->full_name, &unix_times);
1139         } else if (f->handle->sticky_write_time) {
1140                 unix_times.actime = 0;
1141                 unix_times.modtime = nt_time_to_unix(f->handle->name->dos.write_time);
1142                 utime(f->handle->name->full_name, &unix_times);
1143         }
1144
1145         talloc_free(f);
1146
1147         return NT_STATUS_OK;
1148 }
1149
1150
1151 /*
1152   logoff - close all file descriptors open by a vuid
1153 */
1154 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
1155                      struct smbsrv_request *req)
1156 {
1157         struct pvfs_state *pvfs = ntvfs->private_data;
1158         struct pvfs_file *f, *next;
1159
1160         for (f=pvfs->open_files;f;f=next) {
1161                 next = f->next;
1162                 if (f->session == req->session) {
1163                         talloc_free(f);
1164                 }
1165         }
1166
1167         return NT_STATUS_OK;
1168 }
1169
1170
1171 /*
1172   exit - close files for the current pid
1173 */
1174 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
1175                    struct smbsrv_request *req)
1176 {
1177         struct pvfs_state *pvfs = ntvfs->private_data;
1178         struct pvfs_file *f, *next;
1179
1180         for (f=pvfs->open_files;f;f=next) {
1181                 next = f->next;
1182                 if (f->smbpid == req->smbpid) {
1183                         talloc_free(f);
1184                 }
1185         }
1186
1187         return NT_STATUS_OK;
1188 }
1189
1190
1191 /*
1192   change the create options on an already open file
1193 */
1194 NTSTATUS pvfs_change_create_options(struct pvfs_state *pvfs,
1195                                     struct smbsrv_request *req, 
1196                                     struct pvfs_file *f, uint32_t create_options)
1197 {
1198         struct odb_lock *lck;
1199         NTSTATUS status;
1200
1201         if (f->handle->create_options == create_options) {
1202                 return NT_STATUS_OK;
1203         }
1204
1205         if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
1206             (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
1207                 return NT_STATUS_CANNOT_DELETE;
1208         }
1209
1210         lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1211         if (lck == NULL) {
1212                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1213         }
1214
1215         status = odb_set_create_options(lck, f->handle, create_options);
1216         if (NT_STATUS_IS_OK(status)) {
1217                 f->handle->create_options = create_options;
1218         }
1219
1220         return status;
1221 }
1222
1223
1224 /*
1225   determine if a file can be deleted, or if it is prevented by an
1226   already open file
1227 */
1228 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs, struct pvfs_filename *name)
1229 {
1230         NTSTATUS status;
1231         DATA_BLOB key;
1232
1233         status = pvfs_locking_key(name, name, &key);
1234         if (!NT_STATUS_IS_OK(status)) {
1235                 return NT_STATUS_NO_MEMORY;
1236         }
1237
1238         status = odb_can_open(pvfs->odb_context, &key, 
1239                               NTCREATEX_SHARE_ACCESS_READ |
1240                               NTCREATEX_SHARE_ACCESS_WRITE | 
1241                               NTCREATEX_SHARE_ACCESS_DELETE, 
1242                               NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 
1243                               SEC_STD_DELETE);
1244
1245         return status;
1246 }
1247
1248 /*
1249   determine if a file can be renamed, or if it is prevented by an
1250   already open file
1251 */
1252 NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs, struct pvfs_filename *name)
1253 {
1254         NTSTATUS status;
1255         DATA_BLOB key;
1256
1257         status = pvfs_locking_key(name, name, &key);
1258         if (!NT_STATUS_IS_OK(status)) {
1259                 return NT_STATUS_NO_MEMORY;
1260         }
1261
1262         status = odb_can_open(pvfs->odb_context, &key, 
1263                               NTCREATEX_SHARE_ACCESS_READ |
1264                               NTCREATEX_SHARE_ACCESS_WRITE,
1265                               0,
1266                               SEC_STD_DELETE);
1267
1268         return status;
1269 }