import HEAD into svn+ssh://svn.samba.org/home/svn/samba/trunk
[metze/old/v3-2-winbind-ndr.git] / source / modules / vfs_netatalk.c
1 /* 
2  * AppleTalk VFS module for Samba-3.x
3  *
4  * Copyright (C) Alexei Kotovich, 2002
5  * Copyright (C) Stefan (metze) Metzmacher, 2003
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 #undef DBGC_CLASS
25 #define DBGC_CLASS DBGC_VFS
26
27 #define APPLEDOUBLE     ".AppleDouble"
28 #define ADOUBLEMODE     0777
29
30 /* atalk functions */
31
32 static int atalk_build_paths(TALLOC_CTX *ctx, const char *path,
33   const char *fname, char **adbl_path, char **orig_path, 
34   SMB_STRUCT_STAT *adbl_info, SMB_STRUCT_STAT *orig_info);
35
36 static int atalk_unlink_file(const char *path);
37
38 static int atalk_get_path_ptr(char *path)
39 {
40         int i   = 0;
41         int ptr = 0;
42         
43         for (i = 0; path[i]; i ++) {
44                 if (path[i] == '/')
45                         ptr = i;
46                 /* get out some 'spam';) from win32's file name */
47                 else if (path[i] == ':') {
48                         path[i] = '\0';
49                         break;
50                 }
51         }
52         
53         return ptr;
54 }
55
56 static int atalk_build_paths(TALLOC_CTX *ctx, const char *path, const char *fname,
57                               char **adbl_path, char **orig_path,
58                               SMB_STRUCT_STAT *adbl_info, SMB_STRUCT_STAT *orig_info)
59 {
60         int ptr0 = 0;
61         int ptr1 = 0;
62         char *dname = 0;
63         char *name  = 0;
64
65         if (!ctx || !path || !fname || !adbl_path || !orig_path ||
66                 !adbl_info || !orig_info)
67                 return -1;
68 #if 0
69         DEBUG(3, ("ATALK: PATH: %s[%s]\n", path, fname));
70 #endif
71         if (strstr(path, APPLEDOUBLE) || strstr(fname, APPLEDOUBLE)) {
72                 DEBUG(3, ("ATALK: path %s[%s] already contains %s\n", path, fname, APPLEDOUBLE));
73                 return -1;
74         }
75
76         if (fname[0] == '.') ptr0 ++;
77         if (fname[1] == '/') ptr0 ++;
78
79         *orig_path = talloc_asprintf(ctx, "%s/%s", path, &fname[ptr0]);
80
81         /* get pointer to last '/' */
82         ptr1 = atalk_get_path_ptr(*orig_path);
83
84         sys_lstat(*orig_path, orig_info);
85
86         if (S_ISDIR(orig_info->st_mode)) {
87                 *adbl_path = talloc_asprintf(ctx, "%s/%s/%s/", 
88                   path, &fname[ptr0], APPLEDOUBLE);
89         } else {
90                 dname = talloc_strdup(ctx, *orig_path);
91                 dname[ptr1] = '\0';
92                 name = *orig_path;
93                 *adbl_path = talloc_asprintf(ctx, "%s/%s/%s", 
94                   dname, APPLEDOUBLE, &name[ptr1 + 1]);
95         }
96 #if 0
97         DEBUG(3, ("ATALK: DEBUG:\n%s\n%s\n", *orig_path, *adbl_path)); 
98 #endif
99         sys_lstat(*adbl_path, adbl_info);
100         return 0;
101 }
102
103 static int atalk_unlink_file(const char *path)
104 {
105         int ret = 0;
106
107         become_root();
108         ret = unlink(path);
109         unbecome_root();
110         
111         return ret;
112 }
113
114 static void atalk_add_to_list(name_compare_entry **list)
115 {
116         int i, count = 0;
117         name_compare_entry *new_list = 0;
118         name_compare_entry *cur_list = 0;
119
120         cur_list = *list;
121
122         if (cur_list) {
123                 for (i = 0, count = 0; cur_list[i].name; i ++, count ++) {
124                         if (strstr(cur_list[i].name, APPLEDOUBLE))
125                                 return;
126                 }
127         }
128
129         if (!(new_list = calloc(1, 
130           (count == 0 ? 1 : count + 1) * sizeof(name_compare_entry))))
131                 return;
132
133         for (i = 0; i < count; i ++) {
134                 new_list[i].name    = strdup(cur_list[i].name);
135                 new_list[i].is_wild = cur_list[i].is_wild;
136         }
137
138         new_list[i].name    = strdup(APPLEDOUBLE);
139         new_list[i].is_wild = False;
140
141         free_namearray(*list);
142
143         *list = new_list;
144         new_list = 0;
145         cur_list = 0;
146 }
147
148 static void atalk_rrmdir(TALLOC_CTX *ctx, char *path)
149 {
150         char *dpath;
151         struct dirent *dent = 0;
152         DIR *dir;
153
154         if (!path) return;
155
156         dir = opendir(path);
157         if (!dir) return;
158
159         while (NULL != (dent = readdir(dir))) {
160                 if (strcmp(dent->d_name, ".") == 0 ||
161                     strcmp(dent->d_name, "..") == 0)
162                         continue;
163                 if (!(dpath = talloc_asprintf(ctx, "%s/%s", 
164                                               path, dent->d_name)))
165                         continue;
166                 atalk_unlink_file(dpath);
167         }
168
169         closedir(dir);
170 }
171
172 /* Disk operations */
173
174 /* Directory operations */
175
176 DIR *atalk_opendir(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *fname)
177 {
178         DIR *ret = 0;
179
180         ret = SMB_VFS_NEXT_OPENDIR(handle, conn, fname);
181
182         /*
183          * when we try to perform delete operation upon file which has fork
184          * in ./.AppleDouble and this directory wasn't hidden by Samba,
185          * MS Windows explorer causes the error: "Cannot find the specified file"
186          * There is some workaround to avoid this situation, i.e. if
187          * connection has not .AppleDouble entry in either veto or hide 
188          * list then it would be nice to add one.
189          */
190
191         atalk_add_to_list(&conn->hide_list);
192         atalk_add_to_list(&conn->veto_list);
193
194         return ret;
195 }
196
197 static int atalk_rmdir(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path)
198 {
199         BOOL add = False;
200         TALLOC_CTX *ctx = 0;
201         char *dpath;
202
203         if (!conn || !conn->origpath || !path) goto exit_rmdir;
204
205         /* due to there is no way to change bDeleteVetoFiles variable
206          * from this module, gotta use talloc stuff..
207          */
208
209         strstr(path, APPLEDOUBLE) ? (add = False) : (add = True);
210
211         if (!(ctx = talloc_init("remove_directory")))
212                 goto exit_rmdir;
213
214         if (!(dpath = talloc_asprintf(ctx, "%s/%s%s", 
215           conn->origpath, path, add ? "/"APPLEDOUBLE : "")))
216                 goto exit_rmdir;
217
218         atalk_rrmdir(ctx, dpath);
219
220 exit_rmdir:
221         talloc_destroy(ctx);
222         return SMB_VFS_NEXT_RMDIR(handle, conn, path);
223 }
224
225 /* File operations */
226
227 static int atalk_rename(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *old, const char *new)
228 {
229         int ret = 0;
230         char *adbl_path = 0;
231         char *orig_path = 0;
232         SMB_STRUCT_STAT adbl_info;
233         SMB_STRUCT_STAT orig_info;
234         TALLOC_CTX *ctx;
235
236         ret = SMB_VFS_NEXT_RENAME(handle, conn, old, new);
237
238         if (!conn || !old) return ret;
239
240         if (!(ctx = talloc_init("rename_file")))
241                 return ret;
242
243         if (atalk_build_paths(ctx, conn->origpath, old, &adbl_path, &orig_path, 
244           &adbl_info, &orig_info) != 0)
245                 return ret;
246
247         if (S_ISDIR(orig_info.st_mode) || S_ISREG(orig_info.st_mode)) {
248                 DEBUG(3, ("ATALK: %s has passed..\n", adbl_path));              
249                 goto exit_rename;
250         }
251
252         atalk_unlink_file(adbl_path);
253
254 exit_rename:
255         talloc_destroy(ctx);
256         return ret;
257 }
258
259 static int atalk_unlink(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path)
260 {
261         int ret = 0, i;
262         char *adbl_path = 0;
263         char *orig_path = 0;
264         SMB_STRUCT_STAT adbl_info;
265         SMB_STRUCT_STAT orig_info;
266         TALLOC_CTX *ctx;
267
268         ret = SMB_VFS_NEXT_UNLINK(handle, conn, path);
269
270         if (!conn || !path) return ret;
271
272         /* no .AppleDouble sync if veto or hide list is empty,
273          * otherwise "Cannot find the specified file" error will be caused
274          */
275
276         if (!conn->veto_list) return ret;
277         if (!conn->hide_list) return ret;
278
279         for (i = 0; conn->veto_list[i].name; i ++) {
280                 if (strstr(conn->veto_list[i].name, APPLEDOUBLE))
281                         break;
282         }
283
284         if (!conn->veto_list[i].name) {
285                 for (i = 0; conn->hide_list[i].name; i ++) {
286                         if (strstr(conn->hide_list[i].name, APPLEDOUBLE))
287                                 break;
288                         else {
289                                 DEBUG(3, ("ATALK: %s is not hidden, skipped..\n",
290                                   APPLEDOUBLE));                
291                                 return ret;
292                         }
293                 }
294         }
295
296         if (!(ctx = talloc_init("unlink_file")))
297                 return ret;
298
299         if (atalk_build_paths(ctx, conn->origpath, path, &adbl_path, &orig_path, 
300           &adbl_info, &orig_info) != 0)
301                 return ret;
302
303         if (S_ISDIR(orig_info.st_mode) || S_ISREG(orig_info.st_mode)) {
304                 DEBUG(3, ("ATALK: %s has passed..\n", adbl_path));
305                 goto exit_unlink;
306         }
307
308         atalk_unlink_file(adbl_path);
309
310 exit_unlink:    
311         talloc_destroy(ctx);
312         return ret;
313 }
314
315 static int atalk_chmod(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, mode_t mode)
316 {
317         int ret = 0;
318         char *adbl_path = 0;
319         char *orig_path = 0;
320         SMB_STRUCT_STAT adbl_info;
321         SMB_STRUCT_STAT orig_info;
322         TALLOC_CTX *ctx;
323
324         ret = SMB_VFS_NEXT_CHMOD(handle, conn, path, mode);
325
326         if (!conn || !path) return ret;
327
328         if (!(ctx = talloc_init("chmod_file")))
329                 return ret;
330
331         if (atalk_build_paths(ctx, conn->origpath, path, &adbl_path, &orig_path,
332           &adbl_info, &orig_info) != 0)
333                 return ret;
334
335         if (!S_ISDIR(orig_info.st_mode) && !S_ISREG(orig_info.st_mode)) {
336                 DEBUG(3, ("ATALK: %s has passed..\n", orig_path));              
337                 goto exit_chmod;
338         }
339
340         chmod(adbl_path, ADOUBLEMODE);
341
342 exit_chmod:     
343         talloc_destroy(ctx);
344         return ret;
345 }
346
347 static int atalk_chown(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, uid_t uid, gid_t gid)
348 {
349         int ret = 0;
350         char *adbl_path = 0;
351         char *orig_path = 0;
352         SMB_STRUCT_STAT adbl_info;
353         SMB_STRUCT_STAT orig_info;
354         TALLOC_CTX *ctx;
355
356         ret = SMB_VFS_NEXT_CHOWN(handle, conn, path, uid, gid);
357
358         if (!conn || !path) return ret;
359
360         if (!(ctx = talloc_init("chown_file")))
361                 return ret;
362
363         if (atalk_build_paths(ctx, conn->origpath, path, &adbl_path, &orig_path,
364           &adbl_info, &orig_info) != 0)
365                 return ret;
366
367         if (!S_ISDIR(orig_info.st_mode) && !S_ISREG(orig_info.st_mode)) {
368                 DEBUG(3, ("ATALK: %s has passed..\n", orig_path));              
369                 goto exit_chown;
370         }
371
372         chown(adbl_path, uid, gid);
373
374 exit_chown:     
375         talloc_destroy(ctx);
376         return ret;
377 }
378
379 static vfs_op_tuple atalk_ops[] = {
380     
381         /* Directory operations */
382
383         {SMB_VFS_OP(atalk_opendir),     SMB_VFS_OP_OPENDIR,     SMB_VFS_LAYER_TRANSPARENT},
384         {SMB_VFS_OP(atalk_rmdir),               SMB_VFS_OP_RMDIR,       SMB_VFS_LAYER_TRANSPARENT},
385
386         /* File operations */
387
388         {SMB_VFS_OP(atalk_rename),              SMB_VFS_OP_RENAME,      SMB_VFS_LAYER_TRANSPARENT},
389         {SMB_VFS_OP(atalk_unlink),              SMB_VFS_OP_UNLINK,      SMB_VFS_LAYER_TRANSPARENT},
390         {SMB_VFS_OP(atalk_chmod),               SMB_VFS_OP_CHMOD,       SMB_VFS_LAYER_TRANSPARENT},
391         {SMB_VFS_OP(atalk_chown),               SMB_VFS_OP_CHOWN,       SMB_VFS_LAYER_TRANSPARENT},
392         
393         /* Finish VFS operations definition */
394         
395         {SMB_VFS_OP(NULL),                      SMB_VFS_OP_NOOP,        SMB_VFS_LAYER_NOOP}
396 };
397
398 NTSTATUS vfs_netatalk_init(void)
399 {
400         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "netatalk", atalk_ops);
401 }