]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/catsearch.c
Move volume loading to libatalk
[netatalk.git] / etc / afpd / catsearch.c
1 /* 
2  * Netatalk 2002 (c)
3  * Copyright (C) 1990, 1993 Regents of The University of Michigan
4  * Copyright (C) 2010 Frank Lahm
5  * All Rights Reserved. See COPYRIGHT
6  */
7
8
9 /*
10  * This file contains FPCatSearch implementation. FPCatSearch performs
11  * file/directory search based on specified criteria. It is used by client
12  * to perform fast searches on (propably) big volumes. So, it has to be
13  * pretty fast.
14  *
15  * This implementation bypasses most of adouble/libatalk stuff as long as
16  * possible and does a standard filesystem search. It calls higher-level
17  * libatalk/afpd functions only when it is really needed, mainly while
18  * returning some non-UNIX information or filtering by non-UNIX criteria.
19  *
20  * Initial version written by Rafal Lewczuk <rlewczuk@pronet.pl>
21  *
22  * Starting with Netatalk 2.2 searching by name criteria utilizes the
23  * CNID database in conjunction with an enhanced cnid_dbd. This requires
24  * the use of cnidscheme:dbd for the searched volume, the new functionality
25  * is not built into cnidscheme:cdb.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif /* HAVE_CONFIG_H */
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <errno.h>
35 #include <ctype.h>
36 #include <string.h>
37 #include <time.h>
38 #include <string.h>
39 #include <sys/file.h>
40 #include <netinet/in.h>
41
42 #include <atalk/afp.h>
43 #include <atalk/adouble.h>
44 #include <atalk/logger.h>
45 #include <atalk/cnid.h>
46 #include <atalk/cnid_dbd_private.h>
47 #include <atalk/util.h>
48 #include <atalk/bstradd.h>
49 #include <atalk/unicode.h>
50 #include <atalk/globals.h>
51 #include <atalk/netatalk_conf.h>
52
53 #include "desktop.h"
54 #include "directory.h"
55 #include "dircache.h"
56 #include "file.h"
57 #include "volume.h"
58 #include "filedir.h"
59 #include "fork.h"
60
61
62 struct finderinfo {
63         uint32_t f_type;
64         uint32_t creator;
65         uint16_t attrs;    /* File attributes (high 8 bits)*/
66         uint16_t label;    /* Label (low 8 bits )*/
67         char reserved[22]; /* Unknown (at least for now...) */
68 };
69
70 typedef char packed_finder[ADEDLEN_FINDERI];
71
72 /* Known attributes:
73  * 0x04 - has a custom icon
74  * 0x20 - name/icon is locked
75  * 0x40 - is invisible
76  * 0x80 - is alias
77  *
78  * Known labels:
79  * 0x02 - project 2
80  * 0x04 - project 1
81  * 0x06 - personal
82  * 0x08 - cool
83  * 0x0a - in progress
84  * 0x0c - hot
85  * 0x0e - essential
86  */
87
88 /* This is our search-criteria structure. */
89 struct scrit {
90         uint32_t rbitmap;          /* Request bitmap - which values should we check ? */
91         uint16_t fbitmap, dbitmap; /* file & directory bitmap - which values should we return ? */
92         uint16_t attr;             /* File attributes */
93         time_t cdate;               /* Creation date */
94         time_t mdate;               /* Last modification date */
95         time_t bdate;               /* Last backup date */
96         uint32_t pdid;             /* Parent DID */
97     uint16_t offcnt;           /* Offspring count */
98         struct finderinfo finfo;    /* Finder info */
99         char lname[64];             /* Long name */ 
100         char utf8name[514];         /* UTF8 or UCS2 name */ /* for convert_charset dest_len parameter +2 */
101 };
102
103 /*
104  * Directory tree search is recursive by its nature. But AFP specification
105  * requires FPCatSearch to pause after returning n results and be able to
106  * resume the search later. So we have to do recursive search using flat
107  * (iterative) algorithm and remember all directories to look into in an
108  * stack-like structure. The structure below is one item of directory stack.
109  *
110  */
111 struct dsitem {
112 //      struct dir *dir; /* Structure describing this directory */
113 //  cnid_t did;      /* CNID of this directory           */
114         int pidx;        /* Parent's dsitem structure index. */
115         int checked;     /* Have we checked this directory ? */
116         int path_len;
117         char *path;      /* absolute UNIX path to this directory */
118 };
119  
120
121 #define DS_BSIZE 128
122 static int save_cidx = -1; /* Saved index of currently scanned directory. */
123
124 static struct dsitem *dstack = NULL; /* Directory stack data... */
125 static int dssize = 0;               /* Directory stack (allocated) size... */
126 static int dsidx = 0;                /* First free item index... */
127
128 static struct scrit c1, c2;          /* search criteria */
129
130 /* Puts new item onto directory stack. */
131 static int addstack(char *uname, struct dir *dir, int pidx)
132 {
133         struct dsitem *ds;
134         size_t         l, u;
135
136         /* check if we have some space on stack... */
137         if (dsidx >= dssize) {
138                 dssize += DS_BSIZE;
139                 dstack = realloc(dstack, dssize * sizeof(struct dsitem));       
140                 if (dstack == NULL)
141                         return -1;
142         }
143
144         /* Put new element. Allocate and copy lname and path. */
145         ds = dstack + dsidx++;
146 //      ds->did = dir->d_did;
147         ds->pidx = pidx;
148         ds->checked = 0;
149         if (pidx >= 0) {
150             l = dstack[pidx].path_len;
151             u = strlen(uname) +1;
152             if (!(ds->path = malloc(l + u + 1) ))
153                         return -1;
154                 memcpy(ds->path, dstack[pidx].path, l);
155                 ds->path[l] = '/';
156                 memcpy(&ds->path[l+1], uname, u);
157                 ds->path_len = l +u;
158         }
159         else {
160             ds->path = strdup(uname);
161                 ds->path_len = strlen(uname);
162         }
163         return 0;
164 }
165
166 /* Removes checked items from top of directory stack. Returns index of the first unchecked elements or -1. */
167 static int reducestack(void)
168 {
169         int r;
170         if (save_cidx != -1) {
171                 r = save_cidx;
172                 save_cidx = -1;
173                 return r;
174         }
175
176         while (dsidx > 0) {
177                 if (dstack[dsidx-1].checked) {
178                         dsidx--;
179                         free(dstack[dsidx].path);
180                 } else
181                         return dsidx - 1;
182         } 
183         return -1;
184
185
186 /* Clears directory stack. */
187 static void clearstack(void) 
188 {
189         save_cidx = -1;
190         while (dsidx > 0) {
191                 dsidx--;
192                 free(dstack[dsidx].path);
193         }
194
195
196 /* Looks up for an opened adouble structure, opens resource fork of selected file. 
197  * FIXME What about noadouble?
198 */
199 static struct adouble *adl_lkup(struct vol *vol, struct path *path, struct adouble *adp)
200 {
201         static struct adouble ad;
202         
203         struct ofork *of;
204         int isdir;
205         
206         if (adp)
207             return adp;
208             
209         isdir  = S_ISDIR(path->st.st_mode);
210
211         if (!isdir && (of = of_findname(path))) {
212                 adp = of->of_ad;
213         } else {
214                 ad_init(&ad, vol);
215                 adp = &ad;
216         } 
217
218     if ( ad_metadata( path->u_name, ((isdir) ? ADFLAGS_DIR : 0), adp) < 0 ) {
219         adp = NULL; /* FIXME without resource fork adl_lkup will be call again */
220     }
221     
222         return adp;     
223 }
224
225 /* -------------------- */
226 static struct finderinfo *unpack_buffer(struct finderinfo *finfo, char *buffer)
227 {
228         memcpy(&finfo->f_type,  buffer +FINDERINFO_FRTYPEOFF, sizeof(finfo->f_type));
229         memcpy(&finfo->creator, buffer +FINDERINFO_FRCREATOFF, sizeof(finfo->creator));
230         memcpy(&finfo->attrs,   buffer +FINDERINFO_FRFLAGOFF, sizeof(finfo->attrs));
231         memcpy(&finfo->label,   buffer +FINDERINFO_FRFLAGOFF, sizeof(finfo->label));
232         finfo->attrs &= 0xff00; /* high 8 bits */
233         finfo->label &= 0xff;   /* low 8 bits */
234
235         return finfo;
236 }
237
238 /* -------------------- */
239 static struct finderinfo *
240 unpack_finderinfo(struct vol *vol, struct path *path, struct adouble **adp, struct finderinfo *finfo, int islnk)
241 {
242         packed_finder  buf;
243         void           *ptr;
244         
245     *adp = adl_lkup(vol, path, *adp);
246         ptr = get_finderinfo(vol, path->u_name, *adp, &buf,islnk);
247         return unpack_buffer(finfo, ptr);
248 }
249
250 /* -------------------- */
251 #define CATPBIT_PARTIAL 31
252 /* Criteria checker. This function returns a 2-bit value. */
253 /* bit 0 means if checked file meets given criteria. */
254 /* bit 1 means if it is a directory and we should descent into it. */
255 /* uname - UNIX name 
256  * fname - our fname (translated to UNIX)
257  * cidx - index in directory stack
258  */
259 static int crit_check(struct vol *vol, struct path *path) {
260         int result = 0;
261         uint16_t attr, flags = CONV_PRECOMPOSE;
262         struct finderinfo *finfo = NULL, finderinfo;
263         struct adouble *adp = NULL;
264         time_t c_date, b_date;
265         uint32_t ac_date, ab_date;
266         static char convbuf[514]; /* for convert_charset dest_len parameter +2 */
267         size_t len;
268     int islnk;
269     islnk=S_ISLNK(path->st.st_mode);
270
271         if (S_ISDIR(path->st.st_mode)) {
272                 if (!c1.dbitmap)
273                         return 0;
274         }
275         else {
276                 if (!c1.fbitmap)
277                         return 0;
278
279                 /* compute the Mac name 
280                  * first try without id (it's slow to find it)
281                  * An other option would be to call get_id in utompath but 
282                  * we need to pass parent dir
283                 */
284         if (!(path->m_name = utompath(vol, path->u_name, 0 , utf8_encoding()) )) {
285                 /*retry with the right id */
286        
287                 cnid_t id;
288                 
289                 adp = adl_lkup(vol, path, adp);
290                 id = get_id(vol, adp, &path->st, path->d_dir->d_did, path->u_name, strlen(path->u_name));
291                 if (!id) {
292                         /* FIXME */
293                         return 0;
294                 }
295                 /* save the id for getfilparm */
296                 path->id = id;
297                 if (!(path->m_name = utompath(vol, path->u_name, id , utf8_encoding()))) {
298                         return 0;
299                 }
300         }
301         }
302                 
303         /* Kind of optimization: 
304          * -- first check things we've already have - filename
305          * -- last check things we get from ad_open()
306          * FIXME strmcp strstr (icase)
307          */
308
309         /* Check for filename */
310         if ((c1.rbitmap & (1<<DIRPBIT_LNAME))) { 
311                 if ( (size_t)(-1) == (len = convert_string(vol->v_maccharset, CH_UCS2, path->m_name, -1, convbuf, 512)) )
312                         goto crit_check_ret;
313
314                 if ((c1.rbitmap & (1<<CATPBIT_PARTIAL))) {
315                         if (strcasestr_w( (ucs2_t*) convbuf, (ucs2_t*) c1.lname) == NULL)
316                                 goto crit_check_ret;
317                 } else
318                         if (strcasecmp_w((ucs2_t*) convbuf, (ucs2_t*) c1.lname) != 0)
319                                 goto crit_check_ret;
320         } 
321         
322         if ((c1.rbitmap & (1<<FILPBIT_PDINFO))) { 
323                 if ( (size_t)(-1) == (len = convert_charset( CH_UTF8_MAC, CH_UCS2, CH_UTF8, path->m_name, strlen(path->m_name), convbuf, 512, &flags))) {
324                         goto crit_check_ret;
325                 }
326
327                 if (c1.rbitmap & (1<<CATPBIT_PARTIAL)) {
328                         if (strcasestr_w((ucs2_t *) convbuf, (ucs2_t*)c1.utf8name) == NULL)
329                                 goto crit_check_ret;
330                 } else
331                         if (strcasecmp_w((ucs2_t *)convbuf, (ucs2_t*)c1.utf8name) != 0)
332                                 goto crit_check_ret;
333         } 
334
335
336         /* FIXME */
337         if ((unsigned)c2.mdate > 0x7fffffff)
338                 c2.mdate = 0x7fffffff;
339         if ((unsigned)c2.cdate > 0x7fffffff)
340                 c2.cdate = 0x7fffffff;
341         if ((unsigned)c2.bdate > 0x7fffffff)
342                 c2.bdate = 0x7fffffff;
343
344         /* Check for modification date */
345         if ((c1.rbitmap & (1<<DIRPBIT_MDATE))) {
346                 if (path->st.st_mtime < c1.mdate || path->st.st_mtime > c2.mdate)
347                         goto crit_check_ret;
348         }
349         
350         /* Check for creation date... */
351         if ((c1.rbitmap & (1<<DIRPBIT_CDATE))) {
352                 c_date = path->st.st_mtime;
353                 adp = adl_lkup(vol, path, adp);
354                 if (adp && ad_getdate(adp, AD_DATE_CREATE, &ac_date) >= 0)
355                     c_date = AD_DATE_TO_UNIX(ac_date);
356
357                 if (c_date < c1.cdate || c_date > c2.cdate)
358                         goto crit_check_ret;
359         }
360
361         /* Check for backup date... */
362         if ((c1.rbitmap & (1<<DIRPBIT_BDATE))) {
363                 b_date = path->st.st_mtime;
364                 adp = adl_lkup(vol, path, adp);
365                 if (adp && ad_getdate(adp, AD_DATE_BACKUP, &ab_date) >= 0)
366                         b_date = AD_DATE_TO_UNIX(ab_date);
367
368                 if (b_date < c1.bdate || b_date > c2.bdate)
369                         goto crit_check_ret;
370         }
371                                 
372         /* Check attributes */
373         if ((c1.rbitmap & (1<<DIRPBIT_ATTR)) && c2.attr != 0) {
374                 if ((adp = adl_lkup(vol, path, adp))) {
375                         ad_getattr(adp, &attr);
376                         if ((attr & c2.attr) != c1.attr)
377                                 goto crit_check_ret;
378                 } else 
379                         goto crit_check_ret;
380         }               
381
382         /* Check file type ID */
383         if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.f_type != 0) {
384             finfo = unpack_finderinfo(vol, path, &adp, &finderinfo,islnk);
385                 if (finfo->f_type != c1.finfo.f_type)
386                         goto crit_check_ret;
387         }
388         
389         /* Check creator ID */
390         if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.creator != 0) {
391                 if (!finfo) {
392                         finfo = unpack_finderinfo(vol, path, &adp, &finderinfo,islnk);
393                 }
394                 if (finfo->creator != c1.finfo.creator)
395                         goto crit_check_ret;
396         }
397                 
398         /* Check finder info attributes */
399         if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.attrs != 0) {
400                 if (!finfo) {
401                         finfo = unpack_finderinfo(vol, path, &adp, &finderinfo,islnk);
402                 }
403
404                 if ((finfo->attrs & c2.finfo.attrs) != c1.finfo.attrs)
405                         goto crit_check_ret;
406         }
407         
408         /* Check label */
409         if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.label != 0) {
410                 if (!finfo) {
411                         finfo = unpack_finderinfo(vol, path, &adp, &finderinfo,islnk);
412                 }
413                 if ((finfo->label & c2.finfo.label) != c1.finfo.label)
414                         goto crit_check_ret;
415         }       
416         /* FIXME: Attributes check ! */
417         
418         /* All criteria are met. */
419         result |= 1;
420 crit_check_ret:
421         if (adp != NULL)
422                 ad_close(adp, ADFLAGS_HF);
423         return result;
424 }  
425
426 /* ------------------------------ */
427 static int rslt_add (const AFPObj *obj, struct vol *vol, struct path *path, char **buf, int ext)
428 {
429
430         char            *p = *buf;
431         int             ret;
432         size_t          tbuf =0;
433         uint16_t        resultsize;
434         int             isdir = S_ISDIR(path->st.st_mode); 
435
436         /* Skip resultsize */
437         if (ext) {
438                 p += sizeof(resultsize); 
439         }
440         else {
441                 p++;
442         }
443         *p++ = isdir ? FILDIRBIT_ISDIR : FILDIRBIT_ISFILE;    /* IsDir ? */
444
445         if (ext) {
446                 *p++ = 0;                  /* Pad */
447         }
448         
449         if ( isdir ) {
450         ret = getdirparams(obj, vol, c1.dbitmap, path, path->d_dir, p , &tbuf ); 
451         }
452         else {
453             /* FIXME slow if we need the file ID, we already know it, done ? */
454                 ret = getfilparams (obj, vol, c1.fbitmap, path, path->d_dir, p, &tbuf);
455         }
456
457         if ( ret != AFP_OK )
458                 return 0;
459
460         /* Make sure entry length is even */
461         if ((tbuf & 1)) {
462            *p++ = 0;
463            tbuf++;
464         }
465
466         if (ext) {
467                 resultsize = htons(tbuf);
468                 memcpy ( *buf, &resultsize, sizeof(resultsize) );
469                 *buf += tbuf + 4;
470         }
471         else {
472                 **buf = tbuf;
473                 *buf += tbuf + 2;
474         }
475
476         return 1;
477
478         
479 #define VETO_STR \
480         "./../.AppleDouble/.AppleDB/Network Trash Folder/TheVolumeSettingsFolder/TheFindByContentFolder/.AppleDesktop/.Parent/"
481
482 /*!
483  * This function performs a filesystem search
484  *
485  * Uses globals c1, c2, the search criteria
486  *
487  * @param vol       (r)  volume we are searching on ...
488  * @param dir       (rw) directory we are starting from ...
489  * @param rmatches  (r)  maximum number of matches we can return
490  * @param pos       (r)  position we've stopped recently
491  * @param rbuf      (w)  output buffer
492  * @param nrecs     (w)  number of matches
493  * @param rsize     (w)  length of data written to output buffer
494  * @param ext       (r)  extended search flag
495  */
496 #define NUM_ROUNDS 200
497 static int catsearch(const AFPObj *obj,
498                      struct vol *vol,
499                      struct dir *dir,  
500                      int rmatches,
501                      uint32_t *pos,
502                      char *rbuf,
503                      uint32_t *nrecs,
504                      int *rsize,
505                      int ext)
506 {
507     static uint32_t cur_pos;    /* Saved position index (ID) - used to remember "position" across FPCatSearch calls */
508     static DIR *dirpos;                  /* UNIX structure describing currently opened directory. */
509     struct dir *currentdir;      /* struct dir of current directory */
510         int cidx, r;
511         struct dirent *entry;
512         int result = AFP_OK;
513         int ccr;
514     struct path path;
515         char *vpath = vol->v_path;
516         char *rrbuf = rbuf;
517     time_t start_time;
518     int num_rounds = NUM_ROUNDS;
519     int cwd = -1;
520     int error;
521         
522         if (*pos != 0 && *pos != cur_pos) {
523                 result = AFPERR_CATCHNG;
524                 goto catsearch_end;
525         }
526
527         /* FIXME: Category "offspring count ! */
528
529
530         /* We need to initialize all mandatory structures/variables and change working directory appropriate... */
531         if (*pos == 0) {
532                 clearstack();
533                 if (dirpos != NULL) {
534                         closedir(dirpos);
535                         dirpos = NULL;
536                 } 
537                 
538                 if (addstack(vpath, dir, -1) == -1) {
539                         result = AFPERR_MISC;
540                         goto catsearch_end;
541                 }
542                 /* FIXME: Sometimes DID is given by client ! (correct this one above !) */
543         }
544
545         /* Save current path */
546     if ((cwd = open(".", O_RDONLY)) < 0) {
547         result = AFPERR_MISC;
548         goto catsearch_end;
549     }
550         
551         /* So we are beginning... */
552     start_time = time(NULL);
553
554         while ((cidx = reducestack()) != -1) {
555         LOG(log_debug, logtype_afpd, "catsearch: dir: \"%s\"", dstack[cidx].path);
556
557                 error = lchdir(dstack[cidx].path);
558
559                 if (!error && dirpos == NULL)
560                         dirpos = opendir(".");
561
562                 if (dirpos == NULL)
563                         dirpos = opendir(dstack[cidx].path);
564
565                 if (error || dirpos == NULL) {
566                         switch (errno) {
567                         case EACCES:
568                                 dstack[cidx].checked = 1;
569                                 continue;
570                         case EMFILE:
571                         case ENFILE:
572                         case ENOENT:
573                                 result = AFPERR_NFILE;
574                                 break;
575                         case ENOMEM:
576                         case ENOTDIR:
577                         default:
578                                 result = AFPERR_MISC;
579                         } /* switch (errno) */
580                         goto catsearch_end;
581                 }
582
583         if ((currentdir = dirlookup_bypath(vol, dstack[cidx].path)) == NULL) {
584             result = AFPERR_MISC;
585             goto catsearch_end;
586         }
587         LOG(log_debug, logtype_afpd, "catsearch: current struct dir: \"%s\"", cfrombstr(currentdir->d_fullpath));
588                 
589                 while ((entry = readdir(dirpos)) != NULL) {
590                         (*pos)++;
591
592                         if (!check_dirent(vol, entry->d_name))
593                            continue;
594
595             LOG(log_debug, logtype_afpd, "catsearch(\"%s\"): dirent: \"%s\"",
596                 cfrombstr(currentdir->d_fullpath), entry->d_name);
597
598                         memset(&path, 0, sizeof(path));
599                         path.u_name = entry->d_name;
600                         if (of_stat(&path) != 0) {
601                                 switch (errno) {
602                                 case EACCES:
603                                 case ELOOP:
604                                 case ENOENT:
605                                         continue;
606                                 case ENOTDIR:
607                                 case EFAULT:
608                                 case ENOMEM:
609                                 case ENAMETOOLONG:
610                                 default:
611                                         result = AFPERR_MISC;
612                                         goto catsearch_end;
613                                 } 
614                         }
615                         if (S_ISDIR(path.st.st_mode)) {
616                                 /* here we can short cut 
617                                    ie if in the same loop the parent dir wasn't in the cache
618                                    ALL dirsearch_byname will fail.
619                                 */
620                 int unlen = strlen(path.u_name);
621                 path.d_dir = dircache_search_by_name(vol,
622                                                      currentdir,
623                                                      path.u_name,
624                                                      unlen);
625                 if (path.d_dir == NULL) {
626                         /* path.m_name is set by adddir */
627                     if ((path.d_dir = dir_add(vol,
628                                               currentdir,
629                                               &path,
630                                               unlen)) == NULL) {
631                                                 result = AFPERR_MISC;
632                                                 goto catsearch_end;
633                                         }
634                 }
635                 path.m_name = cfrombstr(path.d_dir->d_m_name);
636                         
637                                 if (addstack(path.u_name, path.d_dir, cidx) == -1) {
638                                         result = AFPERR_MISC;
639                                         goto catsearch_end;
640                                 } 
641             } else {
642                 path.d_dir = currentdir;
643             }
644
645                         ccr = crit_check(vol, &path);
646
647                         /* bit 0 means that criteria has been met */
648                         if ((ccr & 1)) {
649                                 r = rslt_add (obj, vol, &path, &rrbuf, ext);
650                                 
651                                 if (r == 0) {
652                                         result = AFPERR_MISC;
653                                         goto catsearch_end;
654                                 } 
655                                 *nrecs += r;
656                                 /* Number of matches limit */
657                                 if (--rmatches == 0) 
658                                         goto catsearch_pause;
659                                 /* Block size limit */
660                                 if (rrbuf - rbuf >= 448)
661                                         goto catsearch_pause;
662                         }
663                         /* MacOS 9 doesn't like servers executing commands longer than few seconds */
664                         if (--num_rounds <= 0) {
665                             if (start_time != time(NULL)) {
666                                         result=AFP_OK;
667                                         goto catsearch_pause;
668                             }
669                             num_rounds = NUM_ROUNDS;
670                         }
671                 } /* while ((entry=readdir(dirpos)) != NULL) */
672                 closedir(dirpos);
673                 dirpos = NULL;
674                 dstack[cidx].checked = 1;
675         } /* while (current_idx = reducestack()) != -1) */
676
677         /* We have finished traversing our tree. Return EOF here. */
678         result = AFPERR_EOF;
679         goto catsearch_end;
680
681 catsearch_pause:
682         cur_pos = *pos; 
683         save_cidx = cidx;
684
685 catsearch_end: /* Exiting catsearch: error condition */
686         *rsize = rrbuf - rbuf;
687     if (cwd != -1) {
688         if ((fchdir(cwd)) != 0) {
689             LOG(log_debug, logtype_afpd, "error chdiring back: %s", strerror(errno));        
690         }
691         close(cwd);
692     }
693         return result;
694 } /* catsearch() */
695
696 /*!
697  * This function performs a CNID db search
698  *
699  * Uses globals c1, c2, the search criteria
700  *
701  * @param vol       (r)  volume we are searching on ...
702  * @param dir       (rw) directory we are starting from ...
703  * @param uname     (r)  UNIX name of object to search
704  * @param rmatches  (r)  maximum number of matches we can return
705  * @param pos       (r)  position we've stopped recently
706  * @param rbuf      (w)  output buffer
707  * @param nrecs     (w)  number of matches
708  * @param rsize     (w)  length of data written to output buffer
709  * @param ext       (r)  extended search flag
710  */
711 static int catsearch_db(const AFPObj *obj,
712                         struct vol *vol,
713                         struct dir *dir,  
714                         const char *uname,
715                         int rmatches,
716                         uint32_t *pos,
717                         char *rbuf,
718                         uint32_t *nrecs,
719                         int *rsize,
720                         int ext)
721 {
722     static char resbuf[DBD_MAX_SRCH_RSLTS * sizeof(cnid_t)];
723     static uint32_t cur_pos;
724     static int num_matches;
725     int ccr ,r;
726         int result = AFP_OK;
727     struct path path;
728         char *rrbuf = rbuf;
729     char buffer[MAXPATHLEN +2];
730     uint16_t flags = CONV_TOLOWER;
731
732     LOG(log_debug, logtype_afpd, "catsearch_db(req pos: %u): {pos: %u, name: %s}",
733         *pos, cur_pos, uname);
734         
735         if (*pos != 0 && *pos != cur_pos) {
736                 result = AFPERR_CATCHNG;
737                 goto catsearch_end;
738         }
739
740     if (cur_pos == 0 || *pos == 0) {
741         if (convert_charset(vol->v_volcharset,
742                             vol->v_volcharset,
743                             vol->v_maccharset,
744                             uname,
745                             strlen(uname),
746                             buffer,
747                             MAXPATHLEN,
748                             &flags) == (size_t)-1) {
749             LOG(log_error, logtype_afpd, "catsearch_db: conversion error");
750             result = AFPERR_MISC;
751             goto catsearch_end;
752         }
753
754         LOG(log_debug, logtype_afpd, "catsearch_db: %s", buffer);
755
756         if ((num_matches = cnid_find(vol->v_cdb,
757                                      buffer,
758                                      strlen(uname),
759                                      resbuf,
760                                      sizeof(resbuf))) == -1) {
761             result = AFPERR_MISC;
762             goto catsearch_end;
763         }
764     }
765         
766         while (cur_pos < num_matches) {
767         char *name;
768         cnid_t cnid, did;
769         char resolvebuf[12 + MAXPATHLEN + 1];
770         struct dir *dir;
771
772         /* Next CNID to process from buffer */
773         memcpy(&cnid, resbuf + cur_pos * sizeof(cnid_t), sizeof(cnid_t));
774         did = cnid;
775
776         if ((name = cnid_resolve(vol->v_cdb, &did, resolvebuf, 12 + MAXPATHLEN + 1)) == NULL)
777             goto next;
778         LOG(log_debug, logtype_afpd, "catsearch_db: {pos: %u, name:%s, cnid: %u}",
779             cur_pos, name, ntohl(cnid));
780         if ((dir = dirlookup(vol, did)) == NULL)
781             goto next;
782         if (movecwd(vol, dir) < 0 )
783             goto next;
784
785         memset(&path, 0, sizeof(path));
786         path.u_name = name;
787         path.m_name = utompath(vol, name, cnid, utf8_encoding());
788
789         if (of_stat(&path) != 0) {
790             switch (errno) {
791             case EACCES:
792             case ELOOP:
793                 goto next;
794             case ENOENT:
795                 
796             default:
797                 result = AFPERR_MISC;
798                 goto catsearch_end;
799             } 
800         }
801         /* For files path.d_dir is the parent dir, for dirs its the dir itself */
802         if (S_ISDIR(path.st.st_mode))
803             if ((dir = dirlookup(vol, cnid)) == NULL)
804                 goto next;
805         path.d_dir = dir;
806
807         LOG(log_maxdebug, logtype_afpd,"catsearch_db: dir: %s, cwd: %s, name: %s", 
808             cfrombstr(dir->d_fullpath), getcwdpath(), path.u_name);
809
810         /* At last we can check the search criteria */
811         ccr = crit_check(vol, &path);
812         if ((ccr & 1)) {
813             LOG(log_debug, logtype_afpd,"catsearch_db: match: %s/%s",
814                 getcwdpath(), path.u_name);
815             /* bit 1 means that criteria has been met */
816             r = rslt_add(obj, vol, &path, &rrbuf, ext);
817             if (r == 0) {
818                 result = AFPERR_MISC;
819                 goto catsearch_end;
820             } 
821             *nrecs += r;
822             /* Number of matches limit */
823             if (--rmatches == 0) 
824                 goto catsearch_pause;
825             /* Block size limit */
826             if (rrbuf - rbuf >= 448)
827                 goto catsearch_pause;
828         }
829     next:
830         cur_pos++;
831     } /* while */
832
833         /* finished */
834         result = AFPERR_EOF;
835     cur_pos = 0;
836         goto catsearch_end;
837
838 catsearch_pause:
839     *pos = cur_pos;
840
841 catsearch_end: /* Exiting catsearch: error condition */
842         *rsize = rrbuf - rbuf;
843     LOG(log_debug, logtype_afpd, "catsearch_db(req pos: %u): {pos: %u}", *pos, cur_pos);
844         return result;
845 }
846
847 /* -------------------------- */
848 static int catsearch_afp(AFPObj *obj _U_, char *ibuf, size_t ibuflen,
849                   char *rbuf, size_t *rbuflen, int ext)
850 {
851     struct vol *vol;
852     uint16_t   vid;
853     uint16_t   spec_len;
854     uint32_t   rmatches, reserved;
855     uint32_t    catpos[4];
856     uint32_t   pdid = 0;
857     int ret, rsize;
858     uint32_t nrecs = 0;
859     unsigned char *spec1, *spec2, *bspec1, *bspec2;
860     size_t      len;
861     uint16_t    namelen;
862     uint16_t    flags;
863     char            tmppath[256];
864     char        *uname;
865
866     *rbuflen = 0;
867
868     /* min header size */
869     if (ibuflen < 32) {
870         return AFPERR_PARAM;
871     }
872
873     memset(&c1, 0, sizeof(c1));
874     memset(&c2, 0, sizeof(c2));
875
876     ibuf += 2;
877     memcpy(&vid, ibuf, sizeof(vid));
878     ibuf += sizeof(vid);
879
880     if ((vol = getvolbyvid(vid)) == NULL) {
881         return AFPERR_PARAM;
882     }
883     
884     memcpy(&rmatches, ibuf, sizeof(rmatches));
885     rmatches = ntohl(rmatches);
886     ibuf += sizeof(rmatches); 
887
888     /* FIXME: (rl) should we check if reserved == 0 ? */
889     ibuf += sizeof(reserved);
890
891     memcpy(catpos, ibuf, sizeof(catpos));
892     ibuf += sizeof(catpos);
893
894     memcpy(&c1.fbitmap, ibuf, sizeof(c1.fbitmap));
895     c1.fbitmap = c2.fbitmap = ntohs(c1.fbitmap);
896     ibuf += sizeof(c1.fbitmap);
897
898     memcpy(&c1.dbitmap, ibuf, sizeof(c1.dbitmap));
899     c1.dbitmap = c2.dbitmap = ntohs(c1.dbitmap);
900     ibuf += sizeof(c1.dbitmap);
901
902     memcpy(&c1.rbitmap, ibuf, sizeof(c1.rbitmap));
903     c1.rbitmap = c2.rbitmap = ntohl(c1.rbitmap);
904     ibuf += sizeof(c1.rbitmap);
905
906     if (! (c1.fbitmap || c1.dbitmap)) {
907             return AFPERR_BITMAP;
908     }
909
910     if ( ext) {
911         memcpy(&spec_len, ibuf, sizeof(spec_len));
912         spec_len = ntohs(spec_len);
913     }
914     else {
915         /* with catsearch only name and parent id are allowed */
916         c1.fbitmap &= (1<<FILPBIT_LNAME) | (1<<FILPBIT_PDID);
917         c1.dbitmap &= (1<<DIRPBIT_LNAME) | (1<<DIRPBIT_PDID);
918         spec_len = *(unsigned char*)ibuf;
919     }
920
921     /* Parse file specifications */
922     spec1 = (unsigned char*)ibuf;
923     spec2 = (unsigned char*)ibuf + spec_len + 2;
924
925     spec1 += 2; 
926     spec2 += 2; 
927
928     bspec1 = spec1;
929     bspec2 = spec2;
930     /* File attribute bits... */
931     if (c1.rbitmap & (1 << FILPBIT_ATTR)) {
932             memcpy(&c1.attr, spec1, sizeof(c1.attr));
933             spec1 += sizeof(c1.attr);
934             memcpy(&c2.attr, spec2, sizeof(c2.attr));
935             spec2 += sizeof(c1.attr);
936     }
937
938     /* Parent DID */
939     if (c1.rbitmap & (1 << FILPBIT_PDID)) {
940             memcpy(&c1.pdid, spec1, sizeof(pdid));
941             spec1 += sizeof(c1.pdid);
942             memcpy(&c2.pdid, spec2, sizeof(pdid));
943             spec2 += sizeof(c2.pdid);
944     } /* FIXME: PDID - do we demarshall this argument ? */
945
946     /* Creation date */
947     if (c1.rbitmap & (1 << FILPBIT_CDATE)) {
948             memcpy(&c1.cdate, spec1, sizeof(c1.cdate));
949             spec1 += sizeof(c1.cdate);
950             c1.cdate = AD_DATE_TO_UNIX(c1.cdate);
951             memcpy(&c2.cdate, spec2, sizeof(c2.cdate));
952             spec2 += sizeof(c1.cdate);
953             ibuf += sizeof(c1.cdate);;
954             c2.cdate = AD_DATE_TO_UNIX(c2.cdate);
955     }
956
957     /* Modification date */
958     if (c1.rbitmap & (1 << FILPBIT_MDATE)) {
959             memcpy(&c1.mdate, spec1, sizeof(c1.mdate));
960             c1.mdate = AD_DATE_TO_UNIX(c1.mdate);
961             spec1 += sizeof(c1.mdate);
962             memcpy(&c2.mdate, spec2, sizeof(c2.mdate));
963             c2.mdate = AD_DATE_TO_UNIX(c2.mdate);
964             spec2 += sizeof(c1.mdate);
965     }
966     
967     /* Backup date */
968     if (c1.rbitmap & (1 << FILPBIT_BDATE)) {
969             memcpy(&c1.bdate, spec1, sizeof(c1.bdate));
970             spec1 += sizeof(c1.bdate);
971             c1.bdate = AD_DATE_TO_UNIX(c1.bdate);
972             memcpy(&c2.bdate, spec2, sizeof(c2.bdate));
973             spec2 += sizeof(c2.bdate);
974             c1.bdate = AD_DATE_TO_UNIX(c2.bdate);
975     }
976
977     /* Finder info */
978     if (c1.rbitmap & (1 << FILPBIT_FINFO)) {
979         packed_finder buf;
980         
981             memcpy(buf, spec1, sizeof(buf));
982             unpack_buffer(&c1.finfo, buf);      
983             spec1 += sizeof(buf);
984
985             memcpy(buf, spec2, sizeof(buf));
986             unpack_buffer(&c2.finfo, buf);
987             spec2 += sizeof(buf);
988     } /* Finder info */
989
990     if ((c1.rbitmap & (1 << DIRPBIT_OFFCNT)) != 0) {
991         /* Offspring count - only directories */
992                 if (c1.fbitmap == 0) {
993                 memcpy(&c1.offcnt, spec1, sizeof(c1.offcnt));
994                 spec1 += sizeof(c1.offcnt);
995                 c1.offcnt = ntohs(c1.offcnt);
996                 memcpy(&c2.offcnt, spec2, sizeof(c2.offcnt));
997                 spec2 += sizeof(c2.offcnt);
998                 c2.offcnt = ntohs(c2.offcnt);
999                 }
1000                 else if (c1.dbitmap == 0) {
1001                         /* ressource fork length */
1002                 }
1003                 else {
1004                 return AFPERR_BITMAP;  /* error */
1005                 }
1006     } /* Offspring count/ressource fork length */
1007
1008     /* Long name */
1009     if (c1.rbitmap & (1 << FILPBIT_LNAME)) {
1010         /* Get the long filename */     
1011                 memcpy(tmppath, bspec1 + spec1[1] + 1, (bspec1 + spec1[1])[0]);
1012                 tmppath[(bspec1 + spec1[1])[0]]= 0;
1013                 len = convert_string ( vol->v_maccharset, CH_UCS2, tmppath, -1, c1.lname, sizeof(c1.lname));
1014         if (len == (size_t)(-1))
1015             return AFPERR_PARAM;
1016
1017 #if 0   
1018                 /* FIXME: do we need it ? It's always null ! */
1019                 memcpy(c2.lname, bspec2 + spec2[1] + 1, (bspec2 + spec2[1])[0]);
1020                 c2.lname[(bspec2 + spec2[1])[0]]= 0;
1021 #endif
1022     }
1023         /* UTF8 Name */
1024     if (c1.rbitmap & (1 << FILPBIT_PDINFO)) {
1025
1026                 /* offset */
1027                 memcpy(&namelen, spec1, sizeof(namelen));
1028                 namelen = ntohs (namelen);
1029
1030                 spec1 = bspec1+namelen+4; /* Skip Unicode Hint */
1031
1032                 /* length */
1033                 memcpy(&namelen, spec1, sizeof(namelen));
1034                 namelen = ntohs (namelen);
1035                 if (namelen > UTF8FILELEN_EARLY)  /* Safeguard */
1036                         namelen = UTF8FILELEN_EARLY;
1037
1038                 memcpy (c1.utf8name, spec1+2, namelen);
1039                 c1.utf8name[namelen] = 0;
1040         if ((uname = mtoupath(vol, c1.utf8name, 0, utf8_encoding())) == NULL)
1041             return AFPERR_PARAM;
1042
1043                 /* convert charset */
1044                 flags = CONV_PRECOMPOSE;
1045                 len = convert_charset(CH_UTF8_MAC, CH_UCS2, CH_UTF8, c1.utf8name, namelen, c1.utf8name, 512, &flags);
1046         if (len == (size_t)(-1))
1047             return AFPERR_PARAM;
1048     }
1049     
1050     /* Call search */
1051     *rbuflen = 24;
1052     if ((c1.rbitmap & (1 << FILPBIT_PDINFO))
1053         && (strcmp(vol->v_cnidscheme, "dbd") == 0)
1054         && (vol->v_flags & AFPVOL_SEARCHDB))
1055         /* we've got a name and it's a dbd volume, so search CNID database */
1056         ret = catsearch_db(obj, vol, vol->v_root, uname, rmatches, &catpos[0], rbuf+24, &nrecs, &rsize, ext);
1057     else
1058         /* perform a slow filesystem tree search */
1059         ret = catsearch(obj, vol, vol->v_root, rmatches, &catpos[0], rbuf+24, &nrecs, &rsize, ext);
1060
1061     memcpy(rbuf, catpos, sizeof(catpos));
1062     rbuf += sizeof(catpos);
1063
1064     c1.fbitmap = htons(c1.fbitmap);
1065     memcpy(rbuf, &c1.fbitmap, sizeof(c1.fbitmap));
1066     rbuf += sizeof(c1.fbitmap);
1067
1068     c1.dbitmap = htons(c1.dbitmap);
1069     memcpy(rbuf, &c1.dbitmap, sizeof(c1.dbitmap));
1070     rbuf += sizeof(c1.dbitmap);
1071
1072     nrecs = htonl(nrecs);
1073     memcpy(rbuf, &nrecs, sizeof(nrecs));
1074     rbuf += sizeof(nrecs);
1075     *rbuflen += rsize;
1076
1077     return ret;
1078 } /* catsearch_afp */
1079
1080 /* -------------------------- */
1081 int afp_catsearch (AFPObj *obj, char *ibuf, size_t ibuflen,
1082                   char *rbuf, size_t *rbuflen)
1083 {
1084         return catsearch_afp( obj, ibuf, ibuflen, rbuf, rbuflen, 0);
1085 }
1086
1087
1088 int afp_catsearch_ext (AFPObj *obj, char *ibuf, size_t ibuflen,
1089                   char *rbuf, size_t *rbuflen)
1090 {
1091         return catsearch_afp( obj, ibuf, ibuflen, rbuf, rbuflen, 1);
1092 }
1093
1094 /* FIXME: we need a clean separation between afp stubs and 'real' implementation */
1095 /* (so, all buffer packing/unpacking should be done in stub, everything else 
1096    should be done in other functions) */