]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/enumerate.c
Trunk-BP: validity checking.
[netatalk.git] / etc / afpd / enumerate.c
1 /*
2  * $Id: enumerate.c,v 1.9.2.3 2002-02-07 23:56:04 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 #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         syslog( LOG_ERR, "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 #endif /* CNID_DB */
85
86     if (cdir->d_did == 0) {
87 #ifdef USE_LASTDID
88         /* last way of doing DIDs */
89         cdir->d_did = htonl( vol->v_lastdid++ );
90 #else /* USE_LASTDID */
91         lstp = lstat(upath, &lst) < 0 ? st : &lst;
92 #ifdef DID_MTAB
93         /* mtab way of doing DIDs */
94         cdir->d_did = htonl( afpd_st_cnid ( lstp ) );
95 #else /* DID_MTAB */
96         /* the old way of doing DIDs (default) */
97         cdir->d_did = htonl( CNID(lstp, 0) );
98 #endif /* DID_MTAB */
99 #endif /* USE_LASTDID */
100     }
101
102     if ((edir = dirinsert( vol, cdir ))) {
103 #ifndef CNID_DB
104         if (edir->d_name) {
105             if (strcmp(edir->d_name, cdir->d_name)) {
106                 syslog(LOG_INFO, "WARNING: DID conflict for '%s' and '%s'. Are these the same file?", edir->d_name, cdir->d_name);
107             }
108             free(cdir->d_name);
109             free(cdir);
110             return edir;
111         }
112 #endif /* CNID_DB */
113         edir->d_name = cdir->d_name;
114         free(cdir);
115         cdir = edir;
116     }
117
118     /* parent/child directories */
119     cdir->d_parent = dir;
120     dirchildadd(dir, cdir);
121     return( cdir );
122 }
123
124 /*
125  * Struct to save directory reading context in. Used to prevent
126  * O(n^2) searches on a directory.
127  */
128 struct savedir {
129     u_short     sd_vid;
130     int         sd_did;
131     int         sd_buflen;
132     char        *sd_buf;
133     char        *sd_last;
134     int         sd_sindex;
135 };
136 #define SDBUFBRK        1024
137
138 int afp_enumerate(obj, ibuf, ibuflen, rbuf, rbuflen )
139 AFPObj      *obj;
140 char    *ibuf, *rbuf;
141 int             ibuflen, *rbuflen;
142 {
143     struct stat                 st;
144     static struct savedir       sd = { 0, 0, 0, NULL, NULL, 0 };
145     struct vol                  *vol;
146     struct dir                  *dir;
147     struct dirent               *de;
148     DIR                         *dp;
149     int                         did, ret, esz, len, first = 1;
150     char                        *path, *data, *end, *start;
151     u_int16_t                   vid, fbitmap, dbitmap, reqcnt, actcnt = 0;
152     u_int16_t                   sindex, maxsz, sz = 0;
153
154     if ( sd.sd_buflen == 0 ) {
155         if (( sd.sd_buf = (char *)malloc( SDBUFBRK )) == NULL ) {
156             syslog( LOG_ERR, "afp_enumerate: malloc: %s", strerror(errno) );
157             *rbuflen = 0;
158             return AFPERR_MISC;
159         }
160         sd.sd_buflen = SDBUFBRK;
161     }
162
163     ibuf += 2;
164
165     memcpy( &vid, ibuf, sizeof( vid ));
166     ibuf += sizeof( vid );
167
168     if (( vol = getvolbyvid( vid )) == NULL ) {
169         *rbuflen = 0;
170         return( AFPERR_PARAM );
171     }
172
173     memcpy( &did, ibuf, sizeof( did ));
174     ibuf += sizeof( did );
175
176     if (( dir = dirsearch( vol, did )) == NULL ) {
177         *rbuflen = 0;
178         return( AFPERR_NODIR );
179     }
180
181     memcpy( &fbitmap, ibuf, sizeof( fbitmap ));
182     fbitmap = ntohs( fbitmap );
183     ibuf += sizeof( fbitmap );
184
185     memcpy( &dbitmap, ibuf, sizeof( dbitmap ));
186     dbitmap = ntohs( dbitmap );
187     ibuf += sizeof( dbitmap );
188
189     /* check for proper bitmaps -- the stuff in comments is for
190      * variable directory ids. */
191     if (!(fbitmap || dbitmap)
192             /*|| (fbitmap & (1 << FILPBIT_PDID)) ||
193               (dbitmap & (1 << DIRPBIT_PDID))*/) {
194         *rbuflen = 0;
195         return AFPERR_BITMAP;
196     }
197
198     memcpy( &reqcnt, ibuf, sizeof( reqcnt ));
199     reqcnt = ntohs( reqcnt );
200     ibuf += sizeof( reqcnt );
201
202     memcpy( &sindex, ibuf, sizeof( sindex ));
203     sindex = ntohs( sindex );
204     ibuf += sizeof( sindex );
205
206     memcpy( &maxsz, ibuf, sizeof( maxsz ));
207     maxsz = ntohs( maxsz );
208     ibuf += sizeof( maxsz );
209
210     maxsz = min(maxsz, *rbuflen);
211
212     if (( path = cname( vol, dir, &ibuf )) == NULL ) {
213         *rbuflen = 0;
214         return( AFPERR_NODIR );
215     }
216     data = rbuf + 3 * sizeof( u_int16_t );
217     sz = 3 * sizeof( u_int16_t );
218
219     /*
220      * Read the directory into a pre-malloced buffer, stored
221      *          len <name> \0
222      * The end is indicated by a len of 0.
223      */
224     if ( sindex == 1 || curdir->d_did != sd.sd_did || vid != sd.sd_vid ) {
225         sd.sd_last = sd.sd_buf;
226
227         if (( dp = opendir( mtoupath(vol, path ))) == NULL ) {
228             *rbuflen = 0;
229             return (errno == ENOTDIR) ? AFPERR_BADTYPE : AFPERR_NODIR;
230         }
231
232         end = sd.sd_buf + sd.sd_buflen;
233         for ( de = readdir( dp ); de != NULL; de = readdir( dp )) {
234             if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, "."))
235                 continue;
236
237             if (!(validupath(vol, de->d_name)))
238                 continue;
239
240             /* check for vetoed filenames */
241             if (veto_file(vol->v_veto, de->d_name))
242                 continue;
243
244             /* now check against too big a file */
245             if (strlen(utompath(vol, de->d_name)) > MACFILELEN)
246                 continue;
247
248             len = strlen(de->d_name);
249             *(sd.sd_last)++ = len;
250
251             if ( sd.sd_last + len + 2 > end ) {
252                 char *buf;
253
254                 start = sd.sd_buf;
255                 if ((buf = (char *) realloc( sd.sd_buf, sd.sd_buflen +
256                                              SDBUFBRK )) == NULL ) {
257                     syslog( LOG_ERR, "afp_enumerate: realloc: %s",
258                             strerror(errno) );
259                     closedir(dp);
260                     *rbuflen = 0;
261                     return AFPERR_MISC;
262                 }
263                 sd.sd_buf = buf;
264                 sd.sd_buflen += SDBUFBRK;
265                 sd.sd_last = ( sd.sd_last - start ) + sd.sd_buf;
266                 end = sd.sd_buf + sd.sd_buflen;
267             }
268
269             memcpy( sd.sd_last, de->d_name, len + 1 );
270             sd.sd_last += len + 1;
271         }
272         *sd.sd_last = 0;
273
274         sd.sd_last = sd.sd_buf;
275         sd.sd_sindex = 1;
276
277         closedir( dp );
278         sd.sd_vid = vid;
279         sd.sd_did = did;
280     }
281
282     /*
283      * Position sd_last as dictated by sindex.
284      */
285     if ( sindex < sd.sd_sindex ) {
286         sd.sd_sindex = 1;
287         sd.sd_last = sd.sd_buf;
288     }
289     while ( sd.sd_sindex < sindex ) {
290         len = *(sd.sd_last)++;
291         if ( len == 0 ) {
292             sd.sd_did = -1;     /* invalidate sd struct to force re-read */
293             *rbuflen = 0;
294             return( AFPERR_NOOBJ );
295         }
296         sd.sd_last += len + 1;
297         sd.sd_sindex++;
298     }
299
300     while (( len = *(sd.sd_last)) != 0 ) {
301         /*
302          * If we've got all we need, send it.
303          */
304         if ( actcnt == reqcnt ) {
305             break;
306         }
307
308         /*
309          * Save the start position, in case we exceed the buffer
310          * limitation, and have to back up one.
311          */
312         start = sd.sd_last;
313         sd.sd_last++;
314
315         if ( stat( sd.sd_last, &st ) < 0 ) {
316             syslog( LOG_DEBUG, "afp_enumerate: stat %s: %s",
317                     sd.sd_last, strerror(errno) );
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 }