]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/filedir.c
Merge master
[netatalk.git] / etc / afpd / filedir.c
1 /*
2  * Copyright (c) 1990,1993 Regents of The University of Michigan.
3  * All Rights Reserved.  See COPYRIGHT.
4  */
5
6 #ifdef HAVE_CONFIG_H
7 #include "config.h"
8 #endif /* HAVE_CONFIG_H */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <errno.h>
14 #include <sys/param.h>
15
16 #include <atalk/adouble.h>
17 #include <atalk/vfs.h>
18 #include <atalk/afp.h>
19 #include <atalk/util.h>
20 #include <atalk/cnid.h>
21 #include <atalk/logger.h>
22 #include <atalk/unix.h>
23 #include <atalk/bstrlib.h>
24 #include <atalk/bstradd.h>
25 #include <atalk/acl.h>
26
27 #include "directory.h"
28 #include "dircache.h"
29 #include "desktop.h"
30 #include "volume.h"
31 #include "fork.h"
32 #include "file.h"
33 #include "globals.h"
34 #include "filedir.h"
35 #include "unix.h"
36
37 int afp_getfildirparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
38 {
39     struct stat     *st;
40     struct vol      *vol;
41     struct dir      *dir;
42     u_int32_t           did;
43     int         ret;
44     size_t      buflen;
45     u_int16_t       fbitmap, dbitmap, vid;
46     struct path         *s_path;
47
48     *rbuflen = 0;
49     ibuf += 2;
50
51     memcpy( &vid, ibuf, sizeof( vid ));
52     ibuf += sizeof( vid );
53     if (NULL == ( vol = getvolbyvid( vid )) ) {
54         /* was AFPERR_PARAM but it helps OS 10.3 when a volume has been removed
55          * from the list.
56          */
57         return( AFPERR_ACCESS );
58     }
59
60     memcpy( &did, ibuf, sizeof( did ));
61     ibuf += sizeof( did );
62
63     if (NULL == ( dir = dirlookup( vol, did )) ) {
64         return afp_errno;
65     }
66
67     memcpy( &fbitmap, ibuf, sizeof( fbitmap ));
68     fbitmap = ntohs( fbitmap );
69     ibuf += sizeof( fbitmap );
70     memcpy( &dbitmap, ibuf, sizeof( dbitmap ));
71     dbitmap = ntohs( dbitmap );
72     ibuf += sizeof( dbitmap );
73
74     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
75         return get_afp_errno(AFPERR_NOOBJ);
76     }
77
78     LOG(log_debug, logtype_afpd, "getfildirparams(vid:%u, did:%u, f/d:%04x/%04x) {cwdid:%u, cwd: %s, name:'%s'}",
79         ntohs(vid), ntohl(dir->d_did), fbitmap, dbitmap,
80         ntohl(curdir->d_did), cfrombstr(curdir->d_fullpath), s_path->u_name);
81
82     st   = &s_path->st;
83     if (!s_path->st_valid) {
84         /* it's a dir and it should be there
85          * because we chdir in it in cname or
86          * it's curdir (maybe deleted, but then we can't know).
87          * So we need to try harder.
88          */
89         of_statdir(vol, s_path);
90     }
91     if ( s_path->st_errno != 0 ) {
92         if (afp_errno != AFPERR_ACCESS) {
93             return( AFPERR_NOOBJ );
94         }
95     }
96
97
98     buflen = 0;
99     if (S_ISDIR(st->st_mode)) {
100         if (dbitmap) {
101             dir = s_path->d_dir;
102             if (!dir)
103                 return AFPERR_NOOBJ;
104
105             ret = getdirparams(vol, dbitmap, s_path, dir,
106                                rbuf + 3 * sizeof( u_int16_t ), &buflen );
107             if (ret != AFP_OK )
108                 return( ret );
109         }
110         /* this is a directory */
111         *(rbuf + 2 * sizeof( u_int16_t )) = (char) FILDIRBIT_ISDIR;
112     } else {
113         if (fbitmap && AFP_OK != (ret = getfilparams(vol, fbitmap, s_path, curdir,
114                                                      rbuf + 3 * sizeof( u_int16_t ), &buflen )) ) {
115             return( ret );
116         }
117         /* this is a file */
118         *(rbuf + 2 * sizeof( u_int16_t )) = FILDIRBIT_ISFILE;
119     }
120     *rbuflen = buflen + 3 * sizeof( u_int16_t );
121     fbitmap = htons( fbitmap );
122     memcpy( rbuf, &fbitmap, sizeof( fbitmap ));
123     rbuf += sizeof( fbitmap );
124     dbitmap = htons( dbitmap );
125     memcpy( rbuf, &dbitmap, sizeof( dbitmap ));
126     rbuf += sizeof( dbitmap ) + sizeof( u_char );
127     *rbuf = 0;
128
129     return( AFP_OK );
130 }
131
132 int afp_setfildirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
133 {
134     struct stat *st;
135     struct vol  *vol;
136     struct dir  *dir;
137     struct path *path;
138     u_int16_t   vid, bitmap;
139     int     did, rc;
140
141     *rbuflen = 0;
142     ibuf += 2;
143     memcpy( &vid, ibuf, sizeof(vid));
144     ibuf += sizeof( vid );
145
146     if (NULL == ( vol = getvolbyvid( vid )) ) {
147         return( AFPERR_PARAM );
148     }
149
150     if (vol->v_flags & AFPVOL_RO)
151         return AFPERR_VLOCK;
152
153     memcpy( &did, ibuf, sizeof( did));
154     ibuf += sizeof( did);
155
156     if (NULL == ( dir = dirlookup( vol, did )) ) {
157         return afp_errno;
158     }
159
160     memcpy( &bitmap, ibuf, sizeof( bitmap ));
161     bitmap = ntohs( bitmap );
162     ibuf += sizeof( bitmap );
163
164     if (NULL == ( path = cname( vol, dir, &ibuf ))) {
165         return get_afp_errno(AFPERR_NOOBJ);
166     }
167
168     st   = &path->st;
169     if (!path->st_valid) {
170         /* it's a dir and it should be there
171          * because we chdir in it in cname
172          */
173         of_statdir(vol, path);
174     }
175
176     if ( path->st_errno != 0 ) {
177         if (afp_errno != AFPERR_ACCESS)
178             return( AFPERR_NOOBJ );
179     }
180     /*
181      * If ibuf is odd, make it even.
182      */
183     if ((u_long)ibuf & 1 ) {
184         ibuf++;
185     }
186
187     if (S_ISDIR(st->st_mode)) {
188         rc = setdirparams(vol, path, bitmap, ibuf );
189     } else {
190         rc = setfilparams(vol, path, bitmap, ibuf );
191     }
192     if ( rc == AFP_OK ) {
193         setvoltime(obj, vol );
194     }
195
196     return( rc );
197 }
198
199 /* --------------------------------------------
200    Factorise some checks on a pathname
201 */
202 int check_name(const struct vol *vol, char *name)
203 {
204     /* check for illegal characters in the unix filename */
205     if (!wincheck(vol, name))
206         return AFPERR_PARAM;
207
208     if ((vol->v_flags & AFPVOL_NOHEX) && strchr(name, '/'))
209         return AFPERR_PARAM;
210
211     if (!vol->vfs->vfs_validupath(vol, name)) {
212         LOG(log_error, logtype_afpd, "check_name: illegal name: '%s'", name);
213         return AFPERR_EXIST;
214     }
215
216     /* check for vetoed filenames */
217     if (veto_file(vol->v_veto, name))
218         return AFPERR_EXIST;
219     return 0;
220 }
221
222 /* ------------------------- 
223     move and rename sdir:oldname to curdir:newname in volume vol
224     special care is needed for lock   
225 */
226 static int moveandrename(const struct vol *vol,
227                          struct dir *sdir,
228                          int sdir_fd,
229                          char *oldname,
230                          char *newname,
231                          int isdir)
232 {
233     char            *p;
234     char            *upath;
235     int             rc;
236     struct stat     *st, nst;
237     int             adflags;
238     struct adouble      ad;
239     struct adouble      *adp;
240     struct ofork        *opened = NULL;
241     struct path     path;
242     cnid_t          id;
243     int             cwd_fd = -1;
244
245     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
246     adp = &ad;
247     adflags = 0;
248
249     if (!isdir) {
250         if ((p = mtoupath(vol, oldname, sdir->d_did, utf8_encoding())) == NULL)
251             return AFPERR_PARAM; /* can't convert */
252
253 #ifndef HAVE_RENAMEAT
254         /* Need full path */
255         id = cnid_get(vol->v_cdb, sdir->d_did, p, strlen(p));
256         p = ctoupath( vol, sdir, oldname );
257         if (!p)
258             return AFPERR_PARAM; /* pathname too long */
259 #endif /* HAVE_RENAMEAT */
260
261         path.st_valid = 0;
262         path.u_name = p;
263 #ifdef HAVE_RENAMEAT
264         opened = of_findnameat(sdir_fd, &path);
265 #else
266         opened = of_findname(&path);
267 #endif /* HAVE_RENAMEAT */
268         if (opened) {
269             /* reuse struct adouble so it won't break locks */
270             adp = opened->of_ad;
271         }
272     } else {
273         id = sdir->d_did; /* we already have the CNID */
274         p = ctoupath( vol, dirlookup(vol, sdir->d_pdid), oldname );
275         if (!p) {
276             return AFPERR_PARAM;
277         }
278         adflags = ADFLAGS_DIR;
279     }
280
281     /*
282      * p now points to either
283      *   a) full pathname of the source fs object (if renameat is not available)
284      *   b) the oldname (renameat is available)
285      * we are in the dest folder so we need to use 
286      *   a) p for ad_open
287      *   b) fchdir sdir_fd before eg ad_open or use *at functions where appropiate
288      */
289
290     if (sdir_fd != -1) {
291         if ((cwd_fd = open(".", O_RDONLY)) == -1)
292             return AFPERR_MISC;
293         if (fchdir(sdir_fd) != 0) {
294             rc = AFPERR_MISC;
295             goto exit;
296         }
297     }
298     if (!ad_metadata(p, adflags, adp)) {
299         u_int16_t bshort;
300
301         ad_getattr(adp, &bshort);
302         
303         ad_close_metadata( adp);
304         if ((bshort & htons(ATTRBIT_NORENAME))) {
305             rc = AFPERR_OLOCK;
306             goto exit;
307         }
308     }
309     if (sdir_fd != -1) {
310         if (fchdir(cwd_fd) != 0) {
311             LOG(log_error, logtype_afpd, "moveandrename: %s", strerror(errno) );
312             rc = AFPERR_MISC;
313             goto exit;
314         }
315     }
316
317     if (NULL == (upath = mtoupath(vol, newname, curdir->d_did, utf8_encoding()))){ 
318         rc = AFPERR_PARAM;
319         goto exit;
320     }
321     path.u_name = upath;
322     st = &path.st;
323     if (0 != (rc = check_name(vol, upath))) {
324         goto exit;
325     }
326
327     /* source == destination. we just silently accept this. */
328     if ((!isdir && curdir == sdir) || (isdir && curdir->d_did == sdir->d_pdid)) {
329         if (strcmp(oldname, newname) == 0) {
330             rc = AFP_OK;
331             goto exit;
332         }
333
334         if (stat(upath, st) == 0 || caseenumerate(vol, &path, curdir) == 0) {
335             if (!stat(p, &nst) && !(nst.st_dev == st->st_dev && nst.st_ino == st->st_ino) ) {
336                 /* not the same file */
337                 rc = AFPERR_EXIST;
338                 goto exit;
339             }
340             errno = 0;
341         }
342     } else if (stat(upath, st ) == 0 || caseenumerate(vol, &path, curdir) == 0) {
343         rc = AFPERR_EXIST;
344         goto exit;
345     }
346
347     if ( !isdir ) {
348         path.st_valid = 1;
349         path.st_errno = errno;
350         if (of_findname(&path)) {
351             rc = AFPERR_EXIST; /* was AFPERR_BUSY; */
352         } else {
353             rc = renamefile(vol, sdir_fd, p, upath, newname, adp );
354             if (rc == AFP_OK)
355                 of_rename(vol, opened, sdir, oldname, curdir, newname);
356         }
357     } else {
358         rc = renamedir(vol, sdir_fd, p, upath, sdir, curdir, newname);
359     }
360     if ( rc == AFP_OK && id ) {
361         /* renaming may have moved the file/dir across a filesystem */
362         if (stat(upath, st) < 0) {
363             rc = AFPERR_MISC;
364             goto exit;
365         }
366
367         /* Remove it from the cache */
368         struct dir *cacheddir = dircache_search_by_did(vol, id);
369         if (cacheddir) {
370             LOG(log_warning, logtype_afpd,"Still cached: \"%s/%s\"", getcwdpath(), upath);
371             (void)dir_remove(vol, cacheddir);
372         }
373
374         /* fix up the catalog entry */
375         cnid_update(vol->v_cdb, id, st, curdir->d_did, upath, strlen(upath));
376     }
377
378 exit:
379     if (cwd_fd != -1)
380         close(cwd_fd);
381     return rc;
382 }
383
384 /* -------------------------------------------- */
385 int afp_rename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
386 {
387     struct vol  *vol;
388     struct dir  *sdir;
389     char        *oldname, *newname;
390     struct path *path;
391     u_int32_t   did;
392     int         plen;
393     u_int16_t   vid;
394     int         isdir = 0;
395     int         rc;
396
397     *rbuflen = 0;
398     ibuf += 2;
399
400     memcpy( &vid, ibuf, sizeof( vid ));
401     ibuf += sizeof( vid );
402     if (NULL == ( vol = getvolbyvid( vid )) ) {
403         return( AFPERR_PARAM );
404     }
405
406     if (vol->v_flags & AFPVOL_RO)
407         return AFPERR_VLOCK;
408
409     memcpy( &did, ibuf, sizeof( did ));
410     ibuf += sizeof( did );
411     if (NULL == ( sdir = dirlookup( vol, did )) ) {
412         return afp_errno;
413     }
414
415     /* source pathname */
416     if (NULL == ( path = cname( vol, sdir, &ibuf )) ) {
417         return get_afp_errno(AFPERR_NOOBJ);
418     }
419
420     sdir = curdir;
421     newname = obj->newtmp;
422     oldname = obj->oldtmp;
423     isdir = path_isadir(path);
424     if ( *path->m_name != '\0' ) {
425         strcpy(oldname, path->m_name); /* an extra copy for of_rename */
426         if (isdir) {
427             /* curdir parent dir, need to move sdir back */
428             sdir = path->d_dir;
429         }
430     }
431     else {
432         if ( sdir->d_did == DIRDID_ROOT ) { /* root directory */
433             return( AFPERR_NORENAME );
434         }
435         /* move to destination dir */
436         if ( movecwd( vol, dirlookup(vol, sdir->d_pdid) ) < 0 ) {
437             return afp_errno;
438         }
439         memcpy(oldname, cfrombstr(sdir->d_m_name), blength(sdir->d_m_name) +1);
440     }
441
442     /* another place where we know about the path type */
443     if ((plen = copy_path_name(vol, newname, ibuf)) < 0) {
444         return( AFPERR_PARAM );
445     }
446
447     if (!plen) {
448         return AFP_OK; /* newname == oldname same dir */
449     }
450     
451     rc = moveandrename(vol, sdir, -1, oldname, newname, isdir);
452     if ( rc == AFP_OK ) {
453         setvoltime(obj, vol );
454     }
455
456     return( rc );
457 }
458
459 /* ------------------------------- */
460 int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
461 {
462     struct vol      *vol;
463     struct dir      *dir;
464     struct path         *s_path;
465     char        *upath;
466     int         did, rc;
467     u_int16_t       vid;
468
469     *rbuflen = 0;
470     ibuf += 2;
471
472     memcpy( &vid, ibuf, sizeof( vid ));
473     ibuf += sizeof( vid );
474     if (NULL == ( vol = getvolbyvid( vid )) ) {
475         return( AFPERR_PARAM );
476     }
477
478     if (vol->v_flags & AFPVOL_RO)
479         return AFPERR_VLOCK;
480
481     memcpy( &did, ibuf, sizeof( did ));
482     ibuf += sizeof( int );
483
484     if (NULL == ( dir = dirlookup( vol, did )) ) {
485         return afp_errno;
486     }
487
488     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
489         return get_afp_errno(AFPERR_NOOBJ);
490     }
491
492     upath = s_path->u_name;
493     if ( path_isadir( s_path) ) {
494         if (*s_path->m_name != '\0' || curdir->d_did == DIRDID_ROOT)
495             rc = AFPERR_ACCESS;
496         else
497             rc = deletecurdir( vol);
498     } else if (of_findname(s_path)) {
499         rc = AFPERR_BUSY;
500     } else {
501         /* it's a file st_valid should always be true
502          * only test for ENOENT because EACCES needs
503          * to read meta data in deletefile
504          */
505         if (s_path->st_valid && s_path->st_errno == ENOENT) {
506             rc = AFPERR_NOOBJ;
507         }
508         else {
509             rc = deletefile(vol, -1, upath, 1);
510
511             struct dir *cachedfile;
512             if ((cachedfile = dircache_search_by_name(vol, dir, upath, strlen(upath), s_path->st.st_ctime))) {
513                 dircache_remove(vol, cachedfile, DIRCACHE | DIDNAME_INDEX | QUEUE_INDEX);
514                 dir_free(cachedfile);
515             }
516         }
517     }
518     if ( rc == AFP_OK ) {
519         curdir->offcnt--;
520         setvoltime(obj, vol );
521     }
522
523     return( rc );
524 }
525 /* ------------------------ */
526 char *absupath(const struct vol *vol, struct dir *dir, char *u)
527 {
528     static char pathbuf[MAXPATHLEN + 1];
529     bstring path;
530
531     if (u == NULL || dir == NULL || vol == NULL)
532         return NULL;
533
534     if ((path = bstrcpy(dir->d_fullpath)) == NULL)
535         return NULL;
536     if (bcatcstr(path, "/") != BSTR_OK)
537         return NULL;
538     if (bcatcstr(path, u) != BSTR_OK)
539         return NULL;
540     if (path->slen > MAXPATHLEN)
541         return NULL;
542
543     LOG(log_debug, logtype_afpd, "absupath: %s", cfrombstr(path));
544
545     strncpy(pathbuf, cfrombstr(path), blength(path) + 1);
546     bdestroy(path);
547
548     return(pathbuf);
549 }
550
551 char *ctoupath(const struct vol *vol, struct dir *dir, char *name)
552 {
553     if (vol == NULL || dir == NULL || name == NULL)
554         return NULL;
555     return absupath(vol, dir, mtoupath(vol, name, dir->d_did, utf8_encoding()));
556 }
557
558 /* ------------------------- */
559 int afp_moveandrename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
560 {
561     struct vol  *vol;
562     struct dir  *sdir, *ddir;
563     int         isdir;
564     char    *oldname, *newname;
565     struct path *path;
566     int     did;
567     int     pdid;
568     int         plen;
569     u_int16_t   vid;
570     int         rc;
571     int     sdir_fd = -1;
572
573
574     *rbuflen = 0;
575     ibuf += 2;
576
577     memcpy( &vid, ibuf, sizeof( vid ));
578     ibuf += sizeof( vid );
579     if (NULL == ( vol = getvolbyvid( vid )) ) {
580         return( AFPERR_PARAM );
581     }
582
583     if (vol->v_flags & AFPVOL_RO)
584         return AFPERR_VLOCK;
585
586     /* source did followed by dest did */
587     memcpy( &did, ibuf, sizeof( did ));
588     ibuf += sizeof( int );
589     if (NULL == ( sdir = dirlookup( vol, did )) ) {
590         return afp_errno; /* was AFPERR_PARAM */
591     }
592
593     memcpy( &did, ibuf, sizeof( did ));
594     ibuf += sizeof( int );
595
596     /* source pathname */
597     if (NULL == ( path = cname( vol, sdir, &ibuf )) ) {
598         return get_afp_errno(AFPERR_NOOBJ);
599     }
600
601     sdir = curdir;
602     newname = obj->newtmp;
603     oldname = obj->oldtmp;
604
605     isdir = path_isadir(path);
606     if ( *path->m_name != '\0' ) {
607         if (isdir) {
608             sdir = path->d_dir;
609         }
610         strcpy(oldname, path->m_name); /* an extra copy for of_rename */
611     } else {
612         memcpy(oldname, cfrombstr(sdir->d_m_name), blength(sdir->d_m_name) + 1);
613     }
614
615 #ifdef HAVE_RENAMEAT
616     if ((sdir_fd = open(".", O_RDONLY)) == -1)
617         return AFPERR_MISC;
618 #endif
619
620     /* get the destination directory */
621     if (NULL == ( ddir = dirlookup( vol, did )) ) {
622         rc = afp_errno; /*  was AFPERR_PARAM */
623         goto exit;
624     }
625     if (NULL == ( path = cname( vol, ddir, &ibuf ))) {
626         rc = AFPERR_NOOBJ;
627         goto exit;
628     }
629     pdid = curdir->d_did;
630     if ( *path->m_name != '\0' ) {
631         rc = path_error(path, AFPERR_NOOBJ);
632         goto exit;
633     }
634
635     /* one more place where we know about path type */
636     if ((plen = copy_path_name(vol, newname, ibuf)) < 0) {
637         rc = AFPERR_PARAM;
638         goto exit;
639     }
640
641     if (!plen) {
642         strcpy(newname, oldname);
643     }
644
645     /* This does the work */
646     LOG(log_debug, logtype_afpd, "afp_move(oldname:'%s', newname:'%s', isdir:%u)",
647         oldname, newname, isdir);
648     rc = moveandrename(vol, sdir, sdir_fd, oldname, newname, isdir);
649
650     if ( rc == AFP_OK ) {
651         char *upath = mtoupath(vol, newname, pdid, utf8_encoding());
652
653         if (NULL == upath) {
654             rc = AFPERR_PARAM;
655             goto exit;
656         }
657         curdir->offcnt++;
658         sdir->offcnt--;
659         /* if unix priv don't try to match perm with dest folder */
660         if (!isdir && !vol_unix_priv(vol)) {
661             int  admode = ad_mode("", 0777) | vol->v_fperm;
662
663             setfilmode(upath, admode, NULL, vol->v_umask);
664             vol->vfs->vfs_setfilmode(vol, upath, admode, NULL);
665         }
666         setvoltime(obj, vol );
667     }
668
669 exit:
670 #ifdef HAVE_RENAMEAT
671     if (sdir_fd != -1)
672         close(sdir_fd);
673 #endif
674
675     return( rc );
676 }
677
678 int veto_file(const char*veto_str, const char*path)
679 /* given a veto_str like "abc/zxc/" and path "abc", return 1
680  * veto_str should be '/' delimited
681  * if path matches any one of the veto_str elements exactly, then 1 is returned
682  * otherwise, 0 is returned.
683  */
684 {
685     int i;  /* index to veto_str */
686     int j;  /* index to path */
687
688     if ((veto_str == NULL) || (path == NULL))
689         return 0;
690
691     for(i=0, j=0; veto_str[i] != '\0'; i++) {
692         if (veto_str[i] == '/') {
693             if ((j>0) && (path[j] == '\0')) {
694                 LOG(log_debug, logtype_afpd, "vetoed file:'%s'", path);
695                 return 1;
696             }
697             j = 0;
698         } else {
699             if (veto_str[i] != path[j]) {
700                 while ((veto_str[i] != '/')
701                        && (veto_str[i] != '\0'))
702                     i++;
703                 j = 0;
704                 continue;
705             }
706             j++;
707         }
708     }
709     return 0;
710 }
711