]> arthur.barton.de Git - netatalk.git/blobdiff - etc/afpd/directory.c
Check for cached files in dirlookup
[netatalk.git] / etc / afpd / directory.c
index 7439c2bfe3b703af93a0061ec4a62737020e801b..b658ad1d56995528333101dbc032bb41c3f00fe5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: directory.c,v 1.131.2.11 2010-02-05 12:56:13 franklahm Exp $
+ * $Id: directory.c,v 1.140 2010/03/12 15:16:49 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -43,7 +43,7 @@
 #include "mangle.h"
 #include "hash.h"
 
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
 extern void addir_inherit_acl(const struct vol *vol);
 #endif
 
@@ -51,9 +51,6 @@ extern void addir_inherit_acl(const struct vol *vol);
  * FIXMEs, loose ends after the dircache rewrite:
  * o merge dircache_search_by_name and dir_add ??
  * o case-insensitivity is gone from cname
- * o directory offspring count calculation probably broken
- * o doesn't work with CNID backend last and the like,
- *   CNID backend must support persistent CNIDs.
  */
 
 
@@ -73,8 +70,10 @@ struct path Cur_Path = {
     "",  /* mac name */
     ".", /* unix name */
     0,   /* id */
-    NULL,/* struct dir */
+    NULL,/* struct dir * */
     0,   /* stat is not set */
+    0,   /* errno */
+    {0} /* struct stat */
 };
 
 
@@ -86,9 +85,24 @@ struct path Cur_Path = {
 /* -------------------------
    appledouble mkdir afp error code.
 */
-static int netatalk_mkdir(const char *name)
+static int netatalk_mkdir(const struct vol *vol, const char *name)
 {
-    if (ad_mkdir(name, DIRBITS | 0777) < 0) {
+    int ret;
+    struct stat st;
+
+    if (vol->v_flags & AFPVOL_UNIX_PRIV) {
+        if (lstat(".", &st) < 0)
+            return AFPERR_MISC;
+        int mode = (DIRBITS & (~S_ISGID & st.st_mode)) | (0777 & ~vol->v_umask);
+        LOG(log_maxdebug, logtype_afpd, "netatalk_mkdir(\"%s\") {parent mode: %04o, vol umask: %04o}",
+            name, st.st_mode, vol->v_umask);
+
+        ret = mkdir(name, mode);
+    } else {
+        ret = ad_mkdir(name, DIRBITS | 0777);
+    }
+
+    if (ret < 0) {
         switch ( errno ) {
         case ENOENT :
             return( AFPERR_NOOBJ );
@@ -110,7 +124,7 @@ static int netatalk_mkdir(const char *name)
 }
 
 /* ------------------- */
-static int deletedir(char *dir)
+static int deletedir(int dirfd, char *dir)
 {
     char path[MAXPATHLEN + 1];
     DIR *dp;
@@ -124,7 +138,7 @@ static int deletedir(char *dir)
         return AFPERR_PARAM;
 
     /* already gone */
-    if ((dp = opendir(dir)) == NULL)
+    if ((dp = opendirat(dirfd, dir)) == NULL)
         return AFP_OK;
 
     strcpy(path, dir);
@@ -141,13 +155,13 @@ static int deletedir(char *dir)
             break;
         }
         strcpy(path + len, de->d_name);
-        if (stat(path, &st)) {
+        if (lstatat(dirfd, path, &st)) {
             continue;
         }
         if (S_ISDIR(st.st_mode)) {
-            err = deletedir(path);
+            err = deletedir(dirfd, path);
         } else {
-            err = netatalk_unlink(path);
+            err = netatalk_unlinkat(dirfd, path);
         }
     }
     closedir(dp);
@@ -155,13 +169,13 @@ static int deletedir(char *dir)
     /* okay. the directory is empty. delete it. note: we already got rid
        of .AppleDouble.  */
     if (err == AFP_OK) {
-        err = netatalk_rmdir(dir);
+        err = netatalk_rmdir(dirfd, dir);
     }
     return err;
 }
 
 /* do a recursive copy. */
-static int copydir(const struct vol *vol, char *src, char *dst)
+static int copydir(const struct vol *vol, int dirfd, char *src, char *dst)
 {
     char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
     DIR *dp;
@@ -175,11 +189,11 @@ static int copydir(const struct vol *vol, char *src, char *dst)
     /* doesn't exist or the path is too long. */
     if (((slen = strlen(src)) > sizeof(spath) - 2) ||
         ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
-        ((dp = opendir(src)) == NULL))
+        ((dp = opendirat(dirfd, src)) == NULL))
         return AFPERR_PARAM;
 
     /* try to create the destination directory */
-    if (AFP_OK != (err = netatalk_mkdir(dst)) ) {
+    if (AFP_OK != (err = netatalk_mkdir(vol, dst)) ) {
         closedir(dp);
         return err;
     }
@@ -207,7 +221,7 @@ static int copydir(const struct vol *vol, char *src, char *dst)
         }
         strcpy(spath + slen, de->d_name);
 
-        if (stat(spath, &st) == 0) {
+        if (lstatat(dirfd, spath, &st) == 0) {
             if (strlen(de->d_name) > drem) {
                 err = AFPERR_PARAM;
                 break;
@@ -215,9 +229,9 @@ static int copydir(const struct vol *vol, char *src, char *dst)
             strcpy(dpath + dlen, de->d_name);
 
             if (S_ISDIR(st.st_mode)) {
-                if (AFP_OK != (err = copydir(vol, spath, dpath)))
+                if (AFP_OK != (err = copydir(vol, dirfd, spath, dpath)))
                     goto copydir_done;
-            } else if (AFP_OK != (err = copyfile(vol, vol, spath, dpath, NULL, NULL))) {
+            } else if (AFP_OK != (err = copyfile(vol, vol, dirfd, spath, dpath, NULL, NULL))) {
                 goto copydir_done;
 
             } else {
@@ -229,7 +243,7 @@ static int copydir(const struct vol *vol, char *src, char *dst)
     }
 
     /* keep the same time stamp. */
-    if (stat(src, &st) == 0) {
+    if (lstatat(dirfd, src, &st) == 0) {
         ut.actime = ut.modtime = st.st_mtime;
         utime(dst, &ut);
     }
@@ -414,7 +428,8 @@ int get_afp_errno(const int param)
  *
  * Resolve a DID, allocate a struct dir for it
  * 1. Check for special CNIDs 0 (invalid), 1 and 2.
- * 2. Check if the DID is in the cache.
+ * 2a. Check if the DID is in the cache.
+ * 2b. Check if it's really a dir (d_fullpath != NULL) because we cache files too.
  * 3. If it's not in the cache resolve it via the database.
  * 4. Build complete server-side path to the dir.
  * 5. Check if it exists and is a directory.
@@ -432,11 +447,10 @@ int get_afp_errno(const int param)
 struct dir *dirlookup(const struct vol *vol, cnid_t did)
 {
     static char  buffer[12 + MAXPATHLEN + 1];
-    struct bstrList *pathlist = NULL;
-    bstring      fullpath = NULL;
     struct stat  st;
-    struct dir   *ret = NULL;
-    char         *upath, *mpath;
+    struct dir   *ret = NULL, *pdir;
+    bstring      fullpath = NULL;
+    char         *upath = NULL, *mpath;
     cnid_t       cnid, pdid;
     size_t       maxpath;
     int          buflen = 12 + MAXPATHLEN + 1;
@@ -457,7 +471,11 @@ struct dir *dirlookup(const struct vol *vol, cnid_t did)
     }
 
     /* Search the cache */
-    if ((ret = dircache_search_by_did(vol, did)) != NULL) { /* 2 */
+    if ((ret = dircache_search_by_did(vol, did)) != NULL) { /* 2a */
+        if (ret->d_fullpath == NULL) {                      /* 2b */
+            afp_errno = AFPERR_BADTYPE;
+            return NULL;
+        }
         if (lstat(cfrombstring(ret->d_fullpath), &st) != 0) {
             LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {lstat: %s}", ntohl(did), strerror(errno));
             switch (errno) {
@@ -480,61 +498,33 @@ struct dir *dirlookup(const struct vol *vol, cnid_t did)
     utf8 = utf8_encoding();
     maxpath = (utf8) ? MAXPATHLEN - 7 : 255;
 
-    /* Create list for path elements, request 16 list elements for now*/
-    if ((pathlist = bstListCreateMin(16)) == NULL) { /* 4 */
-        LOG(log_error, logtype_afpd, "dirlookup(did: %u): OOM: %s", ntohl(did), strerror(errno));
-        return NULL;
-    }
-
     /* Get it from the database */
     cnid = did;
-    if ( (upath = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen)) == NULL ) { /* 3 */
+    if ( (upath = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen)) == NULL 
+         || (upath = strdup(upath)) == NULL) { /* 3 */
         afp_errno = AFPERR_NOOBJ;
         err = 1;
         goto exit;
     }
     pdid = cnid;
 
-    /* construct path, copy already found uname to path element list*/
-    if ((bstrListPush(pathlist, bfromcstr(upath))) != BSTR_OK) { /* 4 */
-        afp_errno = AFPERR_MISC;
-        err = 1;
-        goto exit;
-    }
-
-    LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {%u, %s}", ntohl(did), ntohl(pdid), upath);
-
-    /* The stuff that follows is for building the full path to the directory */
-
-    /* work upwards until we reach volume root */
-    while (cnid != DIRDID_ROOT) {
-        /* construct path, copy already found uname to path element list*/
-        if ((bstrListPush(pathlist, bfromcstr(upath))) != BSTR_OK) { /* 4 */
-            afp_errno = AFPERR_MISC;
-            err = 1;
-            goto exit;
-        }
-
-        /* next part */
-        if ((upath = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen)) == NULL ) { /* 3 */
-            afp_errno = AFPERR_NOOBJ;
-            err = 1;
-            goto exit;
-        }
-    }
-
-    if ((bstrListPush(pathlist, bfromcstr(vol->v_path))) != BSTR_OK) { /* 4 */
-        afp_errno = AFPERR_MISC;
+    /*
+     * Recurse up the tree, terminates in dirlookup when either
+     * - DIRDID_ROOT is hit
+     * - a cached entry is found
+     */
+    if ((pdir = dirlookup(vol, pdid)) == NULL) {
         err = 1;
         goto exit;
     }
 
-    if ((fullpath = bjoinInv(pathlist, bfromcstr("/"))) == NULL) { /* 4 */
-        afp_errno = AFPERR_MISC;
+    /* build the fullpath */
+    if ((fullpath = bstrcpy(pdir->d_fullpath)) == NULL
+        || bconchar(fullpath, '/') != BSTR_OK
+        || bcatcstr(fullpath, upath) != BSTR_OK) {
         err = 1;
         goto exit;
     }
-    /* Finished building the fullpath */
 
     /* stat it and check if it's a dir */
     LOG(log_debug, logtype_afpd, "dirlookup: {stating %s}", cfrombstring(fullpath));
@@ -586,12 +576,10 @@ struct dir *dirlookup(const struct vol *vol, cnid_t did)
         ntohl(did), ntohl(pdid), cfrombstring(ret->d_fullpath));
 
 exit:
-    if (pathlist)
-        bstrListDestroy(pathlist);
-
     if (err) {
         LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {exit_error: %s}",
             ntohl(did), AfpErr2name(afp_errno));
+        free(upath);
         if (fullpath)
             bdestroy(fullpath);
         if (ret) {
@@ -694,7 +682,7 @@ int caseenumerate(const struct vol *vol, struct path *path, struct dir *dir)
  * @param vol      (r) pointer to struct vol
  * @param pdid     (r) Parent CNID
  * @param did      (r) CNID
- * @param fullpath (r) Full unix path to dir
+ * @param fullpath (r) Full unix path to dir or NULL for files
  *
  * @returns pointer to new struct dir or NULL on error
  *
@@ -764,7 +752,7 @@ void dir_free(struct dir *dir)
  *
  * Create a new struct dir from struct path. Then add it to the cache.
  * The caller must have assured that the dir is not already in the cache,
- * cf the assertion.
+ * cf theAFP_ASSERTion.
  * 1. Open adouble file, get CNID from it.
  * 2. Search the database, hinting with the CNID from (1).
  * 3. Build fullpath and create struct dir.
@@ -788,10 +776,10 @@ struct dir *dir_add(const struct vol *vol, const struct dir *dir, struct path *p
     struct adouble *adp = NULL;
     bstring fullpath;
 
-    assert(vol);
-    assert(dir);
-    assert(path);
-    assert(len > 0);
+    AFP_ASSERT(vol);
+    AFP_ASSERT(dir);
+    AFP_ASSERT(path);
+    AFP_ASSERT(len > 0);
 
     if ((cdir = dircache_search_by_name(vol, dir, path->u_name, strlen(path->u_name))) != NULL) {
         /* there's a stray entry in the dircache */
@@ -881,8 +869,8 @@ exit:
  */
 int dir_remove(const struct vol *vol, struct dir *dir)
 {
-    assert(vol);
-    assert(dir);
+    AFP_ASSERT(vol);
+    AFP_ASSERT(dir);
 
     if (dir->d_did == DIRDID_ROOT_PARENT || dir->d_did == DIRDID_ROOT)
         return 0;
@@ -1234,12 +1222,12 @@ struct path *cname(struct vol *vol, struct dir *dir, char **cpath)
  */
 int movecwd(const struct vol *vol, struct dir *dir)
 {
-    assert(vol);
+    int ret;
 
-    if (dir == NULL)
-        return -1;
+    AFP_ASSERT(vol);
+    AFP_ASSERT(dir);
 
-    LOG(log_maxdebug, logtype_afpd, "movecwd(curdir:'%s', cwd:'%s')", 
+    LOG(log_maxdebug, logtype_afpd, "movecwd(curdir:'%s', cwd:'%s')",
         cfrombstring(curdir->d_fullpath), getcwdpath());
 
     if ( dir == curdir)
@@ -1251,8 +1239,21 @@ int movecwd(const struct vol *vol, struct dir *dir)
 
     LOG(log_debug, logtype_afpd, "movecwd(did:%u, '%s')", ntohl(dir->d_did), cfrombstring(dir->d_fullpath));
 
-    if ( chdir(cfrombstring(dir->d_fullpath)) < 0 ) {
-        LOG(log_debug, logtype_afpd, "movecwd('%s'): %s", cfrombstring(dir->d_fullpath), strerror(errno));
+    if ((ret = lchdir(cfrombstring(dir->d_fullpath))) != 0 ) {
+        LOG(log_debug, logtype_afpd, "movecwd('%s'): ret: %u, %s",
+            cfrombstring(dir->d_fullpath), ret, strerror(errno));
+        if (ret == 1) {
+            /* p is a symlink or getcwd failed */
+            afp_errno = AFPERR_BADTYPE;
+
+            if (chdir(vol->v_path ) < 0) {
+                LOG(log_error, logtype_afpd, "can't chdir back'%s': %s", vol->v_path, strerror(errno));
+                /* XXX what do we do here? */
+            }
+            curdir = vol->v_root;
+            return -1;
+        }
+
         switch (errno) {
         case EACCES:
         case EPERM:
@@ -2105,7 +2106,7 @@ int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_
 
     upath = s_path->u_name;
 
-    if (AFP_OK != (err = netatalk_mkdir( upath))) {
+    if (AFP_OK != (err = netatalk_mkdir(vol, upath))) {
         return err;
     }
 
@@ -2136,10 +2137,10 @@ int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_
     ad_close_metadata( &ad);
 
 createdir_done:
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
     /* FIXME: are we really inside the created dir? */
     addir_inherit_acl(vol);
-#endif
+#endif /* HAVE_ACLS */
 
     memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
     *rbuflen = sizeof( u_int32_t );
@@ -2151,8 +2152,12 @@ createdir_done:
  * dst       new unix filename (not a pathname)
  * newname   new mac name
  * newparent curdir
+ * dirfd     -1 means ignore dirfd (or use AT_FDCWD), otherwise src is relative to dirfd
  */
-int renamedir(const struct vol *vol, char *src, char *dst,
+int renamedir(const struct vol *vol,
+              int dirfd,
+              char *src,
+              char *dst,
               struct dir *dir,
               struct dir *newparent,
               char *newname)
@@ -2161,7 +2166,7 @@ int renamedir(const struct vol *vol, char *src, char *dst,
     int             err;
 
     /* existence check moved to afp_moveandrename */
-    if ( unix_rename( src, dst ) < 0 ) {
+    if ( unix_rename(dirfd, src, -1, dst ) < 0 ) {
         switch ( errno ) {
         case ENOENT :
             return( AFPERR_NOOBJ );
@@ -2175,11 +2180,11 @@ int renamedir(const struct vol *vol, char *src, char *dst,
         case EXDEV:
             /* this needs to copy and delete. bleah. that means we have
              * to deal with entire directory hierarchies. */
-            if ((err = copydir(vol, src, dst)) < 0) {
-                deletedir(dst);
+            if ((err = copydir(vol, dirfd, src, dst)) < 0) {
+                deletedir(-1, dst);
                 return err;
             }
-            if ((err = deletedir(src)) < 0)
+            if ((err = deletedir(dirfd, src)) < 0)
                 return err;
             break;
         default :
@@ -2187,7 +2192,7 @@ int renamedir(const struct vol *vol, char *src, char *dst,
         }
     }
 
-    vol->vfs->vfs_renamedir(vol, src, dst);
+    vol->vfs->vfs_renamedir(vol, dirfd, src, dst);
 
     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
 
@@ -2227,7 +2232,7 @@ int deletecurdir(struct vol *vol)
     if ( ad_metadata( ".", ADFLAGS_DIR, &ad) == 0 ) {
 
         ad_getattr(&ad, &ashort);
-        ad_close( &ad, ADFLAGS_HF );
+        ad_close_metadata(&ad);
         if ((ashort & htons(ATTRBIT_NODELETE))) {
             return  AFPERR_OLOCK;
         }
@@ -2262,7 +2267,7 @@ int deletecurdir(struct vol *vol)
         goto delete_done;
     }
 
-    err = netatalk_rmdir_all_errors(cfrombstring(fdir->d_u_name));
+    err = netatalk_rmdir_all_errors(-1, cfrombstring(fdir->d_u_name));
     if ( err ==  AFP_OK || err == AFPERR_NOOBJ) {
         cnid_delete(vol->v_cdb, fdir->d_did);
         dir_remove( vol, fdir );
@@ -2330,7 +2335,7 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r
             name = NULL;
         }
         break;
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
     case 5 : /* UUID -> username */
     case 6 : /* UUID -> groupname */
         if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
@@ -2364,7 +2369,7 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r
             *rbuflen = 2 * sizeof( id );
         }
         break;
-#endif
+#endif /* HAVE_ACLS */
     default :
         return( AFPERR_PARAM );
     }
@@ -2418,7 +2423,7 @@ int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, siz
     case 4 :
         len = (unsigned char) *ibuf++;
         break;
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
     case 5 : /* username -> UUID  */
     case 6 : /* groupname -> UUID */
         if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
@@ -2427,7 +2432,7 @@ int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, siz
         len = ntohs(ulen);
         ibuf += 2;
         break;
-#endif
+#endif /* HAVE_ACLS */
     default :
         return( AFPERR_PARAM );
     }
@@ -2461,7 +2466,7 @@ int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, siz
             memcpy( rbuf, &id, sizeof( id ));
             *rbuflen = sizeof( id );
             break;
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
         case 5 :        /* username -> UUID */
             LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
             if (0 != getuuidfromname(ibuf, UUID_USER, rbuf))
@@ -2474,7 +2479,7 @@ int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, siz
                 return AFPERR_NOITEM;
             *rbuflen = UUID_BINSIZE;
             break;
-#endif
+#endif /* HAVE_ACLS */
         }
     }
     return( AFP_OK );