]> arthur.barton.de Git - netatalk.git/blobdiff - etc/afpd/catsearch.c
First working IPC reconnect
[netatalk.git] / etc / afpd / catsearch.c
index 59b0227b98e3a3440a0acff25c0aa74c4aefe26f..a488501e6b67e3c101660b844aa218bb460a1da5 100644 (file)
@@ -1,6 +1,7 @@
 /* 
  * Netatalk 2002 (c)
  * Copyright (C) 1990, 1993 Regents of The University of Michigan
+ * Copyright (C) 2010 Frank Lahm
  * All Rights Reserved. See COPYRIGHT
  */
 
  *
  * Initial version written by Rafal Lewczuk <rlewczuk@pronet.pl>
  *
+ * Starting with Netatalk 2.2 searching by name criteria utilizes the
+ * CNID database in conjunction with an enhanced cnid_dbd. This requires
+ * the use of cnidscheme:dbd for the searched volume, the new functionality
+ * is not built into cnidscheme:cdb.
  */
 
 #ifdef HAVE_CONFIG_H
 #include <ctype.h>
 #include <string.h>
 #include <time.h>
-
-#if STDC_HEADERS
 #include <string.h>
-#else
-#ifndef HAVE_MEMCPY
-#define memcpy(d,s,n) bcopy ((s), (d), (n))
-#define memmove(d,s,n) bcopy ((s), (d), (n))
-#endif /* ! HAVE_MEMCPY */
-#endif
-
 #include <sys/file.h>
 #include <netinet/in.h>
 
 #include <atalk/afp.h>
 #include <atalk/adouble.h>
 #include <atalk/logger.h>
-#ifdef CNID_DB
 #include <atalk/cnid.h>
-#endif /* CNID_DB */
+#include <atalk/cnid_dbd_private.h>
+#include <atalk/util.h>
+#include <atalk/bstradd.h>
+#include <atalk/unicode.h>
+#include <atalk/globals.h>
+
 #include "desktop.h"
 #include "directory.h"
+#include "dircache.h"
 #include "file.h"
 #include "volume.h"
-#include "globals.h"
 #include "filedir.h"
 #include "fork.h"
 
@@ -108,7 +108,8 @@ struct scrit {
  *
  */
 struct dsitem {
-       struct dir *dir; /* Structure describing this directory */
+//     struct dir *dir; /* Structure describing this directory */
+//  cnid_t did;      /* CNID of this directory           */
        int pidx;        /* Parent's dsitem structure index. */
        int checked;     /* Have we checked this directory ? */
        int path_len;
@@ -117,8 +118,6 @@ struct dsitem {
  
 
 #define DS_BSIZE 128
-static u_int32_t cur_pos = 0;    /* Saved position index (ID) - used to remember "position" across FPCatSearch calls */
-static DIR *dirpos = NULL; /* UNIX structure describing currently opened directory. */
 static int save_cidx = -1; /* Saved index of currently scanned directory. */
 
 static struct dsitem *dstack = NULL; /* Directory stack data... */
@@ -143,7 +142,7 @@ static int addstack(char *uname, struct dir *dir, int pidx)
 
        /* Put new element. Allocate and copy lname and path. */
        ds = dstack + dsidx++;
-       ds->dir = dir;
+//     ds->did = dir->d_did;
        ds->pidx = pidx;
        ds->checked = 0;
        if (pidx >= 0) {
@@ -237,13 +236,13 @@ static struct finderinfo *unpack_buffer(struct finderinfo *finfo, char *buffer)
 
 /* -------------------- */
 static struct finderinfo *
-unpack_finderinfo(struct vol *vol, struct path *path, struct adouble **adp, struct finderinfo *finfo)
+unpack_finderinfo(struct vol *vol, struct path *path, struct adouble **adp, struct finderinfo *finfo, int islnk)
 {
        packed_finder  buf;
        void           *ptr;
        
     *adp = adl_lkup(vol, path, *adp);
-       ptr = get_finderinfo(vol, path->u_name, *adp, &buf);
+       ptr = get_finderinfo(vol, path->u_name, *adp, &buf,islnk);
        return unpack_buffer(finfo, ptr);
 }
 
@@ -265,6 +264,8 @@ static int crit_check(struct vol *vol, struct path *path) {
        u_int32_t ac_date, ab_date;
        static char convbuf[514]; /* for convert_charset dest_len parameter +2 */
        size_t len;
+    int islnk;
+    islnk=S_ISLNK(path->st.st_mode);
 
        if (S_ISDIR(path->st.st_mode)) {
                if (!c1.dbitmap)
@@ -379,7 +380,7 @@ static int crit_check(struct vol *vol, struct path *path) {
 
         /* Check file type ID */
        if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.f_type != 0) {
-           finfo = unpack_finderinfo(vol, path, &adp, &finderinfo);
+           finfo = unpack_finderinfo(vol, path, &adp, &finderinfo,islnk);
                if (finfo->f_type != c1.finfo.f_type)
                        goto crit_check_ret;
        }
@@ -387,7 +388,7 @@ static int crit_check(struct vol *vol, struct path *path) {
        /* Check creator ID */
        if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.creator != 0) {
                if (!finfo) {
-                       finfo = unpack_finderinfo(vol, path, &adp, &finderinfo);
+                       finfo = unpack_finderinfo(vol, path, &adp, &finderinfo,islnk);
                }
                if (finfo->creator != c1.finfo.creator)
                        goto crit_check_ret;
@@ -396,7 +397,7 @@ static int crit_check(struct vol *vol, struct path *path) {
        /* Check finder info attributes */
        if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.attrs != 0) {
                if (!finfo) {
-                       finfo = unpack_finderinfo(vol, path, &adp, &finderinfo);
+                       finfo = unpack_finderinfo(vol, path, &adp, &finderinfo,islnk);
                }
 
                if ((finfo->attrs & c2.finfo.attrs) != c1.finfo.attrs)
@@ -406,7 +407,7 @@ static int crit_check(struct vol *vol, struct path *path) {
        /* Check label */
        if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.label != 0) {
                if (!finfo) {
-                       finfo = unpack_finderinfo(vol, path, &adp, &finderinfo);
+                       finfo = unpack_finderinfo(vol, path, &adp, &finderinfo,islnk);
                }
                if ((finfo->label & c2.finfo.label) != c1.finfo.label)
                        goto crit_check_ret;
@@ -477,31 +478,44 @@ static int rslt_add ( struct vol *vol, struct path *path, char **buf, int ext)
 #define VETO_STR \
         "./../.AppleDouble/.AppleDB/Network Trash Folder/TheVolumeSettingsFolder/TheFindByContentFolder/.AppleDesktop/.Parent/"
 
-/* This function performs search. It is called directly from afp_catsearch 
- * vol - volume we are searching on ...
- * dir - directory we are starting from ...
- * c1, c2 - search criteria
- * rmatches - maximum number of matches we can return
- * pos - position we've stopped recently
- * rbuf - output buffer
- * rbuflen - output buffer length
+/*!
+ * This function performs a filesystem search
+ *
+ * Uses globals c1, c2, the search criteria
+ *
+ * @param vol       (r)  volume we are searching on ...
+ * @param dir       (rw) directory we are starting from ...
+ * @param rmatches  (r)  maximum number of matches we can return
+ * @param pos       (r)  position we've stopped recently
+ * @param rbuf      (w)  output buffer
+ * @param nrecs     (w)  number of matches
+ * @param rsize     (w)  length of data written to output buffer
+ * @param ext       (r)  extended search flag
  */
 #define NUM_ROUNDS 200
-static int catsearch(struct vol *vol, struct dir *dir,  
-                    int rmatches, u_int32_t *pos, char *rbuf, u_int32_t *nrecs, int *rsize, int ext)
+static int catsearch(struct vol *vol,
+                     struct dir *dir,  
+                     int rmatches,
+                     uint32_t *pos,
+                     char *rbuf,
+                     uint32_t *nrecs,
+                     int *rsize,
+                     int ext)
 {
+    static u_int32_t cur_pos;    /* Saved position index (ID) - used to remember "position" across FPCatSearch calls */
+    static DIR *dirpos;                 /* UNIX structure describing currently opened directory. */
+    struct dir *curdir;          /* struct dir of current directory */
        int cidx, r;
        struct dirent *entry;
        int result = AFP_OK;
        int ccr;
     struct path path;
-       char *orig_dir = NULL;
-       int orig_dir_len = 128;
        char *vpath = vol->v_path;
        char *rrbuf = rbuf;
     time_t start_time;
     int num_rounds = NUM_ROUNDS;
-    int cached;
+    int cwd = -1;
+    int error;
         
        if (*pos != 0 && *pos != cur_pos) {
                result = AFPERR_CATCHNG;
@@ -510,8 +524,6 @@ static int catsearch(struct vol *vol, struct dir *dir,
 
        /* FIXME: Category "offspring count ! */
 
-       /* So we are beginning... */
-    start_time = time(NULL);
 
        /* We need to initialize all mandatory structures/variables and change working directory appropriate... */
        if (*pos == 0) {
@@ -529,23 +541,24 @@ static int catsearch(struct vol *vol, struct dir *dir,
        }
 
        /* Save current path */
-       orig_dir = (char*)malloc(orig_dir_len);
-       while (getcwd(orig_dir, orig_dir_len-1)==NULL) {
-               if (errno != ERANGE) {
-                       result = AFPERR_MISC;
-                       goto catsearch_end;
-               }
-               orig_dir_len += 128; 
-               orig_dir = realloc(orig_dir, orig_dir_len);
-       } /* while() */
+    if ((cwd = open(".", O_RDONLY)) < 0) {
+        result = AFPERR_MISC;
+        goto catsearch_end;
+    }
        
+       /* So we are beginning... */
+    start_time = time(NULL);
+
        while ((cidx = reducestack()) != -1) {
-               cached = 1;
-               if (dirpos == NULL) {
+               error = lchdir(dstack[cidx].path);
+
+               if (!error && dirpos == NULL)
+                       dirpos = opendir(".");
+
+               if (dirpos == NULL)
                        dirpos = opendir(dstack[cidx].path);
-                       cached = (dstack[cidx].dir->d_child != NULL);
-               }
-               if (dirpos == NULL) {
+
+               if (error || dirpos == NULL) {
                        switch (errno) {
                        case EACCES:
                                dstack[cidx].checked = 1;
@@ -562,11 +575,13 @@ static int catsearch(struct vol *vol, struct dir *dir,
                        } /* switch (errno) */
                        goto catsearch_end;
                }
-               /* FIXME error in chdir, what do we do? */
-               chdir(dstack[cidx].path);
-               
 
-               while ((entry=readdir(dirpos)) != NULL) {
+        if ((curdir = dirlookup_bypath(vol, dstack[cidx].path)) == NULL) {
+            result = AFPERR_MISC;
+            goto catsearch_end;
+        }
+               
+               while ((entry = readdir(dirpos)) != NULL) {
                        (*pos)++;
 
                        if (!check_dirent(vol, entry->d_name))
@@ -594,28 +609,31 @@ static int catsearch(struct vol *vol, struct dir *dir,
                                   ie if in the same loop the parent dir wasn't in the cache
                                   ALL dirsearch_byname will fail.
                                */
-                               if (cached)
-                       path.d_dir = dirsearch_byname(vol, dstack[cidx].dir, path.u_name);
-               else
-                       path.d_dir = NULL;
-               if (!path.d_dir) {
+                int unlen = strlen(path.u_name);
+                path.d_dir = dircache_search_by_name(vol,
+                                                     curdir,
+                                                     path.u_name,
+                                                     unlen);
+               if (path.d_dir == NULL) {
                        /* path.m_name is set by adddir */
-                   if (NULL == (path.d_dir = adddir( vol, dstack[cidx].dir, &path) ) ) {
+                   if ((path.d_dir = dir_add(vol,
+                                              curdir,
+                                              &path,
+                                              unlen)) == NULL) {
                                                result = AFPERR_MISC;
                                                goto catsearch_end;
                                        }
                 }
-                path.m_name = path.d_dir->d_m_name; 
+                path.m_name = cfrombstr(path.d_dir->d_m_name);
                        
                                if (addstack(path.u_name, path.d_dir, cidx) == -1) {
                                        result = AFPERR_MISC;
                                        goto catsearch_end;
                                } 
+            } else {
+               path.d_dir = curdir;
             }
-            else {
-               /* yes it sucks for directory d_dir is the directory, for file it's the parent directory*/
-               path.d_dir = dstack[cidx].dir;
-            }
+
                        ccr = crit_check(vol, &path);
 
                        /* bit 0 means that criteria has been met */
@@ -658,13 +676,165 @@ catsearch_pause:
 
 catsearch_end: /* Exiting catsearch: error condition */
        *rsize = rrbuf - rbuf;
-       if (orig_dir != NULL) {
-               chdir(orig_dir);
-               free(orig_dir);
-       }
+    if (cwd != -1) {
+        if ((fchdir(cwd)) != 0) {
+            LOG(log_debug, logtype_afpd, "error chdiring back: %s", strerror(errno));        
+        }
+        close(cwd);
+    }
        return result;
 } /* catsearch() */
 
+/*!
+ * This function performs a CNID db search
+ *
+ * Uses globals c1, c2, the search criteria
+ *
+ * @param vol       (r)  volume we are searching on ...
+ * @param dir       (rw) directory we are starting from ...
+ * @param uname     (r)  UNIX name of object to search
+ * @param rmatches  (r)  maximum number of matches we can return
+ * @param pos       (r)  position we've stopped recently
+ * @param rbuf      (w)  output buffer
+ * @param nrecs     (w)  number of matches
+ * @param rsize     (w)  length of data written to output buffer
+ * @param ext       (r)  extended search flag
+ */
+static int catsearch_db(struct vol *vol,
+                        struct dir *dir,  
+                        const char *uname,
+                        int rmatches,
+                        uint32_t *pos,
+                        char *rbuf,
+                        uint32_t *nrecs,
+                        int *rsize,
+                        int ext)
+{
+    static char resbuf[DBD_MAX_SRCH_RSLTS * sizeof(cnid_t)];
+    static uint32_t cur_pos;
+    static int num_matches;
+    int ccr ,r;
+       int result = AFP_OK;
+    struct path path;
+       char *rrbuf = rbuf;
+    char buffer[MAXPATHLEN +2];
+    uint16_t flags = CONV_TOLOWER;
+
+    LOG(log_debug, logtype_afpd, "catsearch_db(req pos: %u): {pos: %u, name: %s}",
+        *pos, cur_pos, uname);
+        
+       if (*pos != 0 && *pos != cur_pos) {
+               result = AFPERR_CATCHNG;
+               goto catsearch_end;
+       }
+
+    if (cur_pos == 0 || *pos == 0) {
+        if (convert_charset(vol->v_volcharset,
+                            vol->v_volcharset,
+                            vol->v_maccharset,
+                            uname,
+                            strlen(uname),
+                            buffer,
+                            MAXPATHLEN,
+                            &flags) == (size_t)-1) {
+            LOG(log_error, logtype_afpd, "catsearch_db: conversion error");
+            result = AFPERR_MISC;
+            goto catsearch_end;
+        }
+
+        LOG(log_debug, logtype_afpd, "catsearch_db: %s", buffer);
+
+        if ((num_matches = cnid_find(vol->v_cdb,
+                                     buffer,
+                                     strlen(uname),
+                                     resbuf,
+                                     sizeof(resbuf))) == -1) {
+            result = AFPERR_MISC;
+            goto catsearch_end;
+        }
+    }
+       
+       while (cur_pos < num_matches) {
+        char *name;
+        cnid_t cnid, did;
+        char resolvebuf[12 + MAXPATHLEN + 1];
+        struct dir *dir;
+
+        /* Next CNID to process from buffer */
+        memcpy(&cnid, resbuf + cur_pos * sizeof(cnid_t), sizeof(cnid_t));
+        did = cnid;
+
+        if ((name = cnid_resolve(vol->v_cdb, &did, resolvebuf, 12 + MAXPATHLEN + 1)) == NULL)
+            goto next;
+        LOG(log_debug, logtype_afpd, "catsearch_db: {pos: %u, name:%s, cnid: %u}",
+            cur_pos, name, ntohl(cnid));
+        if ((dir = dirlookup(vol, did)) == NULL)
+            goto next;
+        if (movecwd(vol, dir) < 0 )
+            goto next;
+
+        memset(&path, 0, sizeof(path));
+        path.u_name = name;
+        path.m_name = utompath(vol, name, cnid, utf8_encoding());
+
+        if (of_stat(&path) != 0) {
+            switch (errno) {
+            case EACCES:
+            case ELOOP:
+                goto next;
+            case ENOENT:
+                
+            default:
+                result = AFPERR_MISC;
+                goto catsearch_end;
+            } 
+        }
+        /* For files path.d_dir is the parent dir, for dirs its the dir itself */
+        if (S_ISDIR(path.st.st_mode))
+            if ((dir = dirlookup(vol, cnid)) == NULL)
+                goto next;
+        path.d_dir = dir;
+
+        LOG(log_maxdebug, logtype_afpd,"catsearch_db: dir: %s, cwd: %s, name: %s", 
+            cfrombstr(dir->d_fullpath), getcwdpath(), path.u_name);
+
+        /* At last we can check the search criteria */
+        ccr = crit_check(vol, &path);
+        if ((ccr & 1)) {
+            LOG(log_debug, logtype_afpd,"catsearch_db: match: %s/%s",
+                getcwdpath(), path.u_name);
+            /* bit 1 means that criteria has been met */
+            r = rslt_add(vol, &path, &rrbuf, ext);
+            if (r == 0) {
+                result = AFPERR_MISC;
+                goto catsearch_end;
+            } 
+            *nrecs += r;
+            /* Number of matches limit */
+            if (--rmatches == 0) 
+                goto catsearch_pause;
+            /* Block size limit */
+            if (rrbuf - rbuf >= 448)
+                goto catsearch_pause;
+        }
+    next:
+        cur_pos++;
+    } /* while */
+
+       /* finished */
+       result = AFPERR_EOF;
+    cur_pos = 0;
+       goto catsearch_end;
+
+catsearch_pause:
+    *pos = cur_pos;
+
+catsearch_end: /* Exiting catsearch: error condition */
+       *rsize = rrbuf - rbuf;
+    LOG(log_debug, logtype_afpd, "catsearch_db(req pos: %u): {pos: %u}", *pos, cur_pos);
+       return result;
+}
+
 /* -------------------------- */
 static int catsearch_afp(AFPObj *obj _U_, char *ibuf, size_t ibuflen,
                   char *rbuf, size_t *rbuflen, int ext)
@@ -681,7 +851,8 @@ static int catsearch_afp(AFPObj *obj _U_, char *ibuf, size_t ibuflen,
     size_t     len;
     u_int16_t  namelen;
     u_int16_t  flags;
-    char       tmppath[256];
+    char           tmppath[256];
+    char        *uname;
 
     *rbuflen = 0;
 
@@ -852,11 +1023,13 @@ static int catsearch_afp(AFPObj *obj _U_, char *ibuf, size_t ibuflen,
                /* length */
                memcpy(&namelen, spec1, sizeof(namelen));
                namelen = ntohs (namelen);
-               if (namelen > 255)  /* Safeguard */
-                       namelen = 255;
+               if (namelen > UTF8FILELEN_EARLY)  /* Safeguard */
+                       namelen = UTF8FILELEN_EARLY;
 
                memcpy (c1.utf8name, spec1+2, namelen);
-               c1.utf8name[(namelen+1)] =0;
+               c1.utf8name[namelen] = 0;
+        if ((uname = mtoupath(vol, c1.utf8name, 0, utf8_encoding())) == NULL)
+            return AFPERR_PARAM;
 
                /* convert charset */
                flags = CONV_PRECOMPOSE;
@@ -867,7 +1040,15 @@ static int catsearch_afp(AFPObj *obj _U_, char *ibuf, size_t ibuflen,
     
     /* Call search */
     *rbuflen = 24;
-    ret = catsearch(vol, vol->v_dir, rmatches, &catpos[0], rbuf+24, &nrecs, &rsize, ext);
+    if ((c1.rbitmap & (1 << FILPBIT_PDINFO))
+        && (strcmp(vol->v_cnidscheme, "dbd") == 0)
+        && (vol->v_flags & AFPVOL_SEARCHDB))
+        /* we've got a name and it's a dbd volume, so search CNID database */
+        ret = catsearch_db(vol, vol->v_root, uname, rmatches, &catpos[0], rbuf+24, &nrecs, &rsize, ext);
+    else
+        /* perform a slow filesystem tree search */
+        ret = catsearch(vol, vol->v_root, rmatches, &catpos[0], rbuf+24, &nrecs, &rsize, ext);
+
     memcpy(rbuf, catpos, sizeof(catpos));
     rbuf += sizeof(catpos);