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