This commit was manufactured by cvs2svn to create branch 'SAMBA_3_0'.(This used to...
[ira/wip.git] / source3 / modules / 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         char *dpath;
165         struct dirent *dent = 0;
166         DIR *dir;
167
168         if (!path) return;
169
170         dir = opendir(path);
171         if (!dir) return;
172
173         while (NULL != (dent = readdir(dir))) {
174                 if (strcmp(dent->d_name, ".") == 0 ||
175                     strcmp(dent->d_name, "..") == 0)
176                         continue;
177                 if (!(dpath = talloc_asprintf(ctx, "%s/%s", 
178                                               path, dent->d_name)))
179                         continue;
180                 atalk_unlink_file(dpath);
181         }
182
183         closedir(dir);
184 }
185
186 /* Disk operations */
187
188 /* Directory operations */
189
190 DIR *atalk_opendir(struct connection_struct *conn, const char *fname)
191 {
192         DIR *ret = 0;
193         
194         ret = default_vfs_ops.opendir(conn, fname);
195
196         /*
197          * when we try to perform delete operation upon file which has fork
198          * in ./.AppleDouble and this directory wasn't hidden by Samba,
199          * MS Windows explorer causes the error: "Cannot find the specified file"
200          * There is some workaround to avoid this situation, i.e. if
201          * connection has not .AppleDouble entry in either veto or hide 
202          * list then it would be nice to add one.
203          */
204
205         atalk_add_to_list(&conn->hide_list);
206         atalk_add_to_list(&conn->veto_list);
207
208         return ret;
209 }
210
211 static int atalk_rmdir(struct connection_struct *conn, const char *path)
212 {
213         BOOL add = False;
214         TALLOC_CTX *ctx = 0;
215         char *dpath;
216
217         if (!conn || !conn->origpath || !path) goto exit_rmdir;
218
219         /* due to there is no way to change bDeleteVetoFiles variable
220          * from this module, gotta use talloc stuff..
221          */
222
223         strstr(path, APPLEDOUBLE) ? (add = False) : (add = True);
224
225         if (!(ctx = talloc_init("remove_directory")))
226                 goto exit_rmdir;
227
228         if (!(dpath = talloc_asprintf(ctx, "%s/%s%s", 
229           conn->origpath, path, add ? "/"APPLEDOUBLE : "")))
230                 goto exit_rmdir;
231
232         atalk_rrmdir(ctx, dpath);
233
234 exit_rmdir:
235         talloc_destroy(ctx);
236         return default_vfs_ops.rmdir(conn, path);
237 }
238
239 /* File operations */
240
241 static int atalk_rename(struct connection_struct *conn, const char *old, const char *new)
242 {
243         int ret = 0;
244         char *adbl_path = 0;
245         char *orig_path = 0;
246         SMB_STRUCT_STAT adbl_info;
247         SMB_STRUCT_STAT orig_info;
248         TALLOC_CTX *ctx;
249
250         ret = default_vfs_ops.rename(conn, old, new);
251
252         if (!conn || !old) return ret;
253
254         if (!(ctx = talloc_init("rename_file")))
255                 return ret;
256
257         if (atalk_build_paths(ctx, conn->origpath, old, &adbl_path, &orig_path, 
258           &adbl_info, &orig_info) != 0)
259                 return ret;
260
261         if (S_ISDIR(orig_info.st_mode) || S_ISREG(orig_info.st_mode)) {
262                 DEBUG(3, ("ATALK: %s has passed..\n", adbl_path));              
263                 goto exit_rename;
264         }
265
266         atalk_unlink_file(adbl_path);
267
268 exit_rename:
269         talloc_destroy(ctx);
270         return ret;
271 }
272
273 static int atalk_unlink(struct connection_struct *conn, const char *path)
274 {
275         int ret = 0, i;
276         char *adbl_path = 0;
277         char *orig_path = 0;
278         SMB_STRUCT_STAT adbl_info;
279         SMB_STRUCT_STAT orig_info;
280         TALLOC_CTX *ctx;
281
282         ret = default_vfs_ops.unlink(conn, path);
283
284         if (!conn || !path) return ret;
285
286         /* no .AppleDouble sync if veto or hide list is empty,
287          * otherwise "Cannot find the specified file" error will be caused
288          */
289
290         if (!conn->veto_list) return ret;
291         if (!conn->hide_list) return ret;
292
293         for (i = 0; conn->veto_list[i].name; i ++) {
294                 if (strstr(conn->veto_list[i].name, APPLEDOUBLE))
295                         break;
296         }
297
298         if (!conn->veto_list[i].name) {
299                 for (i = 0; conn->hide_list[i].name; i ++) {
300                         if (strstr(conn->hide_list[i].name, APPLEDOUBLE))
301                                 break;
302                         else {
303                                 DEBUG(3, ("ATALK: %s is not hidden, skipped..\n",
304                                   APPLEDOUBLE));                
305                                 return ret;
306                         }
307                 }
308         }
309
310         if (!(ctx = talloc_init("unlink_file")))
311                 return ret;
312
313         if (atalk_build_paths(ctx, conn->origpath, path, &adbl_path, &orig_path, 
314           &adbl_info, &orig_info) != 0)
315                 return ret;
316
317         if (S_ISDIR(orig_info.st_mode) || S_ISREG(orig_info.st_mode)) {
318                 DEBUG(3, ("ATALK: %s has passed..\n", adbl_path));
319                 goto exit_unlink;
320         }
321
322         atalk_unlink_file(adbl_path);
323
324 exit_unlink:    
325         talloc_destroy(ctx);
326         return ret;
327 }
328
329 static int atalk_chmod(struct connection_struct *conn, const char *path, mode_t mode)
330 {
331         int ret = 0;
332         char *adbl_path = 0;
333         char *orig_path = 0;
334         SMB_STRUCT_STAT adbl_info;
335         SMB_STRUCT_STAT orig_info;
336         TALLOC_CTX *ctx;
337
338         ret = default_vfs_ops.chmod(conn, path, mode);
339
340         if (!conn || !path) return ret;
341
342         if (!(ctx = talloc_init("chmod_file")))
343                 return ret;
344
345         if (atalk_build_paths(ctx, conn->origpath, path, &adbl_path, &orig_path,
346           &adbl_info, &orig_info) != 0)
347                 return ret;
348
349         if (!S_ISDIR(orig_info.st_mode) && !S_ISREG(orig_info.st_mode)) {
350                 DEBUG(3, ("ATALK: %s has passed..\n", orig_path));              
351                 goto exit_chmod;
352         }
353
354         chmod(adbl_path, ADOUBLEMODE);
355
356 exit_chmod:     
357         talloc_destroy(ctx);
358         return ret;
359 }
360
361 static int atalk_chown(struct connection_struct *conn, const char *path, uid_t uid, gid_t gid)
362 {
363         int ret = 0;
364         char *adbl_path = 0;
365         char *orig_path = 0;
366         SMB_STRUCT_STAT adbl_info;
367         SMB_STRUCT_STAT orig_info;
368         TALLOC_CTX *ctx;
369
370         ret = default_vfs_ops.chown(conn, path, uid, gid);
371
372         if (!conn || !path) return ret;
373
374         if (!(ctx = talloc_init("chown_file")))
375                 return ret;
376
377         if (atalk_build_paths(ctx, conn->origpath, path, &adbl_path, &orig_path,
378           &adbl_info, &orig_info) != 0)
379                 return ret;
380
381         if (!S_ISDIR(orig_info.st_mode) && !S_ISREG(orig_info.st_mode)) {
382                 DEBUG(3, ("ATALK: %s has passed..\n", orig_path));              
383                 goto exit_chown;
384         }
385
386         chown(adbl_path, uid, gid);
387
388 exit_chown:     
389         talloc_destroy(ctx);
390         return ret;
391 }
392
393 static vfs_op_tuple atalk_ops[] = {
394     
395         /* Directory operations */
396
397         {atalk_opendir,         SMB_VFS_OP_OPENDIR,     SMB_VFS_LAYER_TRANSPARENT},
398         {atalk_rmdir,           SMB_VFS_OP_RMDIR,       SMB_VFS_LAYER_TRANSPARENT},
399
400         /* File operations */
401
402         {atalk_rename,          SMB_VFS_OP_RENAME,      SMB_VFS_LAYER_TRANSPARENT},
403         {atalk_unlink,          SMB_VFS_OP_UNLINK,      SMB_VFS_LAYER_TRANSPARENT},
404         {atalk_chmod,           SMB_VFS_OP_CHMOD,       SMB_VFS_LAYER_TRANSPARENT},
405         {atalk_chown,           SMB_VFS_OP_CHOWN,       SMB_VFS_LAYER_TRANSPARENT},
406         
407         /* Finish VFS operations definition */
408         
409         {NULL,                  SMB_VFS_OP_NOOP,        SMB_VFS_LAYER_NOOP}
410 };
411
412 /* VFS initialisation function.  Return vfs_op_tuple array back to SAMBA. */
413 vfs_op_tuple *vfs_init(int *vfs_version, struct vfs_ops *def_vfs_ops,
414   struct smb_vfs_handle_struct *vfs_handle)
415 {
416         *vfs_version = SMB_VFS_INTERFACE_VERSION;
417         memcpy(&default_vfs_ops, def_vfs_ops, sizeof(struct vfs_ops));
418         
419         atalk_handle = vfs_handle;
420
421         DEBUG(3, ("ATALK: vfs module loaded\n"));
422         return atalk_ops;
423 }
424
425 /* VFS finalization function. */
426 void vfs_done(connection_struct *conn)
427 {
428         DEBUG(3, ("ATALK: vfs module unloaded\n"));
429 }