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