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