]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/enumerate.c
CNID/DID patch from Uwe Hees (moderately tested)
[netatalk.git] / etc / afpd / enumerate.c
1 /*
2  * $Id: enumerate.c,v 1.6 2001-08-14 14:00:10 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 /* 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: %m" );
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: %m" );
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: %m" );
257                     closedir(dp);
258                     *rbuflen = 0;
259                     return AFPERR_MISC;
260                 }
261                 sd.sd_buf = buf;
262                 sd.sd_buflen += SDBUFBRK;
263                 sd.sd_last = ( sd.sd_last - start ) + sd.sd_buf;
264                 end = sd.sd_buf + sd.sd_buflen;
265             }
266
267             memcpy( sd.sd_last, de->d_name, len + 1 );
268             sd.sd_last += len + 1;
269         }
270         *sd.sd_last = 0;
271
272         sd.sd_last = sd.sd_buf;
273         sd.sd_sindex = 1;
274
275         closedir( dp );
276         sd.sd_vid = vid;
277         sd.sd_did = did;
278     }
279
280     /*
281      * Position sd_last as dictated by sindex.
282      */
283     if ( sindex < sd.sd_sindex ) {
284         sd.sd_sindex = 1;
285         sd.sd_last = sd.sd_buf;
286     }
287     while ( sd.sd_sindex < sindex ) {
288         len = *(sd.sd_last)++;
289         if ( len == 0 ) {
290             sd.sd_did = -1;     /* invalidate sd struct to force re-read */
291             *rbuflen = 0;
292             return( AFPERR_NOOBJ );
293         }
294         sd.sd_last += len + 1;
295         sd.sd_sindex++;
296     }
297
298     while (( len = *(sd.sd_last)) != 0 ) {
299         /*
300          * If we've got all we need, send it.
301          */
302         if ( actcnt == reqcnt ) {
303             break;
304         }
305
306         /*
307          * Save the start position, in case we exceed the buffer
308          * limitation, and have to back up one.
309          */
310         start = sd.sd_last;
311         sd.sd_last++;
312
313         if ( stat( sd.sd_last, &st ) < 0 ) {
314             syslog( LOG_DEBUG, "afp_enumerate: stat %s: %m", sd.sd_last );
315             sd.sd_last += len + 1;
316             continue;
317         }
318
319         /*
320          * If a fil/dir is not a dir, it's a file. This is slightly
321          * inaccurate, since that means /dev/null is a file, /dev/printer
322          * is a file, etc.
323          */
324         if ( S_ISDIR(st.st_mode)) {
325             if ( dbitmap == 0 ) {
326                 sd.sd_last += len + 1;
327                 continue;
328             }
329             path = utompath(vol, sd.sd_last);
330             dir = curdir->d_child; 
331             while (dir) {
332                 if ( strcmp( dir->d_name, path ) == 0 ) {
333                     break;
334                 }
335                 dir = (dir == curdir->d_child->d_prev) ? NULL : dir->d_next;
336             }
337             if (!dir && ((dir = adddir( vol, curdir, path, strlen( path ),
338                                         sd.sd_last, len, &st)) == NULL)) {
339               *rbuflen = 0;
340               return AFPERR_MISC;
341             }
342               
343
344             if (( ret = getdirparams(vol, dbitmap, sd.sd_last, dir,
345                     &st, data + 2 * sizeof( u_char ), &esz )) != AFP_OK ) {
346                 *rbuflen = 0;
347                 return( ret );
348             }
349
350         } else {
351             if ( fbitmap == 0 ) {
352                 sd.sd_last += len + 1;
353                 continue;
354             }
355             
356             if (( ret = getfilparams(vol, fbitmap, utompath(vol, sd.sd_last),
357                     curdir, &st, data + 2 * sizeof( u_char ), &esz )) !=
358                     AFP_OK ) {
359                 *rbuflen = 0;
360                 return( ret );
361             }
362         }
363
364         /*
365          * Make sure entry is an even length, possibly with a null
366          * byte on the end.
367          */
368         if ( esz & 1 ) {
369             *(data + 2 * sizeof( u_char ) + esz ) = '\0';
370             esz++;
371         }
372
373         /*
374          * Check if we've exceeded the size limit.
375          */
376         if ( maxsz < sz + esz + 2 * sizeof( u_char )) {
377             if (first) { /* maxsz can't hold a single reply */
378               *rbuflen = 0; 
379               return AFPERR_PARAM;
380             }
381             sd.sd_last = start;
382             break;
383         }
384
385         if (first)
386           first = 0;
387
388         sz += esz + 2 * sizeof( u_char );
389         *data++ = esz + 2 * sizeof( u_char );
390         *data++ = S_ISDIR(st.st_mode) ? FILDIRBIT_ISDIR : FILDIRBIT_ISFILE;
391         data += esz;
392         actcnt++;
393         sd.sd_last += len + 1;
394     }
395
396     if ( actcnt == 0 ) {
397         *rbuflen = 0;
398         sd.sd_did = -1;         /* invalidate sd struct to force re-read */
399         return( AFPERR_NOOBJ );
400     }
401     sd.sd_sindex = sindex + actcnt;
402
403     /*
404      * All done, fill in misc junk in rbuf
405      */
406     fbitmap = htons( fbitmap );
407     memcpy( rbuf, &fbitmap, sizeof( fbitmap ));
408     rbuf += sizeof( fbitmap );
409     dbitmap = htons( dbitmap );
410     memcpy( rbuf, &dbitmap, sizeof( dbitmap ));
411     rbuf += sizeof( dbitmap );
412     actcnt = htons( actcnt );
413     memcpy( rbuf, &actcnt, sizeof( actcnt ));
414     rbuf += sizeof( actcnt );
415     *rbuflen = sz;
416     return( AFP_OK );
417 }
418
419
420 /* why is this here? well, FPCatSearch is essentially an FPEnumerate
421  * with filters. */
422 int afp_catsearch(AFPObj *obj, char *ibuf, int ibuflen, 
423                   char *rbuf, int *rbuflen)
424 {
425     struct vol *vol;
426     u_int16_t   vid;
427
428     ibuf += 2;
429     memcpy(&vid, ibuf, sizeof(vid));
430     ibuf += sizeof(vid);
431
432     *rbuflen = 0;
433     if ((vol = getvolbyvid(vid)) == NULL)
434        return AFPERR_PARAM;
435
436     /* the ritual:
437      * do a breadth-first search of directories:
438      *   lookup did/name info.
439      *   add to result if match
440      *   check to see if we've exceeded our timelimit
441      *     if yes, return current position
442      *     if not, continue
443      * 
444      *   we keep a copy of our current position in struct vol.
445      *   if the next catsearch request for that volume isn't at
446      *   at the current position, bail and return catchanged.
447      */
448
449     /* eof when done */
450     return AFPERR_EOF;
451 }