]> arthur.barton.de Git - netatalk.git/blobdiff - etc/afpd/directory.c
previous commit 'lchdir: use chdir + getcwd' was only testing for symlink outside...
[netatalk.git] / etc / afpd / directory.c
index 9917dd43358173c6e0a0fed5e3757b495476d325..8e41298f48c9df474cc48009b3812df2501e30a2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: directory.c,v 1.125 2010-01-06 11:08:53 franklahm Exp $
+ * $Id: directory.c,v 1.137 2010-02-28 22:29:15 didg Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -62,16 +62,59 @@ char *strchr (), *strrchr ();
 extern void addir_inherit_acl(const struct vol *vol);
 #endif
 
+/* 
+ * Directory caches
+ * ================
+ *
+ * There are currently two cache structures where afpd caches directory information
+ * a) a DID/dirname cache in a hashtable 
+ * b) a (red-black) tree with CNIDs as key
+ *
+ * a) is for searching by DID/dirname
+ * b) is for searching by CNID
+ *
+ * Through additional parent, child, previous and next pointers, b) is also used to
+ * represent the on-disk layout of the filesystem. parent and child point to parent
+ * and child directory respectively, linking 2 or more subdirectories in one
+ * directory with previous and next pointers.
+ *
+ * Usage examples, highlighting the main functions:
+ * 
+ * a) is eg used in enumerate():
+ * if IS_DIR
+ *     dir = dirsearch_byname() // search in cache
+ *     if (dir == NULL)         // not found
+ *         dir = adddir()       // add to cache
+ *      getdirparams()
+ *
+ * b) is eg used in afp_getfildirparams()
+ * dirlookup()             // wrapper for cache and db search
+ *   => dir = dirsearch()  // search in cache
+ *      if (dir)           // found
+ *          return
+ *      else               // not found,
+ *          cnid_resolve() // resolve with CNID database
+ *      cname()            // add to cache
+ */
+
 struct dir  *curdir;
 int             afp_errno;
 
 #define SENTINEL (&sentinel)
-static struct dir sentinel = { SENTINEL, SENTINEL, NULL, DIRTREE_COLOR_BLACK,
-                               NULL, NULL, NULL, NULL, NULL, 0, 0,
-                               0, 0, NULL, NULL, 0, NULL};
-static struct dir rootpar  = { SENTINEL, SENTINEL, NULL, 0,
-                               NULL, NULL, NULL, NULL, NULL, 0, 0,
-                               0, 0, NULL, NULL, 0, NULL};
+static struct dir sentinel = { SENTINEL, SENTINEL, NULL, /* left, right, back */
+                               DIRTREE_COLOR_BLACK,      /* color */
+                               NULL, NULL,               /* parent, child */
+                               NULL, NULL,               /* previous, next */
+                               NULL, 0, 0,               /* oforks, did, flags */
+                               0, 0,                     /* ctime, offcnt */
+                               NULL, NULL, NULL};        /* mname, uname, ucs2-name */
+static struct dir rootpar  = { SENTINEL, SENTINEL, NULL,
+                               0,
+                               NULL, NULL,
+                               NULL, NULL,
+                               NULL, 0, 0,
+                               0, 0,
+                               NULL, NULL, NULL};
 
 /* (from IM: Toolbox Essentials)
  * dirFinderInfo (DInfo) fields:
@@ -157,7 +200,6 @@ dirsearch_byname( const struct vol *vol, struct dir *cdir, char *name)
 
         key.d_parent = cdir;
         key.d_u_name = name;
-        key.d_u_name_len = strlen(name);
         hn = hash_lookup(vol->v_hash, &key);
         if (hn) {
             dir = hnode_get(hn);
@@ -776,7 +818,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)) {
@@ -842,7 +884,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;
@@ -864,7 +906,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);
     }
@@ -944,13 +986,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;
     }
@@ -988,7 +1041,6 @@ adddir(struct vol *vol, struct dir *dir, struct path *path)
         dirfreename(edir);
         edir->d_m_name = cdir->d_m_name;
         edir->d_u_name = cdir->d_u_name;
-        edir->d_u_name_len = cdir->d_u_name_len;
         edir->d_m_name_ucs2 = cdir->d_m_name_ucs2;
         free(cdir);
         cdir = edir;
@@ -1064,7 +1116,6 @@ struct dir *dirnew(const char *m_name, const char *u_name)
         return NULL;
     }
 
-    dir->d_u_name_len = strlen(dir->d_u_name);
     dir->d_m_name_ucs2 = NULL;
     dir->d_left = dir->d_right = SENTINEL;
     dir->d_next = dir->d_prev = dir;
@@ -1112,7 +1163,7 @@ static hash_val_t hash_fun2_dir(const void *key)
 {
     const struct dir *k = key;
     const char *data = k->d_u_name;
-    int len = k->d_u_name_len;
+    int len = strlen(k->d_u_name);
     hash_val_t hash = k->d_parent->d_did, tmp;
 
     int rem = len & 3;
@@ -1491,6 +1542,7 @@ int movecwd(struct vol *vol, struct dir *dir)
     struct dir  *d;
     char    *p, *u;
     int     n;
+    int     ret;
 
     if ( dir == curdir ) {
         return( 0 );
@@ -1501,8 +1553,7 @@ int movecwd(struct vol *vol, struct dir *dir)
     }
 
     p = path + sizeof(path) - 1;
-    *p-- = '\0';
-    *p = '.';
+    *p = '\0';
     for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) {
         u = d->d_u_name;
         if (!u) {
@@ -1529,7 +1580,19 @@ 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 or getcwd failed */
+            afp_errno = AFPERR_BADTYPE;
+            vol->v_curdir = curdir = vol->v_dir;
+            if (chdir(vol->v_path ) < 0) {
+                LOG(log_debug, logtype_afpd, "can't chdir back'%s': %s", vol->v_path, strerror(errno));
+                /* XXX what do we do here? */
+            }
+            return -1;
+        }
         switch (errno) {
         case EACCES:
         case EPERM:
@@ -1648,6 +1711,17 @@ int getdirparams(const struct vol *vol,
         ad_init(&ad, vol->v_adouble, vol->v_ad_options);
         if ( !ad_metadata( upath, ADFLAGS_CREATE|ADFLAGS_DIR, &ad) ) {
             isad = 1;
+            if (ad.ad_md->adf_flags & O_CREAT) {
+                /* We just created it */
+                ad_setname(&ad, s_path->m_name);
+                ad_setid( &ad,
+                          s_path->st.st_dev,
+                          s_path->st.st_ino,
+                          dir->d_did,
+                          dir->d_parent->d_did,
+                          vol->v_stamp);
+                ad_flush( &ad);
+            }
         }
     }
 
@@ -1963,7 +2037,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;
@@ -2119,14 +2193,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;
@@ -2536,7 +2610,6 @@ int renamedir(const struct vol *vol, char *src, char *dst,
         dir->d_u_name = buf;
         strcpy( dir->d_u_name, dst );
     }
-    dir->d_u_name_len = strlen(dir->d_u_name);
 
     if (dir->d_m_name_ucs2)
         free(dir->d_m_name_ucs2);
@@ -2617,11 +2690,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) {