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