fb81c86bcc38fdcb7de0b8d170251f3c07e4ee85
[kai/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   by using a destructor we make sure that abnormal cleanup will not 
51   leak file descriptors (assuming at least the top level pointer is freed, which
52   will cascade down to here)
53 */
54 static int pvfs_fd_destructor(void *p)
55 {
56         struct pvfs_file *f = p;
57
58         pvfs_lock_close(f->pvfs, f);
59
60         if (f->fd != -1) {
61                 close(f->fd);
62                 f->fd = -1;
63         }
64
65         idr_remove(f->pvfs->idtree_fnum, f->fnum);
66
67         return 0;
68 }
69
70 /*
71   open a file
72   TODO: this is a temporary implementation derived from the simple backend
73   its purpose is to allow other tests to run 
74 */
75 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
76                    struct smbsrv_request *req, union smb_open *io)
77 {
78         struct pvfs_state *pvfs = ntvfs->private_data;
79         int fd, flags;
80         struct pvfs_filename *name;
81         struct pvfs_file *f;
82         NTSTATUS status;
83         struct {
84                 dev_t device;
85                 ino_t inode;
86         } lock_context;
87         int fnum;
88
89         if (io->generic.level != RAW_OPEN_GENERIC) {
90                 return ntvfs_map_open(req, io, ntvfs);
91         }
92
93         /* resolve the cifs name to a posix name */
94         status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
95                                    PVFS_RESOLVE_NO_WILDCARD, &name);
96         if (!NT_STATUS_IS_OK(status)) {
97                 return status;
98         }
99
100         switch (io->generic.in.open_disposition) {
101         case NTCREATEX_DISP_SUPERSEDE:
102         case NTCREATEX_DISP_OVERWRITE_IF:
103                 flags = O_CREAT | O_TRUNC;
104                 break;
105         case NTCREATEX_DISP_OPEN:
106                 flags = 0;
107                 break;
108         case NTCREATEX_DISP_OVERWRITE:
109                 flags = O_TRUNC;
110                 break;
111         case NTCREATEX_DISP_CREATE:
112                 flags = O_CREAT | O_EXCL;
113                 break;
114         case NTCREATEX_DISP_OPEN_IF:
115                 flags = O_CREAT;
116                 break;
117         default:
118                 flags = 0;
119                 break;
120         }
121         
122         flags |= O_RDWR;
123
124 /* we need to do this differently to support systems without O_DIRECTORY */
125 #ifndef O_DIRECTORY
126 #define O_DIRECTORY 0
127 #endif
128
129         if (name->exists &&
130             (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY) &&
131             !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
132                 return NT_STATUS_NOT_A_DIRECTORY;
133         }
134
135         if ((name->exists && name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) ||
136             (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
137                 flags = O_RDONLY | O_DIRECTORY;
138                 if (pvfs->flags & PVFS_FLAG_READONLY) {
139                         goto do_open;
140                 }
141                 switch (io->generic.in.open_disposition) {
142                 case NTCREATEX_DISP_CREATE:
143                         if (mkdir(name->full_name, 0755) == -1) {
144                                 return pvfs_map_errno(pvfs,errno);
145                         }
146                         break;
147                 case NTCREATEX_DISP_OPEN_IF:
148                         if (mkdir(name->full_name, 0755) == -1 && errno != EEXIST) {
149                                 return pvfs_map_errno(pvfs,errno);
150                         }
151                         break;
152                 }
153         }
154
155         f = talloc_p(pvfs, struct pvfs_file);
156         if (f == NULL) {
157                 return NT_STATUS_NO_MEMORY;
158         }
159
160         fnum = idr_get_new(pvfs->idtree_fnum, f, UINT16_MAX);
161         if (fnum == -1) {
162                 talloc_free(f);
163                 return NT_STATUS_TOO_MANY_OPENED_FILES;
164         }
165
166 do_open:
167         fd = open(name->full_name, flags, 0644);
168         if (fd == -1) {
169                 if (errno == 0)
170                         errno = ENOENT;
171                 return pvfs_map_errno(pvfs,errno);
172         }
173
174         /* re-resolve the open fd */
175         status = pvfs_resolve_name_fd(pvfs, fd, name);
176         if (!NT_STATUS_IS_OK(status)) {
177                 return status;
178         }
179
180         f->fnum = fnum;
181         f->fd = fd;
182         f->name = talloc_steal(f, name);
183         f->session = req->session;
184         f->smbpid = req->smbpid;
185         f->pvfs = pvfs;
186         f->pending_list = NULL;
187         f->lock_count = 0;
188
189         /* we must zero here to take account of padding */
190         ZERO_STRUCT(lock_context);
191         lock_context.device = name->st.st_dev;
192         lock_context.inode = name->st.st_ino;
193         f->locking_key = data_blob_talloc(f, &lock_context, sizeof(lock_context));
194
195         /* setup a destructor to avoid file descriptor leaks on
196            abnormal termination */
197         talloc_set_destructor(f, pvfs_fd_destructor);
198
199         DLIST_ADD(pvfs->open_files, f);
200
201         ZERO_STRUCT(io->generic.out);
202         
203         io->generic.out.create_time = name->dos.create_time;
204         io->generic.out.access_time = name->dos.access_time;
205         io->generic.out.write_time = name->dos.write_time;
206         io->generic.out.change_time = name->dos.change_time;
207         io->generic.out.fnum = f->fnum;
208         io->generic.out.alloc_size = name->dos.alloc_size;
209         io->generic.out.size = name->st.st_size;
210         io->generic.out.attrib = name->dos.attrib;
211         io->generic.out.is_directory = (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)?1:0;
212
213         return NT_STATUS_OK;
214 }
215
216
217 /*
218   close a file
219 */
220 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
221                     struct smbsrv_request *req, union smb_close *io)
222 {
223         struct pvfs_state *pvfs = ntvfs->private_data;
224         struct pvfs_file *f;
225         NTSTATUS status;
226
227         if (io->generic.level != RAW_CLOSE_CLOSE) {
228                 /* we need a mapping function */
229                 return NT_STATUS_INVALID_LEVEL;
230         }
231
232         f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
233         if (!f) {
234                 return NT_STATUS_INVALID_HANDLE;
235         }
236
237         if (close(f->fd) != 0) {
238                 status = pvfs_map_errno(pvfs, errno);
239         } else {
240                 status = NT_STATUS_OK;
241         }
242         f->fd = -1;
243
244         DLIST_REMOVE(pvfs->open_files, f);
245
246         /* the destructor takes care of the rest */
247         talloc_free(f);
248
249         return status;
250 }
251
252
253 /*
254   logoff - close all file descriptors open by a vuid
255 */
256 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
257                      struct smbsrv_request *req)
258 {
259         struct pvfs_state *pvfs = ntvfs->private_data;
260         struct pvfs_file *f, *next;
261
262         for (f=pvfs->open_files;f;f=next) {
263                 next = f->next;
264                 if (f->session == req->session) {
265                         DLIST_REMOVE(pvfs->open_files, f);
266                         talloc_free(f);
267                 }
268         }
269
270         return NT_STATUS_OK;
271 }
272
273
274 /*
275   exit - close files for the current pid
276 */
277 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
278                    struct smbsrv_request *req)
279 {
280         struct pvfs_state *pvfs = ntvfs->private_data;
281         struct pvfs_file *f, *next;
282
283         for (f=pvfs->open_files;f;f=next) {
284                 next = f->next;
285                 if (f->smbpid == req->smbpid) {
286                         DLIST_REMOVE(pvfs->open_files, f);
287                         talloc_free(f);
288                 }
289         }
290
291         return NT_STATUS_OK;
292 }