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