s4:dsdb: Fix stack use after scope in gkdi_create_root_key()
[samba.git] / source3 / 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 3 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, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include "includes.h"
22 #include "smbd/smbd.h"
23 #include "system/filesys.h"
24
25 #undef DBGC_CLASS
26 #define DBGC_CLASS DBGC_VFS
27
28 #define APPLEDOUBLE     ".AppleDouble"
29 #define ADOUBLEMODE     0777
30
31 /* atalk functions */
32
33 static int atalk_build_paths(TALLOC_CTX *ctx, const char *path,
34                              const char *fname,
35                              char **adbl_path, char **orig_path,
36                              SMB_STRUCT_STAT *adbl_info,
37                              SMB_STRUCT_STAT *orig_info,
38                              bool fake_dir_create_times);
39
40 static int atalk_unlink_file(const char *path);
41
42 static int atalk_get_path_ptr(char *path)
43 {
44         int i   = 0;
45         int ptr = 0;
46         
47         for (i = 0; path[i]; i ++) {
48                 if (path[i] == '/')
49                         ptr = i;
50                 /* get out some 'spam';) from win32's file name */
51                 else if (path[i] == ':') {
52                         path[i] = '\0';
53                         break;
54                 }
55         }
56         
57         return ptr;
58 }
59
60 static int atalk_build_paths(TALLOC_CTX *ctx, const char *path,
61                              const char *fname,
62                              char **adbl_path, char **orig_path,
63                              SMB_STRUCT_STAT *adbl_info,
64                              SMB_STRUCT_STAT *orig_info,
65                              bool fake_dir_create_times)
66 {
67         int ptr0 = 0;
68         int ptr1 = 0;
69         char *dname = 0;
70         char *name  = 0;
71
72         if (!ctx || !path || !fname || !adbl_path || !orig_path ||
73                 !adbl_info || !orig_info)
74                 return -1;
75 #if 0
76         DEBUG(3, ("ATALK: PATH: %s[%s]\n", path, fname));
77 #endif
78         if (strstr(path, APPLEDOUBLE) || strstr(fname, APPLEDOUBLE)) {
79                 DEBUG(3, ("ATALK: path %s[%s] already contains %s\n", path, fname, APPLEDOUBLE));
80                 return -1;
81         }
82
83         if (fname[0] == '.') ptr0 ++;
84         if (fname[1] == '/') ptr0 ++;
85
86         *orig_path = talloc_asprintf(ctx, "%s/%s", path, &fname[ptr0]);
87
88         /* get pointer to last '/' */
89         ptr1 = atalk_get_path_ptr(*orig_path);
90
91         sys_lstat(*orig_path, orig_info, fake_dir_create_times);
92
93         if (S_ISDIR(orig_info->st_ex_mode)) {
94                 *adbl_path = talloc_asprintf(ctx, "%s/%s/%s/", 
95                   path, &fname[ptr0], APPLEDOUBLE);
96         } else {
97                 dname = talloc_strdup(ctx, *orig_path);
98                 dname[ptr1] = '\0';
99                 name = *orig_path;
100                 *adbl_path = talloc_asprintf(ctx, "%s/%s/%s", 
101                   dname, APPLEDOUBLE, &name[ptr1 + 1]);
102         }
103 #if 0
104         DEBUG(3, ("ATALK: DEBUG:\n%s\n%s\n", *orig_path, *adbl_path)); 
105 #endif
106         sys_lstat(*adbl_path, adbl_info, fake_dir_create_times);
107         return 0;
108 }
109
110 static int atalk_unlink_file(const char *path)
111 {
112         int ret = 0;
113
114         become_root();
115         ret = unlink(path);
116         unbecome_root();
117         
118         return ret;
119 }
120
121 static void atalk_add_to_list(name_compare_entry **list)
122 {
123         int i, count = 0;
124         name_compare_entry *new_list = 0;
125         name_compare_entry *cur_list = 0;
126
127         cur_list = *list;
128
129         if (cur_list) {
130                 for (i = 0, count = 0; cur_list[i].name; i ++, count ++) {
131                         if (strstr(cur_list[i].name, APPLEDOUBLE))
132                                 return;
133                 }
134         }
135
136         if (!(new_list = SMB_CALLOC_ARRAY(name_compare_entry, count + 2)))
137                 return;
138
139         for (i = 0; i < count; i ++) {
140                 new_list[i].name    = SMB_STRDUP(cur_list[i].name);
141                 new_list[i].is_wild = cur_list[i].is_wild;
142         }
143
144         new_list[i].name    = SMB_STRDUP(APPLEDOUBLE);
145         new_list[i].is_wild = False;
146
147         free_namearray(*list);
148
149         *list = new_list;
150         new_list = 0;
151         cur_list = 0;
152 }
153
154 static void atalk_rrmdir(TALLOC_CTX *ctx, char *path)
155 {
156         char *dpath;
157         SMB_STRUCT_DIRENT *dent = 0;
158         SMB_STRUCT_DIR *dir;
159
160         if (!path) return;
161
162         dir = sys_opendir(path);
163         if (!dir) return;
164
165         while (NULL != (dent = sys_readdir(dir))) {
166                 if (strcmp(dent->d_name, ".") == 0 ||
167                     strcmp(dent->d_name, "..") == 0)
168                         continue;
169                 if (!(dpath = talloc_asprintf(ctx, "%s/%s", 
170                                               path, dent->d_name)))
171                         continue;
172                 atalk_unlink_file(dpath);
173         }
174
175         sys_closedir(dir);
176 }
177
178 /* Disk operations */
179
180 /* Directory operations */
181
182 static SMB_STRUCT_DIR *atalk_opendir(struct vfs_handle_struct *handle, const char *fname, const char *mask, uint32 attr)
183 {
184         SMB_STRUCT_DIR *ret = 0;
185
186         ret = SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
187
188         /*
189          * when we try to perform delete operation upon file which has fork
190          * in ./.AppleDouble and this directory wasn't hidden by Samba,
191          * MS Windows explorer causes the error: "Cannot find the specified file"
192          * There is some workaround to avoid this situation, i.e. if
193          * connection has not .AppleDouble entry in either veto or hide 
194          * list then it would be nice to add one.
195          */
196
197         atalk_add_to_list(&handle->conn->hide_list);
198         atalk_add_to_list(&handle->conn->veto_list);
199
200         return ret;
201 }
202
203 static SMB_STRUCT_DIR *atalk_fdopendir(struct vfs_handle_struct *handle, files_struct *fsp, const char *mask, uint32 attr)
204 {
205         SMB_STRUCT_DIR *ret = 0;
206
207         ret = SMB_VFS_NEXT_FDOPENDIR(handle, fsp, mask, attr);
208
209         if (ret == NULL) {
210                 return ret;
211         }
212
213         /*
214          * when we try to perform delete operation upon file which has fork
215          * in ./.AppleDouble and this directory wasn't hidden by Samba,
216          * MS Windows explorer causes the error: "Cannot find the specified file"
217          * There is some workaround to avoid this situation, i.e. if
218          * connection has not .AppleDouble entry in either veto or hide 
219          * list then it would be nice to add one.
220          */
221
222         atalk_add_to_list(&handle->conn->hide_list);
223         atalk_add_to_list(&handle->conn->veto_list);
224
225         return ret;
226 }
227
228 static int atalk_rmdir(struct vfs_handle_struct *handle, const char *path)
229 {
230         bool add = False;
231         TALLOC_CTX *ctx = 0;
232         char *dpath;
233
234         if (!handle->conn->origpath || !path) goto exit_rmdir;
235
236         /* due to there is no way to change bDeleteVetoFiles variable
237          * from this module, gotta use talloc stuff..
238          */
239
240         strstr(path, APPLEDOUBLE) ? (add = False) : (add = True);
241
242         if (!(ctx = talloc_init("remove_directory")))
243                 goto exit_rmdir;
244
245         if (!(dpath = talloc_asprintf(ctx, "%s/%s%s", 
246           handle->conn->origpath, path, add ? "/"APPLEDOUBLE : "")))
247                 goto exit_rmdir;
248
249         atalk_rrmdir(ctx, dpath);
250
251 exit_rmdir:
252         talloc_destroy(ctx);
253         return SMB_VFS_NEXT_RMDIR(handle, path);
254 }
255
256 /* File operations */
257
258 static int atalk_rename(struct vfs_handle_struct *handle,
259                         const struct smb_filename *smb_fname_src,
260                         const struct smb_filename *smb_fname_dst)
261 {
262         int ret = 0;
263         char *oldname = NULL;
264         char *adbl_path = NULL;
265         char *orig_path = NULL;
266         SMB_STRUCT_STAT adbl_info;
267         SMB_STRUCT_STAT orig_info;
268         NTSTATUS status;
269
270         ret = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
271
272         status = get_full_smb_filename(talloc_tos(), smb_fname_src, &oldname);
273         if (!NT_STATUS_IS_OK(status)) {
274                 return ret;
275         }
276
277         if (atalk_build_paths(talloc_tos(), handle->conn->origpath, oldname,
278                               &adbl_path, &orig_path, &adbl_info,
279                               &orig_info, false) != 0)
280                 goto exit_rename;
281
282         if (S_ISDIR(orig_info.st_ex_mode) || S_ISREG(orig_info.st_ex_mode)) {
283                 DEBUG(3, ("ATALK: %s has passed..\n", adbl_path));              
284                 goto exit_rename;
285         }
286
287         atalk_unlink_file(adbl_path);
288
289 exit_rename:
290         TALLOC_FREE(oldname);
291         TALLOC_FREE(adbl_path);
292         TALLOC_FREE(orig_path);
293         return ret;
294 }
295
296 static int atalk_unlink(struct vfs_handle_struct *handle,
297                         const struct smb_filename *smb_fname)
298 {
299         int ret = 0, i;
300         char *path = NULL;
301         char *adbl_path = NULL;
302         char *orig_path = NULL;
303         SMB_STRUCT_STAT adbl_info;
304         SMB_STRUCT_STAT orig_info;
305         NTSTATUS status;
306
307         ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
308
309         status = get_full_smb_filename(talloc_tos(), smb_fname, &path);
310         if (!NT_STATUS_IS_OK(status)) {
311                 return ret;
312         }
313
314         /* no .AppleDouble sync if veto or hide list is empty,
315          * otherwise "Cannot find the specified file" error will be caused
316          */
317
318         if (!handle->conn->veto_list) return ret;
319         if (!handle->conn->hide_list) return ret;
320
321         for (i = 0; handle->conn->veto_list[i].name; i ++) {
322                 if (strstr(handle->conn->veto_list[i].name, APPLEDOUBLE))
323                         break;
324         }
325
326         if (!handle->conn->veto_list[i].name) {
327                 for (i = 0; handle->conn->hide_list[i].name; i ++) {
328                         if (strstr(handle->conn->hide_list[i].name, APPLEDOUBLE))
329                                 break;
330                         else {
331                                 DEBUG(3, ("ATALK: %s is not hidden, skipped..\n",
332                                   APPLEDOUBLE));                
333                                 goto exit_unlink;
334                         }
335                 }
336         }
337
338         if (atalk_build_paths(talloc_tos(), handle->conn->origpath, path,
339                               &adbl_path, &orig_path,
340                               &adbl_info, &orig_info, false) != 0)
341                 goto exit_unlink;
342
343         if (S_ISDIR(orig_info.st_ex_mode) || S_ISREG(orig_info.st_ex_mode)) {
344                 DEBUG(3, ("ATALK: %s has passed..\n", adbl_path));
345                 goto exit_unlink;
346         }
347
348         atalk_unlink_file(adbl_path);
349
350 exit_unlink:
351         TALLOC_FREE(path);
352         TALLOC_FREE(adbl_path);
353         TALLOC_FREE(orig_path);
354         return ret;
355 }
356
357 static int atalk_chmod(struct vfs_handle_struct *handle, const char *path, mode_t mode)
358 {
359         int ret = 0;
360         char *adbl_path = 0;
361         char *orig_path = 0;
362         SMB_STRUCT_STAT adbl_info;
363         SMB_STRUCT_STAT orig_info;
364         TALLOC_CTX *ctx;
365
366         ret = SMB_VFS_NEXT_CHMOD(handle, path, mode);
367
368         if (!path) return ret;
369
370         if (!(ctx = talloc_init("chmod_file")))
371                 return ret;
372
373         if (atalk_build_paths(ctx, handle->conn->origpath, path, &adbl_path,
374                               &orig_path, &adbl_info, &orig_info,
375                               false) != 0)
376                 goto exit_chmod;
377
378         if (!S_ISDIR(orig_info.st_ex_mode) && !S_ISREG(orig_info.st_ex_mode)) {
379                 DEBUG(3, ("ATALK: %s has passed..\n", orig_path));              
380                 goto exit_chmod;
381         }
382
383         chmod(adbl_path, ADOUBLEMODE);
384
385 exit_chmod:     
386         talloc_destroy(ctx);
387         return ret;
388 }
389
390 static int atalk_chown(struct vfs_handle_struct *handle, const char *path, uid_t uid, gid_t gid)
391 {
392         int ret = 0;
393         char *adbl_path = 0;
394         char *orig_path = 0;
395         SMB_STRUCT_STAT adbl_info;
396         SMB_STRUCT_STAT orig_info;
397         TALLOC_CTX *ctx;
398
399         ret = SMB_VFS_NEXT_CHOWN(handle, path, uid, gid);
400
401         if (!path) return ret;
402
403         if (!(ctx = talloc_init("chown_file")))
404                 return ret;
405
406         if (atalk_build_paths(ctx, handle->conn->origpath, path,
407                               &adbl_path, &orig_path,
408                               &adbl_info, &orig_info, false) != 0)
409                 goto exit_chown;
410
411         if (!S_ISDIR(orig_info.st_ex_mode) && !S_ISREG(orig_info.st_ex_mode)) {
412                 DEBUG(3, ("ATALK: %s has passed..\n", orig_path));              
413                 goto exit_chown;
414         }
415
416         if (chown(adbl_path, uid, gid) == -1) {
417                 DEBUG(3, ("ATALK: chown error %s\n", strerror(errno)));
418         }
419
420 exit_chown:     
421         talloc_destroy(ctx);
422         return ret;
423 }
424
425 static int atalk_lchown(struct vfs_handle_struct *handle, const char *path, uid_t uid, gid_t gid)
426 {
427         int ret = 0;
428         char *adbl_path = 0;
429         char *orig_path = 0;
430         SMB_STRUCT_STAT adbl_info;
431         SMB_STRUCT_STAT orig_info;
432         TALLOC_CTX *ctx;
433
434         ret = SMB_VFS_NEXT_CHOWN(handle, path, uid, gid);
435
436         if (!path) return ret;
437
438         if (!(ctx = talloc_init("lchown_file")))
439                 return ret;
440
441         if (atalk_build_paths(ctx, handle->conn->origpath, path,
442                               &adbl_path, &orig_path,
443                               &adbl_info, &orig_info, false) != 0)
444                 goto exit_lchown;
445
446         if (!S_ISDIR(orig_info.st_ex_mode) && !S_ISREG(orig_info.st_ex_mode)) {
447                 DEBUG(3, ("ATALK: %s has passed..\n", orig_path));              
448                 goto exit_lchown;
449         }
450
451         if (lchown(adbl_path, uid, gid) == -1) {
452                 DEBUG(3, ("ATALK: lchown error %s\n", strerror(errno)));
453         }
454
455 exit_lchown:    
456         talloc_destroy(ctx);
457         return ret;
458 }
459
460 static struct vfs_fn_pointers vfs_netatalk_fns = {
461         .opendir = atalk_opendir,
462         .fdopendir = atalk_fdopendir,
463         .rmdir = atalk_rmdir,
464         .rename = atalk_rename,
465         .unlink = atalk_unlink,
466         .chmod = atalk_chmod,
467         .chown = atalk_chown,
468         .lchown = atalk_lchown,
469 };
470
471 NTSTATUS vfs_netatalk_init(void);
472 NTSTATUS vfs_netatalk_init(void)
473 {
474         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "netatalk",
475                                 &vfs_netatalk_fns);
476 }