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