/*
* 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 <stdio.h>
#include <stdlib.h>
-#include <dirent.h>
#include <errno.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 "desktop.h"
#include "directory.h"
+#include "dircache.h"
#include "file.h"
#include "volume.h"
#include "globals.h"
*
*/
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;
char *path; /* absolute UNIX path to this directory */
};
#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... */
static int addstack(char *uname, struct dir *dir, int pidx)
{
struct dsitem *ds;
- int l;
+ size_t l, u;
/* check if we have some space on stack... */
if (dsidx >= dssize) {
/* 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) {
- l = strlen(dstack[pidx].path);
- if (!(ds->path = malloc(l + strlen(uname) + 2) ))
+ l = dstack[pidx].path_len;
+ u = strlen(uname) +1;
+ if (!(ds->path = malloc(l + u + 1) ))
return -1;
- strcpy(ds->path, dstack[pidx].path);
- strcat(ds->path, "/");
- strcat(ds->path, uname);
+ memcpy(ds->path, dstack[pidx].path, l);
+ ds->path[l] = '/';
+ memcpy(&ds->path[l+1], uname, u);
+ ds->path_len = l +u;
+ }
+ else {
+ ds->path = strdup(uname);
+ ds->path_len = strlen(uname);
}
-
- ds->checked = 0;
-
return 0;
}
adp = &ad;
}
- if ( ad_metadata( path->u_name, ((isdir)?ADFLAGS_DIR:0), adp) < 0 ) {
+ if ( ad_metadata( path->u_name, ((isdir) ? ADFLAGS_DIR : 0), adp) < 0 ) {
adp = NULL; /* FIXME without resource fork adl_lkup will be call again */
}
/* -------------------- */
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);
}
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)
/* Check for filename */
if ((c1.rbitmap & (1<<DIRPBIT_LNAME))) {
- if ( (size_t)(-1) == (len = convert_string(vol->v_maccharset, CH_UCS2, path->m_name, strlen(path->m_name), convbuf, 512)) )
+ if ( (size_t)(-1) == (len = convert_string(vol->v_maccharset, CH_UCS2, path->m_name, -1, convbuf, 512)) )
goto crit_check_ret;
if ((c1.rbitmap & (1<<CATPBIT_PARTIAL))) {
/* 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;
}
/* 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;
/* 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)
/* 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;
{
char *p = *buf;
- int ret, tbuf =0;
+ int ret;
+ size_t tbuf =0;
u_int16_t resultsize;
int isdir = S_ISDIR(path->st.st_mode);
#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 100
-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)
+#define NUM_ROUNDS 200
+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;
/* 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) {
dirpos = NULL;
}
- if (addstack("", dir, -1) == -1) {
+ if (addstack(vpath, dir, -1) == -1) {
result = AFPERR_MISC;
goto catsearch_end;
}
- dstack[0].path = strdup(vpath);
/* FIXME: Sometimes DID is given by client ! (correct this one above !) */
}
/* 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;
} /* 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))
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,
+ path.st.st_ctime);
+ 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 */
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;
+}
+
/* -------------------------- */
-int catsearch_afp(AFPObj *obj _U_, char *ibuf, int ibuflen,
- char *rbuf, int *rbuflen, int ext)
+static int catsearch_afp(AFPObj *obj _U_, char *ibuf, size_t ibuflen,
+ char *rbuf, size_t *rbuflen, int ext)
{
struct vol *vol;
u_int16_t vid;
size_t len;
u_int16_t namelen;
u_int16_t flags;
- char tmppath[256];
+ char tmppath[256];
+ char *uname;
*rbuflen = 0;
}
/* Parse file specifications */
- spec1 = ibuf;
- spec2 = ibuf + spec_len + 2;
+ spec1 = (unsigned char*)ibuf;
+ spec2 = (unsigned char*)ibuf + spec_len + 2;
spec1 += 2;
spec2 += 2;
/* Get the long filename */
memcpy(tmppath, bspec1 + spec1[1] + 1, (bspec1 + spec1[1])[0]);
tmppath[(bspec1 + spec1[1])[0]]= 0;
- len = convert_string ( vol->v_maccharset, CH_UCS2, tmppath, strlen(tmppath), c1.lname, sizeof(c1.lname));
+ len = convert_string ( vol->v_maccharset, CH_UCS2, tmppath, -1, c1.lname, sizeof(c1.lname));
if (len == (size_t)(-1))
return AFPERR_PARAM;
/* 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;
/* 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);
} /* catsearch_afp */
/* -------------------------- */
-int afp_catsearch (AFPObj *obj, char *ibuf, int ibuflen,
- char *rbuf, int *rbuflen)
+int afp_catsearch (AFPObj *obj, char *ibuf, size_t ibuflen,
+ char *rbuf, size_t *rbuflen)
{
return catsearch_afp( obj, ibuf, ibuflen, rbuf, rbuflen, 0);
}
-int afp_catsearch_ext (AFPObj *obj, char *ibuf, int ibuflen,
- char *rbuf, int *rbuflen)
+int afp_catsearch_ext (AFPObj *obj, char *ibuf, size_t ibuflen,
+ char *rbuf, size_t *rbuflen)
{
return catsearch_afp( obj, ibuf, ibuflen, rbuf, rbuflen, 1);
}