Comments to use vfs_* functions instead of dos_* unless really
[samba.git] / source / smbd / vfs.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    VFS initialisation and support functions
5    Copyright (C) Tim Potter 1999
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24 extern int DEBUGLEVEL;
25
26 /* Some structures to help us initialise the vfs operations table */
27
28 struct vfs_syminfo {
29     char *name;
30     void *fptr;
31 };
32
33 /* Default vfs hooks.  WARNING: The order of these initialisers is
34    very important.  They must be in the same order as defined in
35    vfs.h.  Change at your own peril. */
36
37 struct vfs_ops default_vfs_ops = {
38
39     /* Disk operations */        
40
41     vfswrap_dummy_connect,
42     vfswrap_dummy_disconnect,
43     vfswrap_disk_free,
44
45     /* Directory operations */
46
47     vfswrap_opendir,
48     vfswrap_readdir,
49     vfswrap_mkdir,
50     vfswrap_rmdir,
51     vfswrap_closedir,
52
53     /* File operations */
54
55     vfswrap_open,
56     vfswrap_close,
57     vfswrap_read,
58     vfswrap_write,
59     vfswrap_lseek,
60     vfswrap_rename,
61     vfswrap_fsync,
62     vfswrap_stat,
63     vfswrap_fstat,
64     vfswrap_lstat,
65     vfswrap_unlink,
66     vfswrap_chmod,
67     vfswrap_utime
68 };
69
70 /****************************************************************************
71   initialise default vfs hooks
72 ****************************************************************************/
73 int vfs_init_default(connection_struct *conn)
74 {
75     DEBUG(3, ("Initialising default vfs hooks\n"));
76
77     memcpy(&conn->vfs_ops, &default_vfs_ops, sizeof(conn->vfs_ops));
78     return True;
79 }
80
81 /****************************************************************************
82   initialise custom vfs hooks
83 ****************************************************************************/
84 #ifdef HAVE_LIBDL
85 BOOL vfs_init_custom(connection_struct *conn)
86 {
87     void *handle;
88     struct vfs_ops *ops, *(*fptr)(struct vfs_options *options);
89
90     DEBUG(3, ("Initialising custom vfs hooks from %s\n",
91               lp_vfsobj(SNUM(conn))));
92
93     /* Open object file */
94
95     handle = dlopen(lp_vfsobj(SNUM(conn)), RTLD_NOW | RTLD_GLOBAL);
96     conn->vfs_conn->dl_handle = handle;
97
98     if (!handle) {
99         DEBUG(0, ("Error opening %s: %s\n", lp_vfsobj(SNUM(conn)),
100                   dlerror()));
101         return False;
102     }
103
104     /* Get handle on vfs_init() symbol */
105
106     fptr = dlsym(handle, "vfs_init");
107
108     if (fptr == NULL) {
109         DEBUG(0, ("No vfs_init() symbol found in %s\n", 
110                   lp_vfsobj(SNUM(conn))));
111         return False;
112     }
113
114     /* Initialise vfs_ops structure */
115
116     if ((ops = fptr(NULL)) == NULL) {
117         DEBUG(0, ("vfs_init function from %s failed\n", lp_vfsobj(SNUM(conn))));
118         return False;
119     }
120
121     /* Fill in unused operations with default (disk based) ones.
122        There's probably a neater way to do this then a whole bunch of
123        if statements. */ 
124
125     memcpy(&conn->vfs_ops, ops, sizeof(conn->vfs_ops));
126     
127     if (conn->vfs_ops.connect == NULL) {
128         conn->vfs_ops.connect = default_vfs_ops.connect;
129     }
130
131     if (conn->vfs_ops.disconnect == NULL) {
132         conn->vfs_ops.disconnect = default_vfs_ops.disconnect;
133     }
134
135     if (conn->vfs_ops.disk_free == NULL) {
136         conn->vfs_ops.disk_free = default_vfs_ops.disk_free;
137     }
138
139     if (conn->vfs_ops.opendir == NULL) {
140         conn->vfs_ops.opendir = default_vfs_ops.opendir;
141     }
142
143     if (conn->vfs_ops.readdir == NULL) {
144         conn->vfs_ops.readdir = default_vfs_ops.readdir;
145     }
146
147     if (conn->vfs_ops.mkdir == NULL) {
148         conn->vfs_ops.mkdir = default_vfs_ops.mkdir;
149     }
150
151     if (conn->vfs_ops.rmdir == NULL) {
152         conn->vfs_ops.rmdir = default_vfs_ops.rmdir;
153     }
154
155     if (conn->vfs_ops.closedir == NULL) {
156         conn->vfs_ops.closedir = default_vfs_ops.closedir;
157     }
158
159     if (conn->vfs_ops.open == NULL) {
160         conn->vfs_ops.open = default_vfs_ops.open;
161     }
162
163     if (conn->vfs_ops.close == NULL) {
164         conn->vfs_ops.close = default_vfs_ops.close;
165     }
166
167     if (conn->vfs_ops.read == NULL) {
168         conn->vfs_ops.read = default_vfs_ops.read;
169     }
170     
171     if (conn->vfs_ops.write == NULL) {
172         conn->vfs_ops.write = default_vfs_ops.write;
173     }
174     
175     if (conn->vfs_ops.lseek == NULL) {
176         conn->vfs_ops.lseek = default_vfs_ops.lseek;
177     }
178     
179     if (conn->vfs_ops.rename == NULL) {
180         conn->vfs_ops.rename = default_vfs_ops.rename;
181     }
182     
183     if (conn->vfs_ops.fsync == NULL) {
184         conn->vfs_ops.fsync = default_vfs_ops.fsync;
185     }
186     
187     if (conn->vfs_ops.stat == NULL) {
188         conn->vfs_ops.stat = default_vfs_ops.stat;
189     }
190     
191     if (conn->vfs_ops.fstat == NULL) {
192         conn->vfs_ops.fstat = default_vfs_ops.fstat;
193     }
194     
195     if (conn->vfs_ops.lstat == NULL) {
196         conn->vfs_ops.lstat = default_vfs_ops.lstat;
197     }
198     
199     if (conn->vfs_ops.unlink == NULL) {
200         conn->vfs_ops.unlink = default_vfs_ops.unlink;
201     }
202     
203     if (conn->vfs_ops.chmod == NULL) {
204         conn->vfs_ops.chmod = default_vfs_ops.chmod;
205     }
206     
207     if (conn->vfs_ops.utime == NULL) {
208         conn->vfs_ops.utime = default_vfs_ops.utime;
209     }
210     
211     return True;
212 }
213 #endif
214
215 BOOL vfs_directory_exist(connection_struct *conn, char *dname,
216                          SMB_STRUCT_STAT *st)
217 {
218   SMB_STRUCT_STAT st2;
219   BOOL ret;
220
221   if (!st) st = &st2;
222
223   if (conn->vfs_ops.stat(dname,st) != 0) 
224     return(False);
225
226   ret = S_ISDIR(st->st_mode);
227   if(!ret)
228     errno = ENOTDIR;
229
230   return ret;
231 }
232
233 /*******************************************************************
234   check if a vfs file exists
235 ********************************************************************/
236 BOOL vfs_file_exist(connection_struct *conn,char *fname,SMB_STRUCT_STAT *sbuf)
237 {
238   SMB_STRUCT_STAT st;
239   if (!sbuf) sbuf = &st;
240   
241   if (conn->vfs_ops.stat(fname,sbuf) != 0) 
242     return(False);
243
244   return(S_ISREG(sbuf->st_mode));
245 }
246
247 /****************************************************************************
248   write data to a fd on the vfs
249 ****************************************************************************/
250 ssize_t vfs_write_data(files_struct *fsp,char *buffer,size_t N)
251 {
252   size_t total=0;
253   ssize_t ret;
254   int fd = fsp->fd_ptr->fd;
255
256   while (total < N)
257   {
258     ret = fsp->conn->vfs_ops.write(fd,buffer + total,N - total);
259
260     if (ret == -1) return -1;
261     if (ret == 0) return total;
262
263     total += ret;
264   }
265   return (ssize_t)total;
266 }
267
268 /****************************************************************************
269 transfer some data between two file_struct's
270 ****************************************************************************/
271 SMB_OFF_T vfs_transfer_file(int in_fd, files_struct *in_fsp, 
272                             int out_fd, files_struct *out_fsp,
273                             SMB_OFF_T n, char *header, int headlen, int align)
274 {
275   static char *buf=NULL;  
276   static int size=0;
277   char *buf1,*abuf;
278   SMB_OFF_T total = 0;
279
280   DEBUG(4,("vfs_transfer_file n=%.0f  (head=%d) called\n",(double)n,headlen));
281
282   /* Check we have at least somewhere to read from */
283
284   SMB_ASSERT((in_fd != -1) || (in_fsp != NULL));
285
286   if (size == 0) {
287     size = lp_readsize();
288     size = MAX(size,1024);
289   }
290
291   while (!buf && size>0) {
292     buf = (char *)Realloc(buf,size+8);
293     if (!buf) size /= 2;
294   }
295
296   if (!buf) {
297     DEBUG(0,("Can't allocate transfer buffer!\n"));
298     exit(1);
299   }
300
301   abuf = buf + (align%8);
302
303   if (header)
304     n += headlen;
305
306   while (n > 0)
307   {
308     int s = (int)MIN(n,(SMB_OFF_T)size);
309     int ret,ret2=0;
310
311     ret = 0;
312
313     if (header && (headlen >= MIN(s,1024))) {
314       buf1 = header;
315       s = headlen;
316       ret = headlen;
317       headlen = 0;
318       header = NULL;
319     } else {
320       buf1 = abuf;
321     }
322
323     if (header && headlen > 0)
324     {
325       ret = MIN(headlen,size);
326       memcpy(buf1,header,ret);
327       headlen -= ret;
328       header += ret;
329       if (headlen <= 0) header = NULL;
330     }
331
332     if (s > ret) {
333       ret += in_fsp ? 
334           in_fsp->conn->vfs_ops.read(in_fsp->fd_ptr->fd,buf1+ret,s-ret) : read(in_fd,buf1+ret,s-ret);
335     }
336
337     if (ret > 0)
338     {
339         if (out_fsp) {
340             ret2 = out_fsp->conn->vfs_ops.write(out_fsp->fd_ptr->fd,buf1,ret);
341         } else {
342             ret2= (out_fd != -1) ? write_data(out_fd,buf1,ret) : ret;
343         }
344     }
345
346       if (ret2 > 0) total += ret2;
347       /* if we can't write then dump excess data */
348       if (ret2 != ret)
349         vfs_transfer_file(in_fd, in_fsp, -1,NULL,n-(ret+headlen),NULL,0,0);
350
351     if (ret <= 0 || ret2 != ret)
352       return(total);
353     n -= ret;
354   }
355   return(total);
356 }
357
358 /*******************************************************************
359 a vfs_readdir wrapper which just returns the file name
360 ********************************************************************/
361 char *vfs_readdirname(connection_struct *conn, void *p)
362 {
363         struct dirent *ptr;
364         char *dname;
365
366         if (!p) return(NULL);
367   
368         ptr = (struct dirent *)conn->vfs_ops.readdir(p);
369         if (!ptr) return(NULL);
370
371         dname = ptr->d_name;
372
373 #ifdef NEXT2
374         if (telldir(p) < 0) return(NULL);
375 #endif
376
377 #ifdef HAVE_BROKEN_READDIR
378         /* using /usr/ucb/cc is BAD */
379         dname = dname - 2;
380 #endif
381
382         {
383                 static pstring buf;
384                 memcpy(buf, dname, NAMLEN(ptr)+1);
385                 unix_to_dos(buf, True);
386                 dname = buf;
387         }
388
389         unix_to_dos(dname, True);
390         return(dname);
391 }
392
393 /***************************************************************************
394   handle the interpretation of the vfs option parameter
395  *************************************************************************/
396 static BOOL handle_vfs_option(char *pszParmValue, char **ptr)
397 {
398     struct vfs_options *new_option, **options = (struct vfs_options **)ptr;
399     int i;
400     
401     /* Create new vfs option */
402
403     new_option = (struct vfs_options *)malloc(sizeof(*new_option));
404     if (new_option == NULL) {
405         return False;
406     }
407
408     ZERO_STRUCTP(new_option);
409
410     /* Get name and value */
411     
412     new_option->name = strtok(pszParmValue, "=");
413
414     if (new_option->name == NULL) {
415         return False;
416     }
417
418     while(isspace(*new_option->name)) {
419         new_option->name++;
420     }
421
422     for (i = strlen(new_option->name); i > 0; i--) {
423         if (!isspace(new_option->name[i - 1])) break;
424     }
425
426     new_option->name[i] = '\0';
427     new_option->name = strdup(new_option->name);
428
429     new_option->value = strtok(NULL, "=");
430
431     if (new_option->value != NULL) {
432
433         while(isspace(*new_option->value)) {
434             new_option->value++;
435         }
436         
437         for (i = strlen(new_option->value); i > 0; i--) {
438             if (!isspace(new_option->value[i - 1])) break;
439         }
440         
441         new_option->value[i] = '\0';
442         new_option->value = strdup(new_option->value);
443     }
444
445     /* Add to list */
446
447     DLIST_ADD(*options, new_option);
448
449     return True;
450 }