]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/catsearch.c
Rudimentary FPCatSearch implementation.
[netatalk.git] / etc / afpd / catsearch.c
1 /* 
2  * Copyright (C) 2002 Rafal Lewczuk (rlewczuk@pronet.pl) 
3  * Copyright (C) 1990, 1993 Regents of The University of Michigan
4  * All Rights Reserved. See COPYRIGHT
5  */
6
7
8 /*
9  * This file contains FPCatSearch implementation. FPCatSearch performs
10  * file/directory search based on specified criteria. It is used by client
11  * to perform fast searches on (propably) big volumes. So, it has to be
12  * pretty fast.
13  *
14  * This implementation bypasses most of adouble/libatalk stuff as long as
15  * possible and does a standard filesystem search. It calls higher-level
16  * libatalk/afpd functions only when it is really needed, mainly while
17  * returning some non-UNIX information or filtering by non-UNIX criteria.
18  *
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif /* HAVE_CONFIG_H */
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <dirent.h>
28 #include <errno.h>
29 #include <syslog.h>
30 #include <unistd.h>
31
32 #if STDC_HEADERS
33 #include <string.h>
34 #else
35 #ifndef HAVE_MEMCPY
36 #define memcpy(d,s,n) bcopy ((s), (d), (n))
37 #define memmove(d,s,n) bcopy ((s), (d), (n))
38 #endif /* ! HAVE_MEMCPY */
39 #endif
40
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <sys/file.h>
44 #include <netinet/in.h>
45
46 #include <netatalk/endian.h>
47 #include <atalk/afp.h>
48 #include <atalk/adouble.h>
49 #ifdef CNID_DB
50 #include <atalk/cnid.h>
51 #endif /* DID_MTAB */
52 #include "desktop.h"
53 #include "directory.h"
54 #include "file.h"
55 #include "volume.h"
56 #include "globals.h"
57 #include "filedir.h"
58 #include "fork.h"
59
60 #ifdef DID_MTAB
61 #include "parse-mtab.h"
62 #endif /* DID_MTAB */
63
64 #ifdef WITH_CATSEARCH
65
66 struct finderinfo {
67         u_int32_t f_type;
68         u_int32_t creator;
69         u_int8_t attrs;    /* File attributes (8 bits)*/
70         u_int8_t label;    /* Label (8 bits)*/
71         char reserved[22]; /* Unknown (at least for now...) */
72 };
73
74 /* Known attributes:
75  * 0x04 - has a custom icon
76  * 0x20 - name/icon is locked
77  * 0x40 - is invisible
78  * 0x80 - is alias
79  *
80  * Known labels:
81  * 0x02 - project 2
82  * 0x04 - project 1
83  * 0x06 - personal
84  * 0x08 - cool
85  * 0x0a - in progress
86  * 0x0c - hot
87  * 0x0e - essential
88  */
89
90 /* This is our search-criteria structure. */
91 struct scrit {
92         u_int32_t rbitmap;          /* Request bitmap - which values should we check ? */
93         u_int16_t fbitmap, dbitmap; /* file & directory bitmap - which values should we return ? */
94         u_int16_t attr;             /* File attributes */
95         time_t cdate;               /* Creation date */
96         time_t mdate;               /* Last modification date */
97         time_t bdate;               /* Last backup date */
98         u_int32_t pdid;             /* Parent DID */
99         u_int16_t offcnt;           /* Offspring count */
100         struct finderinfo finfo;    /* Finder info */
101         char lname[32];             /* Long name */
102 };
103
104 /*
105  * Directory tree search is recursive by its nature. But AFP specification
106  * requires FPCatSearch to pause after returning n results and be able to
107  * resume the search later. So we have to do recursive search using flat
108  * (iterative) algorithm and remember all directories to look into in an
109  * stack-like structure. The structure below is one item of directory stack.
110  *
111  */
112 struct dsitem {
113         char *lname;     /* Long name */
114         struct dir *dir; /* Structure describing this directory */
115         int pidx;        /* Parent's dsitem structure index. */
116         int checked;     /* Have we checked this directory ? */
117         char *path;      /* UNIX path to this directory */
118 };
119  
120
121 #define DS_BSIZE 128
122 static int cur_pos = 0;    /* Saved position index (ID) - used to remember "position" across FPCatSearch calls */
123 static DIR *dirpos = NULL; /* UNIX structure describing currently opened directory. */
124 static int save_cidx = -1; /* Saved index of currently scanned directory. */
125
126 static struct dsitem *dstack = NULL; /* Directory stack data... */
127 static int dssize = 0;               /* Directory stack (allocated) size... */
128 static int dsidx = 0;                /* First free item index... */
129
130 static struct scrit c1, c2;          /* search criteria */
131
132 /* Puts new item onto directory stack. */
133 static int addstack(char *lname, struct dir *dir, int pidx)
134 {
135         struct dsitem *ds;
136
137         /* check if we have some space on stack... */
138         if (dsidx >= dssize) {
139                 dssize += DS_BSIZE;
140                 dstack = realloc(dstack, dssize * sizeof(struct dsitem));       
141                 if (dstack == NULL)
142                         return -1;
143         }
144
145         /* Put new element. Allocate and copy lname and path. */
146         ds = dstack + dsidx++;
147         ds->lname = strdup(lname);
148         ds->dir = dir;
149         ds->pidx = pidx;
150         if (pidx >= 0) {
151                 ds->path = malloc(strlen(dstack[pidx].path) + strlen(ds->lname) + 2);
152                 strcpy(ds->path, dstack[pidx].path);
153                 strcat(ds->path, "/");
154                 strcat(ds->path, ds->lname);
155         }
156
157         ds->checked = 0;
158
159         return 0;
160 }
161
162 /* Removes checked items from top of directory stack. Returns index of the first unchecked elements or -1. */
163 static int reducestack()
164 {
165         int r;
166         if (save_cidx != -1) {
167                 r = save_cidx;
168                 save_cidx = -1;
169                 return r;
170         }
171
172         while (dsidx > 0) {
173                 if (dstack[dsidx-1].checked) {
174                         dsidx--;
175                         free(dstack[dsidx].lname);
176                         free(dstack[dsidx].path);
177                         /* Check if we need to free (or release) dir structures */
178                 } else
179                         return dsidx - 1;
180         } // while
181         return -1;
182 } /* reducestack() */
183
184 /* Clears directory stack. */
185 static void clearstack() 
186 {
187         save_cidx = -1;
188         while (dsidx > 0) {
189                 dsidx--;
190                 free(dstack[dsidx].lname);
191                 free(dstack[dsidx].path);
192                 /* Check if we need to free (or release) dir structures */
193         }
194 } /* clearstack() */
195
196 /* Fills in dir field of dstack[cidx]. Must fill parent dirs' fields if needed... */
197 static int resolve_dir(struct vol *vol, int cidx)
198 {
199         struct dir *dir, *curdir;
200         struct stat statbuf;
201
202         if (dstack[cidx].dir != NULL)
203                 return 1;
204
205         if (dstack[cidx].pidx < 0)
206                 return 0;
207
208         if (dstack[dstack[cidx].pidx].dir == NULL && resolve_dir(vol, dstack[cidx].pidx) == 0)
209                return 0;
210
211         curdir = dstack[dstack[cidx].pidx].dir;
212         dir = curdir->d_child;
213         while (dir) {
214                 if (strcmp(dir->d_name, dstack[cidx].lname) == 0)
215                         break;
216                 dir = (dir == curdir->d_child->d_prev) ? NULL : dir->d_next;
217         } /* while */
218
219         if (!dir)
220                 if (stat(dstack[cidx].path, &statbuf)==-1) {
221                         syslog(LOG_DEBUG, "resolve_dir: stat %s: %s", dstack[cidx].path, strerror(errno));
222                         return 0;
223                 }
224
225         if (!dir && ((dir = adddir(vol, curdir, dstack[cidx].lname, strlen(dstack[cidx].lname),
226                                                 dstack[cidx].path, strlen(dstack[cidx].path), &statbuf)) == NULL))
227                         return 0;
228         dstack[cidx].dir = dir;
229
230         return 1;
231 } /* resolve_dir */
232
233 /* Looks up for an opened adouble structure, opens resource fork of selected file. */
234 static struct adouble *adl_lkup(struct vol *vol, char *upath, int cidx)
235 {
236         static struct adouble ad;
237         struct adouble *adp;
238         char *mpath = utompath(vol, upath);
239         struct ofork *of;
240
241 /*
242         //if (dstack[cidx].dir == NULL && !resolve_dir(vol, cidx))
243         //      return NULL;
244
245         //if ((of = of_findname(vol, dstack[cidx].dir, mpath))) {
246         //      adp = of->of_ad;
247         //} else {
248         */
249                 memset(&ad, 0, sizeof(ad));
250                 adp = &ad;
251         /* } */
252
253         if ( ad_open( upath, ADFLAGS_HF, O_RDONLY, 0, adp) < 0 ) {
254                 return NULL;
255         } 
256         return adp;     
257 }
258
259 #define CATPBIT_PARTIAL 31
260 /* Criteria checker. This function returns a 2-bit value. */
261 /* bit 0 means if checked file meets given criteria. */
262 /* bit 1 means if it is a directory and we should descent into it. */
263 /* uname - UNIX name 
264  * fname - our fname (translated to UNIX)
265  * cidx - index in directory stack
266  */
267 static int crit_check(struct vol *vol, char *uname, char *fname, int cidx) {
268         int r = 0;
269         struct stat sbuf;
270         u_int16_t attr;
271         struct finderinfo *finfo = NULL;
272         struct adouble *adp = NULL;
273         time_t c_date, b_date;
274
275         if (stat(uname, &sbuf) < 0)
276                 return 0;
277         
278         if (S_ISDIR(sbuf.st_mode))
279                 r = 2;
280
281         /* Kind of optimization: 
282          * -- first check things we've already have - filename
283          * -- last check things we get from ad_open()
284          */
285
286         /* Check for filename */
287         if (c1.rbitmap & (1<<DIRPBIT_LNAME)) { 
288                 if (c1.rbitmap & (1<<CATPBIT_PARTIAL)) {
289                         if (strstr(fname, c1.lname) == NULL)
290                                 goto crit_check_ret;
291                 } else
292                         if (strcmp(fname, c1.lname) != 0)
293                                 goto crit_check_ret;
294         } /* if (c1.rbitmap & ... */
295
296
297         /* FIXME */
298         if ((unsigned)c2.mdate > 0x7fffffff)
299                 c2.mdate = 0x7fffffff;
300         if ((unsigned)c2.cdate > 0x7fffffff)
301                 c2.cdate = 0x7fffffff;
302         if ((unsigned)c2.bdate > 0x7fffffff)
303                 c2.bdate = 0x7fffffff;
304
305         /* Check for modification date FIXME: should we look at adouble structure ? */
306         if ((c1.rbitmap & (1<<DIRPBIT_MDATE))) 
307                 if (sbuf.st_mtime < c1.mdate || sbuf.st_mtime > c2.mdate)
308                         goto crit_check_ret;
309
310         /* Check for creation date... */
311         if (c1.rbitmap & (1<<DIRPBIT_CDATE)) {
312                 if (adp || (adp = adl_lkup(vol, uname, cidx))) {
313                         if (ad_getdate(adp, AD_DATE_CREATE, (u_int32_t*)&c_date) >= 0)
314                                 c_date = AD_DATE_TO_UNIX(c_date);
315                         else c_date = sbuf.st_mtime;
316                 } else c_date = sbuf.st_mtime;
317                 if (c_date < c1.cdate || c_date > c2.cdate)
318                         goto crit_check_ret;
319         }
320
321         /* Check for backup date... */
322         if (c1.rbitmap & (1<<DIRPBIT_BDATE)) {
323                 if (adp || (adp == adl_lkup(vol, uname, cidx))) {
324                         if (ad_getdate(adp, AD_DATE_BACKUP, (u_int32_t*)&b_date) >= 0)
325                                 b_date = AD_DATE_TO_UNIX(b_date);
326                         else b_date = sbuf.st_mtime;
327                 } else b_date = sbuf.st_mtime;
328                 if (b_date < c1.bdate || b_date > c2.bdate)
329                         goto crit_check_ret;
330         }
331                                 
332         /* Check attributes */
333         if ((c1.rbitmap & (1<<DIRPBIT_ATTR)) && c2.attr != 0)
334                 if (adp || (adp = adl_lkup(vol, uname, cidx))) {
335                         ad_getattr(adp, &attr);
336                         if ((attr & c2.attr) != c1.attr)
337                                 goto crit_check_ret;
338                 } else goto crit_check_ret;
339                 
340
341         /* Check file type ID */
342         if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.f_type != 0)
343                 if (adp || (adp = adl_lkup(vol, uname, cidx))) {
344                         finfo = (struct finderinfo*)ad_entry(adp, ADEID_FINDERI);
345                         if (finfo->f_type != c1.finfo.f_type)
346                                 goto crit_check_ret;
347                 } else goto crit_check_ret;
348
349         /* Check creator ID */
350         if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.creator != 0)
351                 if (adp || (adp = adl_lkup(vol, uname, cidx))) {
352                         finfo = (struct finderinfo*)ad_entry(adp, ADEID_FINDERI);
353                         if (finfo->creator != c1.finfo.creator)
354                                 goto crit_check_ret;
355                 } else goto crit_check_ret;
356         
357         /* Check finder info attributes */
358         if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.attrs != 0)
359                 if (adp || (adp = adl_lkup(vol, uname, cidx))) {
360                         finfo = (struct finderinfo*)ad_entry(adp, ADEID_FINDERI);
361                         if ((finfo->attrs & c2.finfo.attrs) != c1.finfo.attrs)
362                                 goto crit_check_ret;
363                 } else goto crit_check_ret;
364
365         /* Check label */
366         if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.label != 0)
367                 if (adp || (adp = adl_lkup(vol, uname, cidx))) {
368                         finfo = (struct finderinfo*)ad_entry(adp, ADEID_FINDERI);
369                         if ((finfo->label & c2.finfo.label) != c1.finfo.label)
370                                 goto crit_check_ret;
371                 } else goto crit_check_ret;
372         
373         /* FIXME: Attributes check ! */
374         
375         /* All criteria are met. */
376         r |= 1;
377 crit_check_ret:
378         if (adp != NULL)
379                 ad_close(adp, ADFLAGS_HF);
380         return r;
381 }  
382
383
384 /* Adds an item to resultset. */
385 static int rslt_add(struct vol *vol, struct stat *statbuf, char *fname, short cidx, int isdir, char **rbuf)
386 {
387         char *p = *rbuf;
388         int l = fname != NULL ? strlen(fname) : 0;
389         u_int32_t did;
390         char p0;
391
392         p0 = p[0] = cidx != -1 ? l + 7 : l + 5;
393         if (p0 & 1) p[0]++;
394         p[1] = isdir ? 128 : 0;
395         p += 2;
396         if (cidx != -1) {
397                 if (dstack[cidx].dir == NULL && resolve_dir(vol, cidx) == 0)
398                         return 0;
399                 did = dstack[cidx].dir->d_did;
400                 memcpy(p, &did, sizeof(did));
401                 p += sizeof(did);
402         }
403
404         /* Fill offset of returned file name */
405         if (fname != NULL) {
406                 *p++ = 0;
407                 *p++ = (int)(p - *rbuf) + 1;
408                 p[0] = l;
409                 strcpy(p+1, fname);
410                 p += l + 1;
411         }
412
413         if (p0 & 1)
414                 *p++ = 0;
415
416         *rbuf = p;
417         /* *rbuf[0] = (int)(p-*rbuf); */
418         return 1;
419 } /* rslt_add */
420
421 #define VETO_STR \
422         "./../.AppleDouble/.AppleDB/Network Trash Folder/TheVolumeSettingsFolder/TheFindByContentFolder/.AppleDesktop/.Parent/"
423
424 /* This function performs search. It is called directly from afp_catsearch 
425  * vol - volume we are searching on ...
426  * dir - directory we are starting from ...
427  * c1, c2 - search criteria
428  * rmatches - maximum number of matches we can return
429  * pos - position we've stopped recently
430  * rbuf - output buffer
431  * rbuflen - output buffer length
432 */
433 static int catsearch(struct vol *vol, struct dir *dir,  
434                      int rmatches, int *pos, char *rbuf, u_int32_t *nrecs, int *rsize)
435 {
436         int cidx, r, i;
437         char *fname = NULL;
438         struct dirent *entry;
439         struct stat statbuf;
440         int result = AFP_OK;
441         int ccr;
442         char *orig_dir = NULL;
443         int orig_dir_len = 128;
444         char *path = vol->v_path;
445         char *rrbuf = rbuf;
446
447         if (*pos != 0 && *pos != cur_pos) 
448                 return AFPERR_CATCHNG;
449
450         /* FIXME: Category "offspring count ! */
451
452         /* So we are beginning... */
453         /* We need to initialize all mandatory structures/variables and change working directory appropriate... */
454         if (*pos == 0) {
455                 clearstack();
456                 if (dirpos != NULL) {
457                         closedir(dirpos);
458                         dirpos = NULL;
459                 } /* if (dirpos != NULL) */
460                 
461                 if (addstack("", dir, -1) == -1) {
462                         result = AFPERR_MISC;
463                         goto catsearch_end;
464                 }
465                 dstack[0].path = strdup(path);
466                 /* FIXME: Sometimes DID is given by klient ! (correct this one above !) */
467         }
468
469         /* Save current path */
470         orig_dir = (char*)malloc(orig_dir_len);
471         while (getcwd(orig_dir, orig_dir_len-1)==NULL) {
472                 if (errno != ERANGE) {
473                         result = AFPERR_MISC;
474                         goto catsearch_end;
475                 }
476                 orig_dir_len += 128; 
477                 orig_dir = realloc(orig_dir, orig_dir_len);
478         } /* while() */
479         
480         while ((cidx = reducestack()) != -1) {
481                 if (dirpos == NULL)
482                         dirpos = opendir(dstack[cidx].path);    
483                 if (dirpos == NULL) {
484                         switch (errno) {
485                         case EACCES:
486                                 dstack[cidx].checked = 1;
487                                 continue;
488                         case EMFILE:
489                         case ENFILE:
490                         case ENOENT:
491                                 result = AFPERR_NFILE;
492                                 break;
493                         case ENOMEM:
494                         case ENOTDIR:
495                         default:
496                                 result = AFPERR_MISC;
497                         } /* switch (errno) */
498                         goto catsearch_end;
499                 }
500                 chdir(dstack[cidx].path);
501                 while ((entry=readdir(dirpos)) != NULL) {
502                         (*pos)++;
503                         if (veto_file(VETO_STR, entry->d_name))
504                                 continue;
505                         if (stat(entry->d_name, &statbuf) != 0) {
506                                 switch (errno) {
507                                 case EACCES:
508                                 case ELOOP:
509                                 case ENOENT:
510                                         continue;
511                                 case ENOTDIR:
512                                 case EFAULT:
513                                 case ENOMEM:
514                                 case ENAMETOOLONG:
515                                 default:
516                                         result = AFPERR_MISC;
517                                         goto catsearch_end;
518                                 } /* switch (errno) */
519                         } /* if (stat(entry->d_name, &statbuf) != 0) */
520                         fname = utompath(vol, entry->d_name);
521                         for (i = 0; fname[i] != 0; i++)
522                                 fname[i] = tolower(fname[i]);
523                         if (strlen(fname) > MACFILELEN) 
524                                 continue;
525                         ccr = crit_check(vol, entry->d_name, fname, cidx);
526                         /* bit 0 means that criteria has ben met */
527                         if (ccr & 1) {
528                                 r = rslt_add(vol, &statbuf, 
529                                              (c1.fbitmap&(1<<FILPBIT_LNAME))|(c1.dbitmap&(1<<DIRPBIT_LNAME)) ? 
530                                                  utompath(vol, entry->d_name) : NULL,   
531                                              (c1.fbitmap&(1<<FILPBIT_PDID))|(c1.dbitmap&(1<<DIRPBIT_PDID)) ? 
532                                                  cidx : -1, 
533                                              S_ISDIR(statbuf.st_mode), &rrbuf); 
534                                 if (r == 0) {
535                                         result = AFPERR_MISC;
536                                         goto catsearch_end;
537                                 } 
538                                 *nrecs += r;
539                                 /* Number of matches limit */
540                                 if (--rmatches == 0) 
541                                         goto catsearch_pause; /* FIXME: timelimit checks ! */
542                                 /* Block size limit */
543                                 if (rrbuf - rbuf >= 448)
544                                         goto catsearch_pause;
545
546                         } 
547                         /* bit 1 means that we have to descend into this directory. */
548                         if (ccr & 2) {
549                                 if (S_ISDIR(statbuf.st_mode))
550                                         if (addstack(entry->d_name, NULL, cidx) == -1) {
551                                                 result = AFPERR_MISC;
552                                                 goto catsearch_end;
553                                         } /* if (addstack... */
554                         }
555                 } /* while ((entry=readdir(dirpos)) != NULL) */
556                 closedir(dirpos);dirpos = NULL;
557                 dstack[cidx].checked = 1;
558         } /* while (current_idx = reducestack()) != -1) */
559
560         /* We have finished traversing our tree. Return EOF here. */
561         result = AFPERR_EOF;
562         goto catsearch_end;
563
564 catsearch_pause:
565         cur_pos = *pos; 
566         save_cidx = cidx;
567
568 catsearch_end: /* Exiting catsearch: error condition */
569         *rsize = rrbuf - rbuf;
570         if (orig_dir != NULL) {
571                 chdir(orig_dir);
572                 free(orig_dir);
573         }
574         return result;
575 } /* catsearch() */
576
577
578 int afp_catsearch(AFPObj *obj, char *ibuf, int ibuflen,
579                   char *rbuf, int *rbuflen)
580 {
581     struct vol *vol;
582     u_int16_t   vid;
583     u_int32_t   rmatches, reserved;
584     u_int32_t   catpos[4];
585     u_int32_t   pdid = 0;
586     char        *lname = NULL;
587     struct dir *dir;
588     int ret, rsize, i = 0;
589     u_int32_t nrecs = 0;
590     static int nrr = 1;
591     char *spec1, *spec2, *bspec1, *bspec2;
592
593     memset(&c1, 0, sizeof(c1));
594     memset(&c2, 0, sizeof(c2));
595
596     ibuf += 2;
597     memcpy(&vid, ibuf, sizeof(vid));
598     ibuf += sizeof(vid);
599
600     *rbuflen = 0;
601     if ((vol = getvolbyvid(vid)) == NULL)
602         return AFPERR_PARAM;
603
604     memcpy(&rmatches, ibuf, sizeof(rmatches));
605     rmatches = ntohl(rmatches);
606     ibuf += sizeof(rmatches); 
607
608     /* FIXME: (rl) should we check if reserved == 0 ? */
609     ibuf += sizeof(reserved);
610
611     memcpy(catpos, ibuf, sizeof(catpos));
612     ibuf += sizeof(catpos);
613
614     memcpy(&c1.fbitmap, ibuf, sizeof(c1.fbitmap));
615     c1.fbitmap = c2.fbitmap = ntohs(c1.fbitmap);
616     ibuf += sizeof(c1.fbitmap);
617
618     memcpy(&c1.dbitmap, ibuf, sizeof(c1.dbitmap));
619     c1.dbitmap = c2.dbitmap = ntohl(c1.dbitmap);
620     ibuf += sizeof(c1.dbitmap);
621
622     memcpy(&c1.rbitmap, ibuf, sizeof(c1.rbitmap));
623     c1.rbitmap = c2.rbitmap = ntohl(c1.rbitmap);
624     ibuf += sizeof(c1.rbitmap);
625
626     if (! (c1.fbitmap || c1.dbitmap)) {
627             *rbuflen = 0;
628             return AFPERR_BITMAP;
629     }
630
631     /* Parse file specifications */
632     spec1 = ibuf;
633     spec2 = ibuf + ibuf[0] + 2;
634
635     spec1 += 2; bspec1 = spec1;
636     spec2 += 2; bspec2 = spec2;
637
638     /* File attribute bits... */
639     if (c1.rbitmap & (1 << FILPBIT_ATTR)) {
640             memcpy(&c1.attr, ibuf, sizeof(c1.attr));
641             spec1 += sizeof(c1.attr);
642             c1.attr = ntohs(c1.attr);
643             memcpy(&c2.attr, ibuf, sizeof(c2.attr));
644             spec2 += sizeof(c1.attr);
645             c2.attr = ntohs(c2.attr);
646     }
647
648     /* Parent DID */
649     if (c1.rbitmap & (1 << FILPBIT_PDID)) {
650             memcpy(&c1.pdid, spec1, sizeof(pdid));
651             spec1 += sizeof(c1.pdid);
652             memcpy(&c2.pdid, spec2, sizeof(pdid));
653             spec2 += sizeof(c2.pdid);
654     } /* FIXME: PDID - do we demarshall this argument ? */
655
656     /* Creation date */
657     if (c1.rbitmap & (1 << FILPBIT_CDATE)) {
658             memcpy(&c1.cdate, spec1, sizeof(c1.cdate));
659             spec1 += sizeof(c1.cdate);
660             c1.cdate = AD_DATE_TO_UNIX(c1.cdate);
661             memcpy(&c2.cdate, spec2, sizeof(c2.cdate));
662             spec2 += sizeof(c1.cdate);
663             ibuf += sizeof(c1.cdate);;
664             c2.cdate = AD_DATE_TO_UNIX(c2.cdate);
665     }
666
667     /* Modification date */
668     if (c1.rbitmap & (1 << FILPBIT_MDATE)) {
669             memcpy(&c1.mdate, spec1, sizeof(c1.mdate));
670             c1.mdate = AD_DATE_TO_UNIX(c1.mdate);
671             spec1 += sizeof(c1.mdate);
672             memcpy(&c2.mdate, spec2, sizeof(c2.mdate));
673             c2.mdate = AD_DATE_TO_UNIX(c2.mdate);
674             spec2 += sizeof(c1.mdate);
675     }
676     
677     /* Backup date */
678     if (c1.rbitmap & (1 << FILPBIT_BDATE)) {
679             memcpy(&c1.bdate, spec1, sizeof(c1.bdate));
680             spec1 += sizeof(c1.bdate);
681             c1.bdate = AD_DATE_TO_UNIX(c1.bdate);
682             memcpy(&c2.bdate, spec2, sizeof(c2.bdate));
683             spec2 += sizeof(c2.bdate);
684             c1.bdate = AD_DATE_TO_UNIX(c2.bdate);
685     }
686
687     /* Finder info */
688     if (c1.rbitmap * (1 << FILPBIT_FINFO)) {
689             memcpy(&c1.finfo, spec1, sizeof(c1.finfo));
690             spec1 += sizeof(c1.finfo);
691             memcpy(&c2.finfo, spec2, sizeof(c2.finfo));
692             spec2 += sizeof(c2.finfo);
693     } /* Finder info */
694
695     /* Offspring count - only directories */
696     if (c1.dbitmap != 0 && (c1.rbitmap & (1 << DIRPBIT_OFFCNT)) != 0) {
697             memcpy(&c1.offcnt, spec1, sizeof(c1.offcnt));
698             spec1 += sizeof(c1.offcnt);
699             c1.offcnt = ntohs(c1.offcnt);
700             memcpy(&c2.offcnt, spec2, sizeof(c2.offcnt));
701             spec2 += sizeof(c2.offcnt);
702             c2.offcnt = ntohs(c2.offcnt);
703     } /* Offspring count */
704
705     /* Long name */
706     if (c1.rbitmap & (1 << FILPBIT_LNAME)) {
707         /* Get the long filename */     
708         memcpy(c1.lname, bspec1 + spec1[1] + 1, (bspec1 + spec1[1])[0]);
709         c1.lname[(bspec1 + spec1[1])[0]]= 0;
710         for (i = 0; c1.lname[i] != 0; i++)
711                 c1.lname[i] = tolower(c1.lname[i]);
712         /* FIXME: do we need it ? It's always null ! */
713         memcpy(c2.lname, bspec2 + spec2[1] + 1, (bspec2 + spec2[1])[0]);
714         c2.lname[(bspec2 + spec2[1])[0]]= 0;
715         for (i = 0; c2.lname[i] != 0; i++)
716                 c2.lname[i] = tolower(c2.lname[i]);
717     }
718
719
720     /* Call search */
721     *rbuflen = 24;
722     ret = catsearch(vol, vol->v_dir, rmatches, &catpos[0], rbuf+24, &nrecs, &rsize);
723     memcpy(rbuf, catpos, sizeof(catpos));
724     rbuf += sizeof(catpos);
725     c1.fbitmap = htons(c1.fbitmap);
726     memcpy(rbuf, &c1.fbitmap, sizeof(c1.fbitmap));
727     rbuf += sizeof(c1.fbitmap);
728     c1.dbitmap = htons(c1.dbitmap);
729     memcpy(rbuf, &c1.dbitmap, sizeof(c1.dbitmap));
730     rbuf += sizeof(c2.dbitmap);
731     nrecs = htonl(nrecs);
732     memcpy(rbuf, &nrecs, sizeof(nrecs));
733     rbuf += sizeof(nrecs);
734     *rbuflen += rsize;
735
736     return ret;
737 } /* afp_catsearch */
738
739 /* FIXME: we need a clean separation between afp stubs and 'real' implementation */
740 /* (so, all buffer packing/unpacking should be done in stub, everything else 
741    should be done in other functions) */
742
743 #endif
744 /* WITH_CATSEARCH */