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