2 * Copyright (c) 1990,1993 Regents of The University of Michigan.
3 * All Rights Reserved. See COPYRIGHT.
8 #endif /* HAVE_CONFIG_H */
17 #include <sys/param.h>
19 #include <atalk/adouble.h>
20 #include <atalk/vfs.h>
21 #include <atalk/afp.h>
22 #include <atalk/util.h>
23 #include <atalk/cnid.h>
24 #include <atalk/logger.h>
25 #include <atalk/unix.h>
26 #include <atalk/bstrlib.h>
27 #include <atalk/bstradd.h>
28 #include <atalk/acl.h>
30 #include "directory.h"
41 int matchfile2dirperms(
42 /* Since it's kinda' big; I decided against an
47 /* The below code changes the way file ownership is determined in the name of
48 fixing dropboxes. It has known security problem. See the netatalk FAQ for
57 LOG(log_debug9, logtype_afpd, "begin matchfile2dirperms:");
60 if (stat(upath, &st ) < 0) {
61 LOG(log_error, logtype_afpd, "Could not stat %s: %s", upath, strerror(errno));
65 adpath = vol->vfs->ad_path( upath, ADFLAGS_HF );
66 /* FIXME dirsearch doesn't move cwd to did ! */
67 if (( dir = dirlookup( vol, did )) == NULL ) {
68 LOG(log_error, logtype_afpd, "matchfile2dirperms: Unable to get directory info.");
71 else if (stat(".", &sb) < 0) {
72 LOG(log_error, logtype_afpd,
73 "matchfile2dirperms: Error checking directory \"%s\": %s",
74 dir->d_m_name, strerror(errno));
79 if ( uid != sb.st_uid )
82 if (lchown(upath, sb.st_uid, sb.st_gid) < 0)
84 LOG(log_error, logtype_afpd,
85 "matchfile2dirperms(%s): Error changing owner/gid: %s",
86 upath, strerror(errno));
89 else if ((!S_ISLNK(st->st_mode)) && (chmod(upath,(st.st_mode&~default_options.umask)| S_IRGRP| S_IROTH) < 0))
91 LOG(log_error, logtype_afpd,
92 "matchfile2dirperms(%s): Error adding file read permissions: %s",
93 upath, strerror(errno));
96 else if (lchown(adpath, sb.st_uid, sb.st_gid) < 0)
98 LOG(log_error, logtype_afpd,
99 "matchfile2dirperms(%s): Error changing AppleDouble owner/gid: %s",
100 adpath, strerror(errno));
103 else if (chmod(adpath, (st.st_mode&~default_options.umask)| S_IRGRP| S_IROTH) < 0)
105 LOG(log_error, logtype_afpd,
106 "matchfile2dirperms(%s): Error adding AD file read permissions: %s",
107 adpath, strerror(errno));
112 } /* end else if stat success */
115 LOG(log_debug9, logtype_afpd, "end matchfile2dirperms:");
121 int afp_getfildirparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
129 u_int16_t fbitmap, dbitmap, vid;
135 memcpy( &vid, ibuf, sizeof( vid ));
136 ibuf += sizeof( vid );
137 if (NULL == ( vol = getvolbyvid( vid )) ) {
138 /* was AFPERR_PARAM but it helps OS 10.3 when a volume has been removed
141 return( AFPERR_ACCESS );
144 memcpy( &did, ibuf, sizeof( did ));
145 ibuf += sizeof( did );
147 if (NULL == ( dir = dirlookup( vol, did )) ) {
151 memcpy( &fbitmap, ibuf, sizeof( fbitmap ));
152 fbitmap = ntohs( fbitmap );
153 ibuf += sizeof( fbitmap );
154 memcpy( &dbitmap, ibuf, sizeof( dbitmap ));
155 dbitmap = ntohs( dbitmap );
156 ibuf += sizeof( dbitmap );
158 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
159 return get_afp_errno(AFPERR_NOOBJ);
162 LOG(log_debug, logtype_afpd, "getfildirparams(vid:%u, did:%u, f/d:%04x/%04x) {cwdid:%u, cwd: %s, name:'%s'}",
163 ntohs(vid), ntohl(dir->d_did), fbitmap, dbitmap,
164 ntohl(curdir->d_did), cfrombstr(curdir->d_fullpath), s_path->u_name);
167 if (!s_path->st_valid) {
168 /* it's a dir and it should be there
169 * because we chdir in it in cname or
170 * it's curdir (maybe deleted, but then we can't know).
171 * So we need to try harder.
173 of_statdir(vol, s_path);
175 if ( s_path->st_errno != 0 ) {
176 if (afp_errno != AFPERR_ACCESS) {
177 return( AFPERR_NOOBJ );
183 if (S_ISDIR(st->st_mode)) {
189 ret = getdirparams(vol, dbitmap, s_path, dir,
190 rbuf + 3 * sizeof( u_int16_t ), &buflen );
194 /* this is a directory */
195 *(rbuf + 2 * sizeof( u_int16_t )) = (char) FILDIRBIT_ISDIR;
197 if (fbitmap && AFP_OK != (ret = getfilparams(vol, fbitmap, s_path, curdir,
198 rbuf + 3 * sizeof( u_int16_t ), &buflen )) ) {
202 *(rbuf + 2 * sizeof( u_int16_t )) = FILDIRBIT_ISFILE;
204 *rbuflen = buflen + 3 * sizeof( u_int16_t );
205 fbitmap = htons( fbitmap );
206 memcpy( rbuf, &fbitmap, sizeof( fbitmap ));
207 rbuf += sizeof( fbitmap );
208 dbitmap = htons( dbitmap );
209 memcpy( rbuf, &dbitmap, sizeof( dbitmap ));
210 rbuf += sizeof( dbitmap ) + sizeof( u_char );
216 int afp_setfildirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
222 u_int16_t vid, bitmap;
227 memcpy( &vid, ibuf, sizeof(vid));
228 ibuf += sizeof( vid );
230 if (NULL == ( vol = getvolbyvid( vid )) ) {
231 return( AFPERR_PARAM );
234 if (vol->v_flags & AFPVOL_RO)
237 memcpy( &did, ibuf, sizeof( did));
238 ibuf += sizeof( did);
240 if (NULL == ( dir = dirlookup( vol, did )) ) {
244 memcpy( &bitmap, ibuf, sizeof( bitmap ));
245 bitmap = ntohs( bitmap );
246 ibuf += sizeof( bitmap );
248 if (NULL == ( path = cname( vol, dir, &ibuf ))) {
249 return get_afp_errno(AFPERR_NOOBJ);
253 if (!path->st_valid) {
254 /* it's a dir and it should be there
255 * because we chdir in it in cname
257 of_statdir(vol, path);
260 if ( path->st_errno != 0 ) {
261 if (afp_errno != AFPERR_ACCESS)
262 return( AFPERR_NOOBJ );
265 * If ibuf is odd, make it even.
267 if ((u_long)ibuf & 1 ) {
271 if (S_ISDIR(st->st_mode)) {
272 rc = setdirparams(vol, path, bitmap, ibuf );
274 rc = setfilparams(vol, path, bitmap, ibuf );
276 if ( rc == AFP_OK ) {
277 setvoltime(obj, vol );
283 /* --------------------------------------------
284 Factorise some checks on a pathname
286 int check_name(const struct vol *vol, char *name)
288 /* check for illegal characters in the unix filename */
289 if (!wincheck(vol, name))
292 if ((vol->v_flags & AFPVOL_NOHEX) && strchr(name, '/'))
295 if (!vol->vfs->vfs_validupath(vol, name)) {
296 LOG(log_error, logtype_afpd, "check_name: illegal name: '%s'", name);
300 /* check for vetoed filenames */
301 if (veto_file(vol->v_veto, name))
306 /* -------------------------
307 move and rename sdir:oldname to curdir:newname in volume vol
308 special care is needed for lock
310 static int moveandrename(const struct vol *vol,
320 struct stat *st, nst;
324 struct ofork *opened = NULL;
329 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
334 if ((p = mtoupath(vol, oldname, sdir->d_did, utf8_encoding())) == NULL)
335 return AFPERR_PARAM; /* can't convert */
337 #ifndef HAVE_RENAMEAT
339 id = cnid_get(vol->v_cdb, sdir->d_did, p, strlen(p));
340 p = ctoupath( vol, sdir, oldname );
342 return AFPERR_PARAM; /* pathname too long */
343 #endif /* HAVE_RENAMEAT */
348 opened = of_findnameat(sdir_fd, &path);
350 opened = of_findname(&path);
351 #endif /* HAVE_RENAMEAT */
353 /* reuse struct adouble so it won't break locks */
357 id = sdir->d_did; /* we already have the CNID */
358 p = ctoupath( vol, dirlookup(vol, sdir->d_pdid), oldname );
362 adflags = ADFLAGS_DIR;
366 * p now points to either
367 * a) full pathname of the source fs object (if renameat is not available)
368 * b) the oldname (renameat is available)
369 * we are in the dest folder so we need to use
371 * b) fchdir sdir_fd before eg ad_open or use *at functions where appropiate
375 if ((cwd_fd = open(".", O_RDONLY)) == -1)
377 if (fchdir(sdir_fd) != 0) {
382 if (!ad_metadata(p, adflags, adp)) {
385 ad_getattr(adp, &bshort);
386 ad_close_metadata( adp);
387 if ((bshort & htons(ATTRBIT_NORENAME))) {
393 if (fchdir(cwd_fd) != 0) {
394 LOG(log_error, logtype_afpd, "moveandrename: %s", strerror(errno) );
400 if (NULL == (upath = mtoupath(vol, newname, curdir->d_did, utf8_encoding()))){
406 if (0 != (rc = check_name(vol, upath))) {
410 /* source == destination. we just silently accept this. */
411 if ((!isdir && curdir == sdir) || (isdir && curdir->d_did == sdir->d_pdid)) {
412 if (strcmp(oldname, newname) == 0) {
417 if (stat(upath, st) == 0 || caseenumerate(vol, &path, curdir) == 0) {
418 if (!stat(p, &nst) && !(nst.st_dev == st->st_dev && nst.st_ino == st->st_ino) ) {
419 /* not the same file */
425 } else if (stat(upath, st ) == 0 || caseenumerate(vol, &path, curdir) == 0) {
432 path.st_errno = errno;
433 if (of_findname(&path)) {
434 rc = AFPERR_EXIST; /* was AFPERR_BUSY; */
436 rc = renamefile(vol, sdir_fd, p, upath, newname, adp );
438 of_rename(vol, opened, sdir, oldname, curdir, newname);
441 rc = renamedir(vol, sdir_fd, p, upath, sdir, curdir, newname);
443 if ( rc == AFP_OK && id ) {
444 /* renaming may have moved the file/dir across a filesystem */
445 if (stat(upath, st) < 0) {
450 /* Remove it from the cache */
451 struct dir *cacheddir = dircache_search_by_did(vol, id);
453 LOG(log_warning, logtype_afpd,"Still cached: \"%s/%s\"", getcwdpath(), upath);
454 (void)dir_remove(vol, cacheddir);
457 /* fix up the catalog entry */
458 cnid_update(vol->v_cdb, id, st, curdir->d_did, upath, strlen(upath));
467 /* -------------------------------------------- */
468 int afp_rename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
472 char *oldname, *newname;
483 memcpy( &vid, ibuf, sizeof( vid ));
484 ibuf += sizeof( vid );
485 if (NULL == ( vol = getvolbyvid( vid )) ) {
486 return( AFPERR_PARAM );
489 if (vol->v_flags & AFPVOL_RO)
492 memcpy( &did, ibuf, sizeof( did ));
493 ibuf += sizeof( did );
494 if (NULL == ( sdir = dirlookup( vol, did )) ) {
498 /* source pathname */
499 if (NULL == ( path = cname( vol, sdir, &ibuf )) ) {
500 return get_afp_errno(AFPERR_NOOBJ);
504 newname = obj->newtmp;
505 oldname = obj->oldtmp;
506 isdir = path_isadir(path);
507 if ( *path->m_name != '\0' ) {
508 strcpy(oldname, path->m_name); /* an extra copy for of_rename */
510 /* curdir parent dir, need to move sdir back */
515 if ( sdir->d_did == DIRDID_ROOT ) { /* root directory */
516 return( AFPERR_NORENAME );
518 /* move to destination dir */
519 if ( movecwd( vol, dirlookup(vol, sdir->d_pdid) ) < 0 ) {
522 memcpy(oldname, cfrombstr(sdir->d_m_name), blength(sdir->d_m_name) +1);
525 /* another place where we know about the path type */
526 if ((plen = copy_path_name(vol, newname, ibuf)) < 0) {
527 return( AFPERR_PARAM );
531 return AFP_OK; /* newname == oldname same dir */
534 rc = moveandrename(vol, sdir, -1, oldname, newname, isdir);
535 if ( rc == AFP_OK ) {
536 setvoltime(obj, vol );
542 /* ------------------------------- */
543 int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
555 memcpy( &vid, ibuf, sizeof( vid ));
556 ibuf += sizeof( vid );
557 if (NULL == ( vol = getvolbyvid( vid )) ) {
558 return( AFPERR_PARAM );
561 if (vol->v_flags & AFPVOL_RO)
564 memcpy( &did, ibuf, sizeof( did ));
565 ibuf += sizeof( int );
567 if (NULL == ( dir = dirlookup( vol, did )) ) {
571 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
572 return get_afp_errno(AFPERR_NOOBJ);
575 upath = s_path->u_name;
576 if ( path_isadir( s_path) ) {
577 if (*s_path->m_name != '\0' || curdir->d_did == DIRDID_ROOT)
580 rc = deletecurdir( vol);
581 } else if (of_findname(s_path)) {
584 /* it's a file st_valid should always be true
585 * only test for ENOENT because EACCES needs
586 * to read meta data in deletefile
588 if (s_path->st_valid && s_path->st_errno == ENOENT) {
592 rc = deletefile(vol, -1, upath, 1);
594 struct dir *cachedfile;
595 if ((cachedfile = dircache_search_by_name(vol, dir, upath, strlen(upath), s_path->st.st_ctime))) {
596 dircache_remove(vol, cachedfile, DIRCACHE | DIDNAME_INDEX | QUEUE_INDEX);
597 dir_free(cachedfile);
601 if ( rc == AFP_OK ) {
603 setvoltime(obj, vol );
608 /* ------------------------ */
609 char *absupath(const struct vol *vol, struct dir *dir, char *u)
611 static char pathbuf[MAXPATHLEN + 1];
614 if (u == NULL || dir == NULL || vol == NULL)
617 if ((path = bstrcpy(dir->d_fullpath)) == NULL)
619 if (bcatcstr(path, "/") != BSTR_OK)
621 if (bcatcstr(path, u) != BSTR_OK)
623 if (path->slen > MAXPATHLEN)
626 LOG(log_debug, logtype_afpd, "absupath: %s", cfrombstr(path));
628 strncpy(pathbuf, cfrombstr(path), blength(path) + 1);
634 char *ctoupath(const struct vol *vol, struct dir *dir, char *name)
636 if (vol == NULL || dir == NULL || name == NULL)
638 return absupath(vol, dir, mtoupath(vol, name, dir->d_did, utf8_encoding()));
641 /* ------------------------- */
642 int afp_moveandrename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
645 struct dir *sdir, *ddir;
647 char *oldname, *newname;
656 #endif /* DROPKLUDGE */
663 memcpy( &vid, ibuf, sizeof( vid ));
664 ibuf += sizeof( vid );
665 if (NULL == ( vol = getvolbyvid( vid )) ) {
666 return( AFPERR_PARAM );
669 if (vol->v_flags & AFPVOL_RO)
672 /* source did followed by dest did */
673 memcpy( &did, ibuf, sizeof( did ));
674 ibuf += sizeof( int );
675 if (NULL == ( sdir = dirlookup( vol, did )) ) {
676 return afp_errno; /* was AFPERR_PARAM */
679 memcpy( &did, ibuf, sizeof( did ));
680 ibuf += sizeof( int );
682 /* source pathname */
683 if (NULL == ( path = cname( vol, sdir, &ibuf )) ) {
684 return get_afp_errno(AFPERR_NOOBJ);
688 newname = obj->newtmp;
689 oldname = obj->oldtmp;
691 isdir = path_isadir(path);
692 if ( *path->m_name != '\0' ) {
696 strcpy(oldname, path->m_name); /* an extra copy for of_rename */
698 memcpy(oldname, cfrombstr(sdir->d_m_name), blength(sdir->d_m_name) + 1);
702 if ((sdir_fd = open(".", O_RDONLY)) == -1)
706 /* get the destination directory */
707 if (NULL == ( ddir = dirlookup( vol, did )) ) {
708 rc = afp_errno; /* was AFPERR_PARAM */
711 if (NULL == ( path = cname( vol, ddir, &ibuf ))) {
715 pdid = curdir->d_did;
716 if ( *path->m_name != '\0' ) {
717 rc = path_error(path, AFPERR_NOOBJ);
721 /* one more place where we know about path type */
722 if ((plen = copy_path_name(vol, newname, ibuf)) < 0) {
728 strcpy(newname, oldname);
731 /* This does the work */
732 LOG(log_debug, logtype_afpd, "afp_move(oldname:'%s', newname:'%s', isdir:%u)",
733 oldname, newname, isdir);
734 rc = moveandrename(vol, sdir, sdir_fd, oldname, newname, isdir);
736 if ( rc == AFP_OK ) {
737 char *upath = mtoupath(vol, newname, pdid, utf8_encoding());
746 if (vol->v_flags & AFPVOL_DROPBOX) {
747 /* FIXME did is not always the source id */
748 if ((retvalue=matchfile2dirperms (upath, vol, did)) != AFP_OK) {
754 #endif /* DROPKLUDGE */
755 /* if unix priv don't try to match perm with dest folder */
756 if (!isdir && !vol_unix_priv(vol)) {
757 int admode = ad_mode("", 0777) | vol->v_fperm;
759 setfilmode(upath, admode, NULL, vol->v_umask);
760 vol->vfs->vfs_setfilmode(vol, upath, admode, NULL);
762 setvoltime(obj, vol );
774 int veto_file(const char*veto_str, const char*path)
775 /* given a veto_str like "abc/zxc/" and path "abc", return 1
776 * veto_str should be '/' delimited
777 * if path matches any one of the veto_str elements exactly, then 1 is returned
778 * otherwise, 0 is returned.
781 int i; /* index to veto_str */
782 int j; /* index to path */
784 if ((veto_str == NULL) || (path == NULL))
787 for(i=0, j=0; veto_str[i] != '\0'; i++) {
788 if (veto_str[i] == '/') {
789 if ((j>0) && (path[j] == '\0')) {
790 LOG(log_debug, logtype_afpd, "vetoed file:'%s'", path);
795 if (veto_str[i] != path[j]) {
796 while ((veto_str[i] != '/')
797 && (veto_str[i] != '\0'))