This commit was manufactured by cvs2svn to create branch 'SAMBA_3_0'.
[sfrench/samba-autobuild/.git] / examples / VFS / netatalk.c
1 /* 
2  * AppleTalk VFS module for Samba-3.x
3  *
4  * Copyright (C) Alexei Kotovich, 2002
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *  
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *  
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20
21 #include "config.h"
22 #include <stdio.h>
23 #include <sys/stat.h>
24 #ifdef HAVE_UTIME_H
25 #include <utime.h>
26 #endif
27 #ifdef HAVE_DIRENT_H
28 #include <dirent.h>
29 #endif
30 #ifdef HAVE_FCNTL_H
31 #include <fcntl.h>
32 #endif
33 #include <errno.h>
34 #include <string.h>
35 #include <includes.h>
36 #include <vfs.h>
37
38 #define APPLEDOUBLE     ".AppleDouble"
39 #define ADOUBLEMODE     0777
40
41 /* atalk functions */
42
43 static int atalk_build_paths(TALLOC_CTX *ctx, const char *path,
44   const char *fname, char **adbl_path, char **orig_path, 
45   SMB_STRUCT_STAT *adbl_info, SMB_STRUCT_STAT *orig_info);
46
47 static int atalk_unlink_file(const char *path);
48
49 static struct vfs_ops default_vfs_ops;  /* For passthrough operation */
50 static struct smb_vfs_handle_struct *atalk_handle;
51
52 static int atalk_get_path_ptr(char *path)
53 {
54         int i   = 0;
55         int ptr = 0;
56         
57         for (i = 0; path[i]; i ++) {
58                 if (path[i] == '/')
59                         ptr = i;
60                 /* get out some 'spam';) from win32's file name */
61                 else if (path[i] == ':') {
62                         path[i] = '\0';
63                         break;
64                 }
65         }
66         
67         return ptr;
68 }
69
70 static int atalk_build_paths(TALLOC_CTX *ctx, const char *path, const char *fname,
71                               char **adbl_path, char **orig_path,
72                               SMB_STRUCT_STAT *adbl_info, SMB_STRUCT_STAT *orig_info)
73 {
74         int ptr0 = 0;
75         int ptr1 = 0;
76         char *dname = 0;
77         char *name  = 0;
78
79         if (!ctx || !path || !fname || !adbl_path || !orig_path ||
80                 !adbl_info || !orig_info)
81                 return -1;
82 #if 0
83         DEBUG(3, ("ATALK: PATH: %s[%s]\n", path, fname));
84 #endif
85         if (strstr(path, APPLEDOUBLE) || strstr(fname, APPLEDOUBLE)) {
86                 DEBUG(3, ("ATALK: path %s[%s] already contains %s\n", path, fname, APPLEDOUBLE));
87                 return -1;
88         }
89
90         if (fname[0] == '.') ptr0 ++;
91         if (fname[1] == '/') ptr0 ++;
92
93         *orig_path = talloc_asprintf(ctx, "%s/%s", path, &fname[ptr0]);
94
95         /* get pointer to last '/' */
96         ptr1 = atalk_get_path_ptr(*orig_path);
97
98         sys_lstat(*orig_path, orig_info);
99
100         if (S_ISDIR(orig_info->st_mode)) {
101                 *adbl_path = talloc_asprintf(ctx, "%s/%s/%s/", 
102                   path, &fname[ptr0], APPLEDOUBLE);
103         } else {
104                 dname = talloc_strdup(ctx, *orig_path);
105                 dname[ptr1] = '\0';
106                 name = *orig_path;
107                 *adbl_path = talloc_asprintf(ctx, "%s/%s/%s", 
108                   dname, APPLEDOUBLE, &name[ptr1 + 1]);
109         }
110 #if 0
111         DEBUG(3, ("ATALK: DEBUG:\n%s\n%s\n", *orig_path, *adbl_path)); 
112 #endif
113         sys_lstat(*adbl_path, adbl_info);
114         return 0;
115 }
116
117 static int atalk_unlink_file(const char *path)
118 {
119         int ret = 0;
120
121         become_root();
122         ret = unlink(path);
123         unbecome_root();
124         
125         return ret;
126 }
127
128 static void atalk_add_to_list(name_compare_entry **list)
129 {
130         int i, count = 0;
131         name_compare_entry *new_list = 0;
132         name_compare_entry *cur_list = 0;
133
134         cur_list = *list;
135
136         if (cur_list) {
137                 for (i = 0, count = 0; cur_list[i].name; i ++, count ++) {
138                         if (strstr(cur_list[i].name, APPLEDOUBLE))
139                                 return;
140                 }
141         }
142
143         if (!(new_list = calloc(1, 
144           (count == 0 ? 1 : count + 1) * sizeof(name_compare_entry))))
145                 return;
146
147         for (i = 0; i < count; i ++) {
148                 new_list[i].name    = strdup(cur_list[i].name);
149                 new_list[i].is_wild = cur_list[i].is_wild;
150         }
151
152         new_list[i].name    = strdup(APPLEDOUBLE);
153         new_list[i].is_wild = False;
154
155         free_namearray(*list);
156
157         *list = new_list;
158         new_list = 0;
159         cur_list = 0;
160 }
161
162 static void atalk_rrmdir(TALLOC_CTX *ctx, char *path)
163 {
164         int n;
165         char *dpath;
166         struct dirent **namelist;
167
168         if (!path) return;
169
170         n = scandir(path, &namelist, 0, alphasort);
171         if (n < 0) {
172                 return;
173         } else {
174                 while (n --) {
175                         if (strcmp(namelist[n]->d_name, ".") == 0 ||
176                           strcmp(namelist[n]->d_name, "..") == 0)
177                                 continue;
178                         if (!(dpath = talloc_asprintf(ctx, "%s/%s", 
179                           path, namelist[n]->d_name)))
180                                 continue;
181                         atalk_unlink_file(dpath);
182                         free(namelist[n]);
183                 }
184         }
185 }
186
187 /* Disk operations */
188
189 /* Directory operations */
190
191 DIR *atalk_opendir(struct connection_struct *conn, const char *fname)
192 {
193         DIR *ret = 0;
194         
195         ret = default_vfs_ops.opendir(conn, fname);
196
197         /*
198          * when we try to perform delete operation upon file which has fork
199          * in ./.AppleDouble and this directory wasn't hidden by Samba,
200          * MS Windows explorer causes the error: "Cannot find the specified file"
201          * There is some workaround to avoid this situation, i.e. if
202          * connection has not .AppleDouble entry in either veto or hide 
203          * list then it would be nice to add one.
204          */
205
206         atalk_add_to_list(&conn->hide_list);
207         atalk_add_to_list(&conn->veto_list);
208
209         return ret;
210 }
211
212 static int atalk_rmdir(struct connection_struct *conn, const char *path)
213 {
214         BOOL add = False;
215         TALLOC_CTX *ctx = 0;
216         char *dpath;
217
218         if (!conn || !conn->origpath || !path) goto exit_rmdir;
219
220         /* due to there is no way to change bDeleteVetoFiles variable
221          * from this module, gotta use talloc stuff..
222          */
223
224         strstr(path, APPLEDOUBLE) ? (add = False) : (add = True);
225
226         if (!(ctx = talloc_init_named("remove_directory")))
227                 goto exit_rmdir;
228
229         if (!(dpath = talloc_asprintf(ctx, "%s/%s%s", 
230           conn->origpath, path, add ? "/"APPLEDOUBLE : "")))
231                 goto exit_rmdir;
232
233         atalk_rrmdir(ctx, dpath);
234
235 exit_rmdir:
236         talloc_destroy(ctx);
237         return default_vfs_ops.rmdir(conn, path);
238 }
239
240 /* File operations */
241
242 static int atalk_rename(struct connection_struct *conn, const char *old, const char *new)
243 {
244         int ret = 0;
245         char *adbl_path = 0;
246         char *orig_path = 0;
247         SMB_STRUCT_STAT adbl_info;
248         SMB_STRUCT_STAT orig_info;
249         TALLOC_CTX *ctx;
250
251         ret = default_vfs_ops.rename(conn, old, new);
252
253         if (!conn || !old) return ret;
254
255         if (!(ctx = talloc_init_named("rename_file")))
256                 return ret;
257
258         if (atalk_build_paths(ctx, conn->origpath, old, &adbl_path, &orig_path, 
259           &adbl_info, &orig_info) != 0)
260                 return ret;
261
262         if (S_ISDIR(orig_info.st_mode) || S_ISREG(orig_info.st_mode)) {
263                 DEBUG(3, ("ATALK: %s has passed..\n", adbl_path));              
264                 goto exit_rename;
265         }
266
267         atalk_unlink_file(adbl_path);
268
269 exit_rename:
270         talloc_destroy(ctx);
271         return ret;
272 }
273
274 static int atalk_unlink(struct connection_struct *conn, const char *path)
275 {
276         int ret = 0, i;
277         char *adbl_path = 0;
278         char *orig_path = 0;
279         SMB_STRUCT_STAT adbl_info;
280         SMB_STRUCT_STAT orig_info;
281         TALLOC_CTX *ctx;
282
283         ret = default_vfs_ops.unlink(conn, path);
284
285         if (!conn || !path) return ret;
286
287         /* no .AppleDouble sync if veto or hide list is empty,
288          * otherwise "Cannot find the specified file" error will be caused
289          */
290
291         if (!conn->veto_list) return ret;
292         if (!conn->hide_list) return ret;
293
294         for (i = 0; conn->veto_list[i].name; i ++) {
295                 if (strstr(conn->veto_list[i].name, APPLEDOUBLE))
296                         break;
297         }
298
299         if (!conn->veto_list[i].name) {
300                 for (i = 0; conn->hide_list[i].name; i ++) {
301                         if (strstr(conn->hide_list[i].name, APPLEDOUBLE))
302                                 break;
303                         else {
304                                 DEBUG(3, ("ATALK: %s is not hidden, skipped..\n",
305                                   APPLEDOUBLE));                
306                                 return ret;
307                         }
308                 }
309         }
310
311         if (!(ctx = talloc_init_named("unlink_file")))
312                 return ret;
313
314         if (atalk_build_paths(ctx, conn->origpath, path, &adbl_path, &orig_path, 
315           &adbl_info, &orig_info) != 0)
316                 return ret;
317
318         if (S_ISDIR(orig_info.st_mode) || S_ISREG(orig_info.st_mode)) {
319                 DEBUG(3, ("ATALK: %s has passed..\n", adbl_path));
320                 goto exit_unlink;
321         }
322
323         atalk_unlink_file(adbl_path);
324
325 exit_unlink:    
326         talloc_destroy(ctx);
327         return ret;
328 }
329
330 static int atalk_chmod(struct connection_struct *conn, const char *path, mode_t mode)
331 {
332         int ret = 0;
333         char *adbl_path = 0;
334         char *orig_path = 0;
335         SMB_STRUCT_STAT adbl_info;
336         SMB_STRUCT_STAT orig_info;
337         TALLOC_CTX *ctx;
338
339         ret = default_vfs_ops.chmod(conn, path, mode);
340
341         if (!conn || !path) return ret;
342
343         if (!(ctx = talloc_init_named("chmod_file")))
344                 return ret;
345
346         if (atalk_build_paths(ctx, conn->origpath, path, &adbl_path, &orig_path,
347           &adbl_info, &orig_info) != 0)
348                 return ret;
349
350         if (!S_ISDIR(orig_info.st_mode) && !S_ISREG(orig_info.st_mode)) {
351                 DEBUG(3, ("ATALK: %s has passed..\n", orig_path));              
352                 goto exit_chmod;
353         }
354
355         chmod(adbl_path, ADOUBLEMODE);
356
357 exit_chmod:     
358         talloc_destroy(ctx);
359         return ret;
360 }
361
362 static int atalk_chown(struct connection_struct *conn, const char *path, uid_t uid, gid_t gid)
363 {
364         int ret = 0;
365         char *adbl_path = 0;
366         char *orig_path = 0;
367         SMB_STRUCT_STAT adbl_info;
368         SMB_STRUCT_STAT orig_info;
369         TALLOC_CTX *ctx;
370
371         ret = default_vfs_ops.chown(conn, path, uid, gid);
372
373         if (!conn || !path) return ret;
374
375         if (!(ctx = talloc_init_named("chown_file")))
376                 return ret;
377
378         if (atalk_build_paths(ctx, conn->origpath, path, &adbl_path, &orig_path,
379           &adbl_info, &orig_info) != 0)
380                 return ret;
381
382         if (!S_ISDIR(orig_info.st_mode) && !S_ISREG(orig_info.st_mode)) {
383                 DEBUG(3, ("ATALK: %s has passed..\n", orig_path));              
384                 goto exit_chown;
385         }
386
387         chown(adbl_path, uid, gid);
388
389 exit_chown:     
390         talloc_destroy(ctx);
391         return ret;
392 }
393
394 static vfs_op_tuple atalk_ops[] = {
395     
396         /* Directory operations */
397
398         {atalk_opendir,         SMB_VFS_OP_OPENDIR,     SMB_VFS_LAYER_TRANSPARENT},
399         {atalk_rmdir,           SMB_VFS_OP_RMDIR,       SMB_VFS_LAYER_TRANSPARENT},
400
401         /* File operations */
402
403         {atalk_rename,          SMB_VFS_OP_RENAME,      SMB_VFS_LAYER_TRANSPARENT},
404         {atalk_unlink,          SMB_VFS_OP_UNLINK,      SMB_VFS_LAYER_TRANSPARENT},
405         {atalk_chmod,           SMB_VFS_OP_CHMOD,       SMB_VFS_LAYER_TRANSPARENT},
406         {atalk_chown,           SMB_VFS_OP_CHOWN,       SMB_VFS_LAYER_TRANSPARENT},
407         
408         /* Finish VFS operations definition */
409         
410         {NULL,                  SMB_VFS_OP_NOOP,        SMB_VFS_LAYER_NOOP}
411 };
412
413 /* VFS initialisation function.  Return vfs_op_tuple array back to SAMBA. */
414 vfs_op_tuple *vfs_init(int *vfs_version, struct vfs_ops *def_vfs_ops,
415   struct smb_vfs_handle_struct *vfs_handle)
416 {
417         *vfs_version = SMB_VFS_INTERFACE_VERSION;
418         memcpy(&default_vfs_ops, def_vfs_ops, sizeof(struct vfs_ops));
419         
420         atalk_handle = vfs_handle;
421
422         DEBUG(3, ("ATALK: vfs module loaded\n"));
423         return atalk_ops;
424 }
425
426 /* VFS finalization function. */
427 void vfs_done(connection_struct *conn)
428 {
429         DEBUG(3, ("ATALK: vfs module unloaded\n"));
430 }