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