]> arthur.barton.de Git - netatalk.git/blobdiff - etc/afpd/directory.c
Merge symlink branch
[netatalk.git] / etc / afpd / directory.c
index 0ffdf698eaf60aad9211eb266b07c73598ff99a7..f330d3ca910fa77c9260eb7898dfd0bc2d2019e0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: directory.c,v 1.128 2010-01-18 11:45:37 franklahm Exp $
+ * $Id: directory.c,v 1.132 2010-02-10 14:05:37 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -133,6 +133,76 @@ static struct dir rootpar  = { SENTINEL, SENTINEL, NULL,
  * frPutAway:   4    home directory ID
  */
 
+/*!
+ * @brief symlink safe chdir replacement
+ *
+ * Only chdirs to dir if it doesn't contain symlinks.
+ *
+ * @returns 1 if a path element is a symlink, 0 otherwise, -1 on syserror
+ */
+static int lchdir(const char *dir)
+{
+    int ret = 0;
+    char buf[MAXPATHLEN+1];
+#ifdef REALPATH_TAKES_NULL
+    char *rpath = NULL;
+#else
+    char rpath[MAXPATHLEN+1];
+#endif
+
+    /* dir might be an relative or an absolute path */
+    if (dir[0] == '/') {
+        /* absolute path, just make sure buf is prepared for strlcat */
+        buf[0] = 0;
+    } else {
+        /* relative path, push cwd int buf */
+        if (getcwd(buf, MAXPATHLEN) == NULL)
+            return -1;
+        if (strlcat(buf, "/", MAXPATHLEN) >= MAXPATHLEN)
+            return -1;
+    }
+
+    if (strlcat(buf, dir, MAXPATHLEN) >= MAXPATHLEN)
+        return -1;
+
+#ifdef REALPATH_TAKES_NULL
+    if ((rpath = realpath(dir, NULL)) == NULL) {
+#else
+    if (realpath(dir, rpath) == NULL) {
+#endif
+        ret = -1;
+        goto exit;
+    }
+
+    /* 
+     * Cases:
+     * chdir request   | realpath result | ret
+     * (after getwcwd) |                 |
+     * =======================================
+     * /a/b/.          | /a/b            | 0
+     * /a/b/.          | /c              | 1
+     * /a/b/.          | /c/d/e/f        | 1
+     */
+    ret = 0;
+    for (int i = 0; rpath[i]; i++) {
+        if (buf[i] != rpath[i]) {
+            ret = 1;
+            goto exit;
+        }
+    }
+
+    if (chdir(dir) != 0) {
+        ret = -1;
+        goto exit;
+    }
+
+exit:
+#ifdef REALPATH_TAKES_NULL
+    free(rpath);
+#endif
+    return ret;
+}
+
 static struct dir *
 vol_tree_root(const struct vol *vol, u_int32_t did)
 {
@@ -818,7 +888,7 @@ static int deletedir(char *dir)
             break;
         }
         strcpy(path + len, de->d_name);
-        if (stat(path, &st)) {
+        if (lstat(path, &st)) {
             continue;
         }
         if (S_ISDIR(st.st_mode)) {
@@ -884,7 +954,7 @@ static int copydir(const struct vol *vol, char *src, char *dst)
         }
         strcpy(spath + slen, de->d_name);
 
-        if (stat(spath, &st) == 0) {
+        if (lstat(spath, &st) == 0) {
             if (strlen(de->d_name) > drem) {
                 err = AFPERR_PARAM;
                 break;
@@ -906,7 +976,7 @@ static int copydir(const struct vol *vol, char *src, char *dst)
     }
 
     /* keep the same time stamp. */
-    if (stat(src, &st) == 0) {
+    if (lstat(src, &st) == 0) {
         ut.actime = ut.modtime = st.st_mtime;
         utime(dst, &ut);
     }
@@ -986,13 +1056,24 @@ adddir(struct vol *vol, struct dir *dir, struct path *path)
     char        *upath;
     struct stat *st;
     int         deleted;
+    struct adouble  ad;
+    struct adouble *adp = NULL;
     cnid_t      id;
 
     upath = path->u_name;
     st    = &path->st;
     upathlen = strlen(upath);
 
-    id = get_id(vol, NULL, st, dir->d_did, upath, upathlen);
+    /* get_id needs adp for reading CNID from adouble file */
+    ad_init(&ad, vol->v_adouble, vol->v_ad_options);
+    if ((ad_open_metadata(upath, ADFLAGS_DIR, 0, &ad)) == 0)
+        adp = &ad;
+
+    id = get_id(vol, adp, st, dir->d_did, upath, upathlen);
+
+    if (adp)
+        ad_close_metadata(adp);
+
     if (id == 0) {
         return NULL;
     }
@@ -1531,6 +1612,7 @@ int movecwd(struct vol *vol, struct dir *dir)
     struct dir  *d;
     char    *p, *u;
     int     n;
+    int     ret;
 
     if ( dir == curdir ) {
         return( 0 );
@@ -1569,7 +1651,14 @@ int movecwd(struct vol *vol, struct dir *dir)
         p -= n;
         memcpy( p, vol->v_path, n );
     }
-    if ( chdir( p ) < 0 ) {
+    if ( (ret = lchdir( p )) != 0 ) {
+        LOG(log_debug, logtype_afpd, "movecwd('%s'): ret:%d, %u/%s", p, ret, errno, strerror(errno));
+
+        if (ret == 1) {
+            /* p is a symlink */
+            afp_errno = AFPERR_BADTYPE;
+            return -1;
+        }
         switch (errno) {
         case EACCES:
         case EPERM:
@@ -2014,7 +2103,7 @@ int setdirparams(struct vol *vol,
     int         bit, isad = 1;
     int                 cdate, bdate;
     int                 owner, group;
-    u_int16_t       ashort, bshort;
+    u_int16_t       ashort, bshort, oshort;
     int                 err = AFP_OK;
     int                 change_mdate = 0;
     int                 change_parent_mdate = 0;
@@ -2170,14 +2259,14 @@ int setdirparams(struct vol *vol,
         case DIRPBIT_ATTR :
             if (isad) {
                 ad_getattr(&ad, &bshort);
-                if ((bshort & htons(ATTRBIT_INVISIBLE)) !=
-                    (ashort & htons(ATTRBIT_INVISIBLE) & htons(ATTRBIT_SETCLR)) )
-                    change_parent_mdate = 1;
+                oshort = bshort;
                 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
                     bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
                 } else {
                     bshort &= ~ashort;
                 }
+                if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
+                    change_parent_mdate = 1;
                 ad_setattr(&ad, bshort);
             }
             break;
@@ -2667,11 +2756,11 @@ int deletecurdir(struct vol *vol)
         goto delete_done;
     }
 
-    if ( !(err = netatalk_rmdir(fdir->d_u_name))) {
+    err = netatalk_rmdir_all_errors(fdir->d_u_name);
+    if ( err ==  AFP_OK || err == AFPERR_NOOBJ) {
         dirchildremove(curdir, fdir);
         cnid_delete(vol->v_cdb, fdir->d_did);
         dir_remove( vol, fdir );
-        err = AFP_OK;
     }
 delete_done:
     if (dp) {