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