]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/enumerate.c
Update the CNID code again to allow for 4G CNIDs, and a little somethin'
[netatalk.git] / etc / afpd / enumerate.c
1 /*
2  * $Id: enumerate.c,v 1.16 2002-01-29 21:12:14 jmarcus Exp $
3  *
4  * Copyright (c) 1990,1993 Regents of The University of Michigan.
5  * All Rights Reserved.  See COPYRIGHT.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif /* HAVE_CONFIG_H */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <dirent.h>
16 #include <errno.h>
17
18 #include <atalk/logger.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <sys/file.h>
22 #include <sys/param.h>
23
24 #include <netatalk/endian.h>
25 #include <atalk/afp.h>
26 #include <atalk/adouble.h>
27 #ifdef CNID_DB
28 #include <atalk/cnid.h>
29 #endif /* CNID_DB */
30 #include "desktop.h"
31 #include "directory.h"
32 #include "volume.h"
33 #include "globals.h"
34 #include "file.h"
35 #include "filedir.h"
36
37 /* check for mtab DID code */
38 #ifdef DID_MTAB
39 #include "parse_mtab.h"
40 #endif /* DID_MTAB */
41
42 #define min(a,b)        ((a)<(b)?(a):(b))
43
44 struct dir *
45             adddir( vol, dir, name, namlen, upath, upathlen, st )
46             struct vol  *vol;
47 struct dir      *dir;
48 char    *name, *upath;
49 int             namlen, upathlen;
50 struct stat *st;
51 {
52     struct dir  *cdir, *edir;
53 #if AD_VERSION > AD_VERSION1
54     struct adouble ad;
55 #endif /* AD_VERSION > AD_VERSION1 */
56
57 #ifndef USE_LASTDID
58     struct stat lst, *lstp;
59 #endif /* USE_LASTDID */
60
61     if ((cdir = dirnew(namlen + 1)) == NULL) {
62         LOG(log_error, logtype_default, "adddir: malloc: %s", strerror(errno) );
63         return NULL;
64     }
65     strcpy( cdir->d_name, name );
66     cdir->d_name[namlen] = '\0';
67
68     cdir->d_did = 0;
69
70 #if AD_VERSION > AD_VERSION1
71     /* look in AD v2 header */
72     memset(&ad, 0, sizeof(ad));
73     if (ad_open(upath, ADFLAGS_HF|ADFLAGS_DIR, O_RDONLY, 0, &ad) >= 0) {
74         /* if we can parse the AppleDouble header, retrieve the DID entry into cdir->d_did */
75         memcpy(&cdir->d_did, ad_entry(&ad, ADEID_DID), sizeof(cdir->d_did));
76         ad_close(&ad, ADFLAGS_HF);
77     }
78 #endif /* AD_VERSION */
79
80 #ifdef CNID_DB
81     /* add to cnid db */
82     cdir->d_did = cnid_add(vol->v_db, st, dir->d_did, upath,
83                            upathlen, cdir->d_did);
84     /* Fail out if things go bad with CNID. */
85     if (cdir->d_did < 0) {
86         switch (errno) {
87         case CNID_ERR_PARAM:
88             LOG(log_error, logtype_default, "adddir: Incorrect parameters passed to cnid_add");
89             return NULL;
90         case CNID_ERR_PATH:
91         case CNID_ERR_DB:
92         case CNID_ERR_MAX:
93             return NULL;
94         }
95     }
96 #endif /* CNID_DB */
97
98     if (cdir->d_did == 0) {
99 #ifdef USE_LASTDID
100         /* last way of doing DIDs */
101         cdir->d_did = htonl( vol->v_lastdid++ );
102 #else /* USE_LASTDID */
103         lstp = lstat(upath, &lst) < 0 ? st : &lst;
104 #ifdef DID_MTAB
105         /* mtab way of doing DIDs */
106         cdir->d_did = htonl( afpd_st_cnid ( lstp ) );
107 #else /* DID_MTAB */
108         /* the old way of doing DIDs (default) */
109         cdir->d_did = htonl( CNID(lstp, 0) );
110 #endif /* DID_MTAB */
111 #endif /* USE_LASTDID */
112     }
113
114     if ((edir = dirinsert( vol, cdir ))) {
115 #ifndef CNID_DB
116         if (edir->d_name) {
117             if (strcmp(edir->d_name, cdir->d_name)) {
118                 LOG(log_info, logtype_default, "WARNING: DID conflict for '%s' and '%s'. Are these the same file?", edir->d_name, cdir->d_name);
119             }
120             free(cdir->d_name);
121             free(cdir);
122             return edir;
123         }
124 #endif /* CNID_DB */
125         edir->d_name = cdir->d_name;
126         free(cdir);
127         cdir = edir;
128     }
129
130     /* parent/child directories */
131     cdir->d_parent = dir;
132     dirchildadd(dir, cdir);
133     return( cdir );
134 }
135
136 /*
137  * Struct to save directory reading context in. Used to prevent
138  * O(n^2) searches on a directory.
139  */
140 struct savedir {
141     u_short     sd_vid;
142     int         sd_did;
143     int         sd_buflen;
144     char        *sd_buf;
145     char        *sd_last;
146     int         sd_sindex;
147 };
148 #define SDBUFBRK        1024
149
150 int afp_enumerate(obj, ibuf, ibuflen, rbuf, rbuflen )
151 AFPObj      *obj;
152 char    *ibuf, *rbuf;
153 int             ibuflen, *rbuflen;
154 {
155     struct stat                 st;
156     static struct savedir       sd = { 0, 0, 0, NULL, NULL, 0 };
157     struct vol                  *vol;
158     struct dir                  *dir;
159     struct dirent               *de;
160     DIR                         *dp;
161     int                         did, ret, esz, len, first = 1;
162     char                        *path, *data, *end, *start;
163     u_int16_t                   vid, fbitmap, dbitmap, reqcnt, actcnt = 0;
164     u_int16_t                   sindex, maxsz, sz = 0;
165
166     if ( sd.sd_buflen == 0 ) {
167         if (( sd.sd_buf = (char *)malloc( SDBUFBRK )) == NULL ) {
168             LOG(log_error, logtype_default, "afp_enumerate: malloc: %s", strerror(errno) );
169             *rbuflen = 0;
170             return AFPERR_MISC;
171         }
172         sd.sd_buflen = SDBUFBRK;
173     }
174
175     ibuf += 2;
176
177     memcpy( &vid, ibuf, sizeof( vid ));
178     ibuf += sizeof( vid );
179
180     if (( vol = getvolbyvid( vid )) == NULL ) {
181         *rbuflen = 0;
182         return( AFPERR_PARAM );
183     }
184
185     memcpy( &did, ibuf, sizeof( did ));
186     ibuf += sizeof( did );
187
188     if (( dir = dirsearch( vol, did )) == NULL ) {
189         *rbuflen = 0;
190         return( AFPERR_NODIR );
191     }
192
193     memcpy( &fbitmap, ibuf, sizeof( fbitmap ));
194     fbitmap = ntohs( fbitmap );
195     ibuf += sizeof( fbitmap );
196
197     memcpy( &dbitmap, ibuf, sizeof( dbitmap ));
198     dbitmap = ntohs( dbitmap );
199     ibuf += sizeof( dbitmap );
200
201     /* check for proper bitmaps -- the stuff in comments is for
202      * variable directory ids. */
203     if (!(fbitmap || dbitmap)
204             /*|| (fbitmap & (1 << FILPBIT_PDID)) ||
205               (dbitmap & (1 << DIRPBIT_PDID))*/) {
206         *rbuflen = 0;
207         return AFPERR_BITMAP;
208     }
209
210     memcpy( &reqcnt, ibuf, sizeof( reqcnt ));
211     reqcnt = ntohs( reqcnt );
212     ibuf += sizeof( reqcnt );
213
214     memcpy( &sindex, ibuf, sizeof( sindex ));
215     sindex = ntohs( sindex );
216     ibuf += sizeof( sindex );
217
218     memcpy( &maxsz, ibuf, sizeof( maxsz ));
219     maxsz = ntohs( maxsz );
220     ibuf += sizeof( maxsz );
221
222     maxsz = min(maxsz, *rbuflen);
223
224     if (( path = cname( vol, dir, &ibuf )) == NULL ) {
225         *rbuflen = 0;
226         return( AFPERR_NODIR );
227     }
228     data = rbuf + 3 * sizeof( u_int16_t );
229     sz = 3 * sizeof( u_int16_t );
230
231     /*
232      * Read the directory into a pre-malloced buffer, stored
233      *          len <name> \0
234      * The end is indicated by a len of 0.
235      */
236     if ( sindex == 1 || curdir->d_did != sd.sd_did || vid != sd.sd_vid ) {
237         sd.sd_last = sd.sd_buf;
238
239         if (( dp = opendir( mtoupath(vol, path ))) == NULL ) {
240             *rbuflen = 0;
241             return (errno == ENOTDIR) ? AFPERR_BADTYPE : AFPERR_NODIR;
242         }
243
244         end = sd.sd_buf + sd.sd_buflen;
245         for ( de = readdir( dp ); de != NULL; de = readdir( dp )) {
246             if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, "."))
247                 continue;
248
249             if (!(validupath(vol, de->d_name)))
250                 continue;
251
252             /* check for vetoed filenames */
253             if (veto_file(vol->v_veto, de->d_name))
254                 continue;
255
256             /* now check against too big a file */
257             if (strlen(utompath(vol, de->d_name)) > MACFILELEN)
258                 continue;
259
260             len = strlen(de->d_name);
261             *(sd.sd_last)++ = len;
262
263             if ( sd.sd_last + len + 2 > end ) {
264                 char *buf;
265
266                 start = sd.sd_buf;
267                 if ((buf = (char *) realloc( sd.sd_buf, sd.sd_buflen +
268                                              SDBUFBRK )) == NULL ) {
269                     LOG(log_error, logtype_default, "afp_enumerate: realloc: %s",
270                         strerror(errno) );
271                     closedir(dp);
272                     *rbuflen = 0;
273                     return AFPERR_MISC;
274                 }
275                 sd.sd_buf = buf;
276                 sd.sd_buflen += SDBUFBRK;
277                 sd.sd_last = ( sd.sd_last - start ) + sd.sd_buf;
278                 end = sd.sd_buf + sd.sd_buflen;
279             }
280
281             memcpy( sd.sd_last, de->d_name, len + 1 );
282             sd.sd_last += len + 1;
283         }
284         *sd.sd_last = 0;
285
286         sd.sd_last = sd.sd_buf;
287         sd.sd_sindex = 1;
288
289         closedir( dp );
290         sd.sd_vid = vid;
291         sd.sd_did = did;
292     }
293
294     /*
295      * Position sd_last as dictated by sindex.
296      */
297     if ( sindex < sd.sd_sindex ) {
298         sd.sd_sindex = 1;
299         sd.sd_last = sd.sd_buf;
300     }
301     while ( sd.sd_sindex < sindex ) {
302         len = *(sd.sd_last)++;
303         if ( len == 0 ) {
304             sd.sd_did = -1;     /* invalidate sd struct to force re-read */
305             *rbuflen = 0;
306             return( AFPERR_NOOBJ );
307         }
308         sd.sd_last += len + 1;
309         sd.sd_sindex++;
310     }
311
312     while (( len = *(sd.sd_last)) != 0 ) {
313         /*
314          * If we've got all we need, send it.
315          */
316         if ( actcnt == reqcnt ) {
317             break;
318         }
319
320         /*
321          * Save the start position, in case we exceed the buffer
322          * limitation, and have to back up one.
323          */
324         start = sd.sd_last;
325         sd.sd_last++;
326
327         if ( stat( sd.sd_last, &st ) < 0 ) {
328             LOG(log_debug, logtype_default, "afp_enumerate: stat %s: %s",
329                 sd.sd_last, strerror(errno) );
330             sd.sd_last += len + 1;
331             continue;
332         }
333
334         /*
335          * If a fil/dir is not a dir, it's a file. This is slightly
336          * inaccurate, since that means /dev/null is a file, /dev/printer
337          * is a file, etc.
338          */
339         if ( S_ISDIR(st.st_mode)) {
340             if ( dbitmap == 0 ) {
341                 sd.sd_last += len + 1;
342                 continue;
343             }
344             path = utompath(vol, sd.sd_last);
345             dir = curdir->d_child;
346             while (dir) {
347                 if ( strcmp( dir->d_name, path ) == 0 ) {
348                     break;
349                 }
350                 dir = (dir == curdir->d_child->d_prev) ? NULL : dir->d_next;
351             }
352             if (!dir && ((dir = adddir( vol, curdir, path, strlen( path ),
353                                         sd.sd_last, len, &st)) == NULL)) {
354                 *rbuflen = 0;
355                 return AFPERR_MISC;
356             }
357
358
359             if (( ret = getdirparams(vol, dbitmap, sd.sd_last, dir,
360                                      &st, data + 2 * sizeof( u_char ), &esz )) != AFP_OK ) {
361                 *rbuflen = 0;
362                 return( ret );
363             }
364
365         } else {
366             if ( fbitmap == 0 ) {
367                 sd.sd_last += len + 1;
368                 continue;
369             }
370
371             if (( ret = getfilparams(vol, fbitmap, utompath(vol, sd.sd_last),
372                                      curdir, &st, data + 2 * sizeof( u_char ), &esz )) !=
373                     AFP_OK ) {
374                 *rbuflen = 0;
375                 return( ret );
376             }
377         }
378
379         /*
380          * Make sure entry is an even length, possibly with a null
381          * byte on the end.
382          */
383         if ( esz & 1 ) {
384             *(data + 2 * sizeof( u_char ) + esz ) = '\0';
385             esz++;
386         }
387
388         /*
389          * Check if we've exceeded the size limit.
390          */
391         if ( maxsz < sz + esz + 2 * sizeof( u_char )) {
392             if (first) { /* maxsz can't hold a single reply */
393                 *rbuflen = 0;
394                 return AFPERR_PARAM;
395             }
396             sd.sd_last = start;
397             break;
398         }
399
400         if (first)
401             first = 0;
402
403         sz += esz + 2 * sizeof( u_char );
404         *data++ = esz + 2 * sizeof( u_char );
405         *data++ = S_ISDIR(st.st_mode) ? FILDIRBIT_ISDIR : FILDIRBIT_ISFILE;
406         data += esz;
407         actcnt++;
408         sd.sd_last += len + 1;
409     }
410
411     if ( actcnt == 0 ) {
412         *rbuflen = 0;
413         sd.sd_did = -1;         /* invalidate sd struct to force re-read */
414         return( AFPERR_NOOBJ );
415     }
416     sd.sd_sindex = sindex + actcnt;
417
418     /*
419      * All done, fill in misc junk in rbuf
420      */
421     fbitmap = htons( fbitmap );
422     memcpy( rbuf, &fbitmap, sizeof( fbitmap ));
423     rbuf += sizeof( fbitmap );
424     dbitmap = htons( dbitmap );
425     memcpy( rbuf, &dbitmap, sizeof( dbitmap ));
426     rbuf += sizeof( dbitmap );
427     actcnt = htons( actcnt );
428     memcpy( rbuf, &actcnt, sizeof( actcnt ));
429     rbuf += sizeof( actcnt );
430     *rbuflen = sz;
431     return( AFP_OK );
432 }
433
434
435 /* why is this here? well, FPCatSearch is essentially an FPEnumerate
436  * with filters. */
437 int afp_catsearch(AFPObj *obj, char *ibuf, int ibuflen,
438                   char *rbuf, int *rbuflen)
439 {
440     struct vol *vol;
441     u_int16_t   vid;
442
443     ibuf += 2;
444     memcpy(&vid, ibuf, sizeof(vid));
445     ibuf += sizeof(vid);
446
447     *rbuflen = 0;
448     if ((vol = getvolbyvid(vid)) == NULL)
449         return AFPERR_PARAM;
450
451     /* the ritual:
452      * do a breadth-first search of directories:
453      *   lookup did/name info.
454      *   add to result if match
455      *   check to see if we've exceeded our timelimit
456      *     if yes, return current position
457      *     if not, continue
458      * 
459      *   we keep a copy of our current position in struct vol.
460      *   if the next catsearch request for that volume isn't at
461      *   at the current position, bail and return catchanged.
462      */
463
464     /* eof when done */
465     return AFPERR_EOF;
466 }