]> arthur.barton.de Git - netatalk.git/blobdiff - etc/afpd/directory.c
Fix ACL permission mapping
[netatalk.git] / etc / afpd / directory.c
index cadcdb025b168366920c02a986a2570154da763d..c90ebe1bedf56f8574d17549a640c2f74858fb97 100644 (file)
 #include "mangle.h"
 #include "hash.h"
 
-#ifdef HAVE_ACLS
-extern void addir_inherit_acl(const struct vol *vol);
-#endif
-
 /*
  * FIXMEs, loose ends after the dircache rewrite:
  * o merge dircache_search_by_name and dir_add ??
@@ -60,7 +56,7 @@ int         afp_errno;
 /* As long as directory.c hasn't got its own init call, this get initialized in dircache_init */
 struct dir rootParent  = {
     NULL, NULL, NULL, NULL,          /* path, d_m_name, d_u_name, d_m_name_ucs2 */
-    NULL, NULL, 0, 0,                /* qidx_node, d_ofork, ctime, d_flags */
+    NULL, 0, 0,                      /* qidx_node, ctime, d_flags */
     0, 0, 0, 0                       /* pdid, did, offcnt, d_vid */
 };
 struct dir  *curdir = &rootParent;
@@ -75,6 +71,17 @@ struct path Cur_Path = {
     {0} /* struct stat */
 };
 
+/*
+ * dir_remove queues struct dirs to be freed here. We can't just delete them immeidately
+ * eg in dircache_search_by_id, because a caller somewhere up the stack might be
+ * referencing it.
+ * So instead:
+ * - we mark it as invalid by setting d_did to CNID_INVALID (ie 0)
+ * - queue it in "invalid_dircache_entries" queue
+ * - which is finally freed at the end of every AFP func in afp_dsi.c.
+ */
+q_t *invalid_dircache_entries;
+
 
 /*******************************************************************************************
  * Locals
@@ -294,7 +301,7 @@ static int cname_mtouname(const struct vol *vol, const struct dir *dir, struct p
 {
     static char temp[ MAXPATHLEN + 1];
     char *t;
-    cnid_t fileid;
+    cnid_t fileid = 0;
 
     if (afp_version >= 30) {
         if (toUTF8) {
@@ -559,12 +566,12 @@ struct dir *dirlookup(const struct vol *vol, cnid_t did)
     }
 
     /* Create struct dir */
-    if ((ret = dir_new(mpath, upath, vol, pdid, did, fullpath)) == NULL) { /* 6 */
+    if ((ret = dir_new(mpath, upath, vol, pdid, did, fullpath, st.st_ctime)) == NULL) { /* 6 */
         LOG(log_error, logtype_afpd, "dirlookup(did: %u) {%s, %s}: %s", ntohl(did), mpath, upath, strerror(errno));
         err = 1;
         goto exit;
     }
-
+    
     /* Add it to the cache only if it's a dir */
     if (dircache_add(ret) != 0) { /* 7 */
         err = 1;
@@ -575,10 +582,10 @@ struct dir *dirlookup(const struct vol *vol, cnid_t did)
         ntohl(did), ntohl(pdid), cfrombstr(ret->d_fullpath));
 
 exit:
+    if (upath) free(upath);
     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) {
@@ -681,7 +688,8 @@ 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 or NULL for files
+ * @param path     (r) Full unix path to dir or NULL for files
+ * @param ctime    (r) st_ctime from stat
  *
  * @returns pointer to new struct dir or NULL on error
  *
@@ -692,7 +700,8 @@ struct dir *dir_new(const char *m_name,
                     const struct vol *vol,
                     cnid_t pdid,
                     cnid_t did,
-                    bstring path)
+                    bstring path,
+                    time_t ctime)
 {
     struct dir *dir;
 
@@ -726,6 +735,7 @@ struct dir *dir_new(const char *m_name,
     dir->d_pdid = pdid;
     dir->d_vid = vol->v_vid;
     dir->d_fullpath = path;
+    dir->ctime_dircache = ctime;
     return dir;
 }
 
@@ -750,8 +760,7 @@ void dir_free(struct dir *dir)
  * @brief Create struct dir from struct path
  *
  * 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 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.
@@ -780,7 +789,7 @@ struct dir *dir_add(struct vol *vol, const struct dir *dir, struct path *path, i
     AFP_ASSERT(path);
     AFP_ASSERT(len > 0);
 
-    if ((cdir = dircache_search_by_name(vol, dir, path->u_name, strlen(path->u_name))) != NULL) {
+    if ((cdir = dircache_search_by_name(vol, dir, path->u_name, strlen(path->u_name), path->st.st_ctime)) != NULL) {
         /* there's a stray entry in the dircache */
         LOG(log_debug, logtype_afpd, "dir_add(did:%u,'%s/%s'): {stray cache entry: did:%u,'%s', removing}",
             ntohl(dir->d_did), cfrombstr(dir->d_fullpath), path->u_name,
@@ -823,7 +832,7 @@ struct dir *dir_add(struct vol *vol, const struct dir *dir, struct path *path, i
     }
 
     /* Allocate and initialize struct dir */
-    if ((cdir = dir_new( path->m_name, path->u_name, vol, dir->d_did, id, fullpath)) == NULL) { /* 3 */
+    if ((cdir = dir_new( path->m_name, path->u_name, vol, dir->d_did, id, fullpath, path->st.st_ctime)) == NULL) { /* 3 */
         err = 4;
         goto exit;
     }
@@ -856,12 +865,25 @@ exit:
 }
 
 /*!
- * @brief Remove a dir from a cache and free it and any ressources with it
+ * Free the queue with invalid struct dirs
+ *
+ * This gets called at the end of every AFP func.
+ */
+void dir_free_invalid_q(void)
+{
+    struct dir *dir;
+    while (dir = (struct dir *)dequeue(invalid_dircache_entries))
+        dir_free(dir);
+}
+
+/*!
+ * @brief Remove a dir from a cache and queue it for freeing
  *
  * 1. Check if the dir is locked or has opened forks
  * 2. If it's a request to remove curdir, just chdir to volume root
  * 3. Remove it from the cache
- * 4. Remove the dir plus any allocated resources it references
+ * 4. Queue it for removal
+ * 5. Mark it as invalid
  *
  * @param (r) pointer to struct vol
  * @param (rw) pointer to struct dir
@@ -874,9 +896,9 @@ int dir_remove(const struct vol *vol, struct dir *dir)
     if (dir->d_did == DIRDID_ROOT_PARENT || dir->d_did == DIRDID_ROOT)
         return 0;
 
-    if (dir->d_flags & DIRF_CACHELOCK || dir->d_ofork) { /* 1 */
-        LOG(log_warning, logtype_afpd, "dir_remove(did:%u,'%s'): dir is locked or has opened forks",
-            ntohl(dir->d_did), cfrombstr(dir->d_fullpath));
+    if (dir->d_flags & DIRF_CACHELOCK) { /* 1 */
+        LOG(log_warning, logtype_afpd, "dir_remove(did:%u,'%s'): dir is locked",
+            ntohl(dir->d_did), cfrombstr(dir->d_u_name));
         return 0;
     }
 
@@ -887,11 +909,11 @@ int dir_remove(const struct vol *vol, struct dir *dir)
     }
 
     LOG(log_debug, logtype_afpd, "dir_remove(did:%u,'%s'): {removing}",
-        ntohl(dir->d_did), cfrombstr(dir->d_fullpath));
+        ntohl(dir->d_did), cfrombstr(dir->d_u_name));
 
     dircache_remove(vol, dir, DIRCACHE | DIDNAME_INDEX | QUEUE_INDEX); /* 3 */
-    dir_free(dir);              /* 4 */
-
+    enqueue(invalid_dircache_entries, dir); /* 4 */
+    dir->d_did = CNID_INVALID;              /* 5 */
     return 0;
 }
 
@@ -1082,7 +1104,7 @@ struct path *cname(struct vol *vol, struct dir *dir, char **cpath)
         /* 6*/
         for ( p = path; *data != 0 && len > 0; len-- ) {
             *p++ = *data++;
-            if (p > &path[ MAXPATHLEN]) {
+            if (p > &path[UTF8FILELEN_EARLY]) {   /* FIXME safeguard, limit of early Mac OS X */
                 afp_errno = AFPERR_PARAM;
                 return NULL;
             }
@@ -1173,7 +1195,7 @@ struct path *cname(struct vol *vol, struct dir *dir, char **cpath)
 
             /* Search the cache */
             int unamelen = strlen(ret.u_name);
-            cdir = dircache_search_by_name(vol, dir, ret.u_name, unamelen); /* 14 */
+            cdir = dircache_search_by_name(vol, dir, ret.u_name, unamelen, ret.st.st_ctime); /* 14 */
             if (cdir == NULL) {
                 /* Not in cache, create one */
                 if ((cdir = dir_add(vol, dir, &ret, unamelen)) == NULL) { /* 15 */
@@ -1306,10 +1328,18 @@ int file_access(struct path *path, int mode)
     struct maccess ma;
 
     accessmode(path->u_name, &ma, curdir, &path->st);
-    if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
+
+    LOG(log_debug, logtype_afpd, "file_access(\"%s\"): mapped user mode: 0x%02x",
+        path->u_name, ma.ma_user);
+
+    if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE)) {
+        LOG(log_debug, logtype_afpd, "file_access(\"%s\"): write access denied", path->u_name);
         return -1;
-    if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
+    }
+    if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD)) {
+        LOG(log_debug, logtype_afpd, "file_access(\"%s\"): read access denied", path->u_name);
         return -1;
+    }
     return 0;
 
 }
@@ -2142,11 +2172,6 @@ int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_
     ad_close_metadata( &ad);
 
 createdir_done:
-#ifdef HAVE_ACLS
-    /* FIXME: are we really inside the created dir? */
-    addir_inherit_acl(vol);
-#endif /* HAVE_ACLS */
-
     memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
     *rbuflen = sizeof( u_int32_t );
     setvoltime(obj, vol );
@@ -2334,17 +2359,18 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r
             name = NULL;
         }
         break;
-#ifdef HAVE_ACLS
+
     case 5 : /* UUID -> username */
     case 6 : /* UUID -> groupname */
         if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
             return AFPERR_PARAM;
         LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request");
         uuidtype_t type;
-        len = getnamefromuuid( ibuf, &name, &type);
+        len = getnamefromuuid((unsigned char*) ibuf, &name, &type);
         if (len != 0)       /* its a error code, not len */
             return AFPERR_NOITEM;
-        if (type == UUID_USER) {
+        switch (type) {
+        case UUID_USER:
             if (( pw = getpwnam( name )) == NULL )
                 return( AFPERR_NOITEM );
             LOG(log_debug, logtype_afpd, "afp_mapid: name:%s -> uid:%d", name, pw->pw_uid);
@@ -2355,7 +2381,8 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r
             memcpy( rbuf, &id, sizeof( id ));
             rbuf += sizeof( id );
             *rbuflen = 2 * sizeof( id );
-        } else {        /* type == UUID_GROUP */
+            break;
+        case UUID_GROUP:
             if (( gr = getgrnam( name )) == NULL )
                 return( AFPERR_NOITEM );
             LOG(log_debug, logtype_afpd, "afp_mapid: group:%s -> gid:%d", name, gr->gr_gid);
@@ -2366,9 +2393,15 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r
             memcpy( rbuf, &id, sizeof( id ));
             rbuf += sizeof( id );
             *rbuflen = 2 * sizeof( id );
+            break;
+        case UUID_LOCAL:
+            free(name);
+            return (AFPERR_NOITEM);
+        default:
+            return AFPERR_MISC;
         }
         break;
-#endif /* HAVE_ACLS */
+
     default :
         return( AFPERR_PARAM );
     }
@@ -2422,7 +2455,6 @@ int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, siz
     case 4 :
         len = (unsigned char) *ibuf++;
         break;
-#ifdef HAVE_ACLS
     case 5 : /* username -> UUID  */
     case 6 : /* groupname -> UUID */
         if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
@@ -2431,7 +2463,6 @@ int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, siz
         len = ntohs(ulen);
         ibuf += 2;
         break;
-#endif /* HAVE_ACLS */
     default :
         return( AFPERR_PARAM );
     }
@@ -2465,20 +2496,18 @@ 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_ACLS
         case 5 :        /* username -> UUID */
             LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
-            if (0 != getuuidfromname(ibuf, UUID_USER, rbuf))
+            if (0 != getuuidfromname(ibuf, UUID_USER, (unsigned char *)rbuf))
                 return AFPERR_NOITEM;
             *rbuflen = UUID_BINSIZE;
             break;
         case 6 :        /* groupname -> UUID */
             LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
-            if (0 != getuuidfromname(ibuf, UUID_GROUP, rbuf))
+            if (0 != getuuidfromname(ibuf, UUID_GROUP, (unsigned char *)rbuf))
                 return AFPERR_NOITEM;
             *rbuflen = UUID_BINSIZE;
             break;
-#endif /* HAVE_ACLS */
         }
     }
     return( AFP_OK );