]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/enumerate.c
8c161d8d9f28eab9bef26f25e3cb364875c3d2e1
[netatalk.git] / etc / afpd / enumerate.c
1 /*
2  * $Id: enumerate.c,v 1.34 2003-02-16 12:35:04 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 <errno.h>
16
17 #include <atalk/logger.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <sys/file.h>
21 #include <sys/param.h>
22
23 #include <netatalk/endian.h>
24 #include <atalk/afp.h>
25 #include <atalk/adouble.h>
26 #ifdef CNID_DB
27 #include <atalk/cnid.h>
28 #endif /* CNID_DB */
29 #include "desktop.h"
30 #include "directory.h"
31 #include "volume.h"
32 #include "globals.h"
33 #include "file.h"
34 #include "fork.h"
35 #include "filedir.h"
36
37 #define min(a,b)        ((a)<(b)?(a):(b))
38
39 /* ---------------------------- */
40 struct dir *
41             adddir( vol, dir, path)
42 struct vol      *vol;
43 struct dir      *dir;
44 struct path     *path;
45 {
46     struct dir  *cdir, *edir;
47     int         upathlen;
48     char        *name;
49     char        *upath;
50 #ifndef USE_LASTDID
51     struct stat lst, *lstp;
52 #endif /* USE_LASTDID */
53     struct stat *st;
54
55     upath = path->u_name;
56     name  = path->m_name;    
57     st    = &path->st;
58     upathlen = strlen(upath);
59     if ((cdir = dirnew(name, upath)) == NULL) {
60         LOG(log_error, logtype_afpd, "adddir: malloc: %s", strerror(errno) );
61         return NULL;
62     }
63
64     cdir->d_did = 0;
65
66 #ifdef CNID_DB
67     /* add to cnid db */
68     cdir->d_did = cnid_add(vol->v_db, st, dir->d_did, upath,
69                            upathlen, cdir->d_did);
70     /* Fail out if things go bad with CNID. */
71     if (cdir->d_did == CNID_INVALID) {
72         switch (errno) {
73         case CNID_ERR_CLOSE: /* the db is closed */
74             break;
75         case CNID_ERR_PARAM:
76             LOG(log_error, logtype_afpd, "adddir: Incorrect parameters passed to cnid_add");
77             return NULL;
78         case CNID_ERR_PATH:
79         case CNID_ERR_DB:
80         case CNID_ERR_MAX:
81             return NULL;
82         }
83     }
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         /* the old way of doing DIDs (default) */
93         cdir->d_did = htonl( CNID(lstp, 0) );
94 #endif /* USE_LASTDID */
95     }
96
97     if ((edir = dirinsert( vol, cdir ))) {
98         /* it's not possible with LASTDID
99            for CNID:
100            - someone else have moved the directory.
101            - it's a symlink inside the share.
102            - it's an ID reused, the old directory was deleted but not
103              the cnid record and the server've reused the inode for 
104              the new dir.
105            for HASH (we should get ride of HASH) 
106            - someone else have moved the directory.
107            - it's an ID reused as above
108            - it's a hash duplicate and we are in big trouble
109         */
110         dirfreename(edir);
111         edir->d_m_name = cdir->d_m_name;
112         edir->d_u_name = cdir->d_u_name;
113         free(cdir);
114         cdir = edir;
115         if (!cdir->d_parent || cdir->d_parent == dir)
116             return cdir;
117         /* the old was not in the same folder */
118         dirchildremove(cdir->d_parent, cdir);
119     }
120
121     /* parent/child directories */
122     cdir->d_parent = dir;
123     dirchildadd(dir, cdir);
124     return( cdir );
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     u_int32_t    sd_did;
133     int          sd_buflen;
134     char         *sd_buf;
135     char         *sd_last;
136     unsigned int sd_sindex;
137 };
138 #define SDBUFBRK        1024
139
140 static int enumerate_loop(struct dirent *de, char *mname, void *data)
141 {
142     struct savedir *sd = data; 
143     char *start, *end;
144     int  len;
145     
146     end = sd->sd_buf + sd->sd_buflen;
147     len = strlen(de->d_name);
148     *(sd->sd_last)++ = len;
149
150     if ( sd->sd_last + len + 2 > end ) {
151         char *buf;
152
153         start = sd->sd_buf;
154         if (!(buf = realloc( sd->sd_buf, sd->sd_buflen +SDBUFBRK )) ) {
155             LOG(log_error, logtype_afpd, "afp_enumerate: realloc: %s",
156                         strerror(errno) );
157             errno = ENOMEM;
158             return -1;
159         }
160         sd->sd_buf = buf;
161         sd->sd_buflen += SDBUFBRK;
162         sd->sd_last = ( sd->sd_last - start ) + sd->sd_buf;
163         end = sd->sd_buf + sd->sd_buflen;
164     }
165
166     memcpy( sd->sd_last, de->d_name, len + 1 );
167     sd->sd_last += len + 1;
168     return 0;
169 }
170
171 /* ----------------------------- 
172  * FIXME: 
173  * Doesn't work with dangling symlink
174  * ie: 
175  * - Move a folder with a dangling symlink in the trash
176  * - empty the trash
177  * afp_enumerate return an empty listing but offspring count != 0 in afp_getdirparams 
178  * and the Mac doesn't try to call afp_delete!
179  *
180  * Another option for symlink
181  * cf:
182  * http://sourceforge.net/tracker/index.php?func=detail&aid=461938&group_id=8642&atid=108642
183  * 
184 */
185 char *check_dirent(const struct vol *vol, char *name)
186 {
187     char *m_name = NULL;
188
189     if (!strcmp(name, "..") || !strcmp(name, "."))
190         return NULL;
191
192     if (!(validupath(vol, name)))
193         return NULL;
194
195     /* check for vetoed filenames */
196     if (veto_file(vol->v_veto, name))
197         return NULL;
198
199     /* now check against too big a file */
200     if (strlen(m_name = utompath(vol, name)) > vol->max_filename)
201         return NULL;
202
203     return m_name;
204 }
205
206 /* ----------------------------- */
207 int 
208 for_each_dirent(const struct vol *vol, char *name, dir_loop fn, void *data)
209 {
210     DIR             *dp;
211     struct dirent       *de;
212     char            *m_name;
213     int             ret;
214     
215     if (NULL == ( dp = opendir( name)) ) {
216         return -1;
217     }
218     ret = 0;
219     for ( de = readdir( dp ); de != NULL; de = readdir( dp )) {
220         if (!(m_name = check_dirent(vol, de->d_name)))
221             continue;
222
223         ret++;
224         if (fn && fn(de,m_name, data) < 0) {
225            closedir(dp);
226            return -1;
227         }
228     }
229     closedir(dp);
230     return ret;
231 }
232
233 /* ----------------------------- */
234 static int enumerate(obj, ibuf, ibuflen, rbuf, rbuflen, ext )
235 AFPObj       *obj;
236 char         *ibuf, *rbuf;
237 unsigned int ibuflen, *rbuflen;
238 int     ext;
239 {
240     static struct savedir       sd = { 0, 0, 0, NULL, NULL, 0 };
241     struct vol                  *vol;
242     struct dir                  *dir;
243     int                         did, ret, esz, len, first = 1;
244     char                        *data, *start;
245     u_int16_t                   vid, fbitmap, dbitmap, reqcnt, actcnt = 0;
246     u_int16_t                   temp16;
247     u_int32_t                   sindex, maxsz, sz = 0;
248     struct path                 *o_path;
249     struct path                 s_path;
250     int                         header;
251         
252     if ( sd.sd_buflen == 0 ) {
253         if (( sd.sd_buf = (char *)malloc( SDBUFBRK )) == NULL ) {
254             LOG(log_error, logtype_afpd, "afp_enumerate: malloc: %s", strerror(errno) );
255             *rbuflen = 0;
256             return AFPERR_MISC;
257         }
258         sd.sd_buflen = SDBUFBRK;
259     }
260
261     ibuf += 2;
262
263     memcpy( &vid, ibuf, sizeof( vid ));
264     ibuf += sizeof( vid );
265
266     if (NULL == ( vol = getvolbyvid( vid )) ) {
267         *rbuflen = 0;
268         return( AFPERR_PARAM );
269     }
270
271     memcpy( &did, ibuf, sizeof( did ));
272     ibuf += sizeof( did );
273
274     if (NULL == ( dir = dirlookup( vol, did )) ) {
275         *rbuflen = 0;
276         return (afp_errno == AFPERR_NOOBJ)?AFPERR_NODIR:afp_errno;
277     }
278
279     memcpy( &fbitmap, ibuf, sizeof( fbitmap ));
280     fbitmap = ntohs( fbitmap );
281     ibuf += sizeof( fbitmap );
282
283     memcpy( &dbitmap, ibuf, sizeof( dbitmap ));
284     dbitmap = ntohs( dbitmap );
285     ibuf += sizeof( dbitmap );
286
287     /* check for proper bitmaps -- the stuff in comments is for
288      * variable directory ids. */
289     if (!(fbitmap || dbitmap)
290             /*|| (fbitmap & (1 << FILPBIT_PDID)) ||
291               (dbitmap & (1 << DIRPBIT_PDID))*/) {
292         *rbuflen = 0;
293         return AFPERR_BITMAP;
294     }
295
296     memcpy( &reqcnt, ibuf, sizeof( reqcnt ));
297     reqcnt = ntohs( reqcnt );
298     ibuf += sizeof( reqcnt );
299
300     if (ext == 2) {
301         memcpy( &sindex, ibuf, sizeof( sindex ));
302         sindex = ntohl( sindex );
303         ibuf += sizeof( sindex );
304     }
305     else {
306         memcpy( &temp16, ibuf, sizeof( temp16 ));
307         sindex = ntohs( temp16 );
308         ibuf += sizeof( temp16 );
309     }
310
311     if (!sindex) {
312         *rbuflen = 0;
313         return AFPERR_PARAM ;
314     }
315
316     if (ext == 2) {
317         memcpy( &maxsz, ibuf, sizeof( maxsz ));
318         maxsz = ntohl( maxsz );
319         ibuf += sizeof( maxsz );
320     }
321     else {
322         memcpy( &temp16, ibuf, sizeof( temp16 ));
323         maxsz = ntohs( temp16 );
324         ibuf += sizeof( temp16 );
325     }
326     
327     header = (ext)?4:2;
328     header *=sizeof( u_char );
329     
330     maxsz = min(maxsz, *rbuflen);
331     o_path = cname( vol, dir, &ibuf );
332
333     if (afp_errno == AFPERR_NOOBJ) 
334         afp_errno = AFPERR_NODIR;
335
336     *rbuflen = 0;
337     if (NULL == o_path ) {
338         return afp_errno;
339     }
340     if ( *o_path->m_name != '\0') {
341         /* it's a file or it's a dir and extendir() was unable to chdir in it */
342         return (path_isadir(o_path))? afp_errno:AFPERR_BADTYPE ;
343     }
344
345     data = rbuf + 3 * sizeof( u_int16_t );
346     sz = 3 * sizeof( u_int16_t );       /* fbitmap, dbitmap, reqcount */
347
348     /*
349      * Read the directory into a pre-malloced buffer, stored
350      *          len <name> \0
351      * The end is indicated by a len of 0.
352      */
353     if ( sindex == 1 || curdir->d_did != sd.sd_did || vid != sd.sd_vid ) {
354         sd.sd_last = sd.sd_buf;
355         /* if dir was in the cache we don't have the inode */
356         if (( !o_path->st_valid && stat( ".", &o_path->st ) < 0 ) ||
357               (ret = for_each_dirent(vol, ".", enumerate_loop, (void *)&sd)) < 0) 
358         {
359             switch (errno) {
360             case EACCES:
361                 return AFPERR_ACCESS;
362             case ENOTDIR:
363                 return AFPERR_BADTYPE;
364             case ENOMEM:
365                 return AFPERR_MISC;
366             default:
367                 return AFPERR_NODIR;
368             }
369         }
370         curdir->ctime  = o_path->st.st_ctime; /* play safe */
371         curdir->offcnt = ret;
372         *sd.sd_last = 0;
373
374         sd.sd_last = sd.sd_buf;
375         sd.sd_sindex = 1;
376
377         sd.sd_vid = vid;
378         sd.sd_did = did;
379     }
380
381     /*
382      * Position sd_last as dictated by sindex.
383      */
384     if ( sindex < sd.sd_sindex ) {
385         sd.sd_sindex = 1;
386         sd.sd_last = sd.sd_buf;
387     }
388     while ( sd.sd_sindex < sindex ) {
389         len = *(sd.sd_last)++;
390         if ( len == 0 ) {
391             sd.sd_did = 0;      /* invalidate sd struct to force re-read */
392             return( AFPERR_NOOBJ );
393         }
394         sd.sd_last += len + 1;
395         sd.sd_sindex++;
396     }
397
398     while (( len = *(sd.sd_last)) != 0 ) {
399         /*
400          * If we've got all we need, send it.
401          */
402         if ( actcnt == reqcnt ) {
403             break;
404         }
405
406         /*
407          * Save the start position, in case we exceed the buffer
408          * limitation, and have to back up one.
409          */
410         start = sd.sd_last;
411         sd.sd_last++;
412
413         if (*sd.sd_last == 0) {
414             /* stat() already failed on this one */
415             sd.sd_last += len + 1;
416             continue;
417         }
418         s_path.u_name = sd.sd_last;
419         if (of_stat( &s_path) < 0 ) {
420             /*
421              * Somebody else plays with the dir, well it can be us with 
422             * "Empty Trash..."
423             */
424
425             /* so the next time it won't try to stat it again
426              * another solution would be to invalidate the cache with 
427              * sd.sd_did = 0 but if it's not ENOENT error it will start again
428              */
429             *sd.sd_last = 0;
430             sd.sd_last += len + 1;
431             curdir->offcnt--;           /* a little lie */
432             continue;
433         }
434
435         /*
436          * If a fil/dir is not a dir, it's a file. This is slightly
437          * inaccurate, since that means /dev/null is a file, /dev/printer
438          * is a file, etc.
439          */
440         if ( S_ISDIR(s_path.st.st_mode)) {
441             if ( dbitmap == 0 ) {
442                 sd.sd_last += len + 1;
443                 continue;
444             }
445             dir = dirsearch_byname(curdir, s_path.u_name);
446             if (!dir) {
447                 s_path.m_name = utompath(vol, s_path.u_name);
448                 if ((dir = adddir( vol, curdir, &s_path)) == NULL) {
449                     return AFPERR_MISC;
450                 }
451             }
452             else {
453                 s_path.m_name = NULL;
454             }
455             if (AFP_OK != ( ret = getdirparams(vol, dbitmap, &s_path, dir,
456                                      data + header , &esz ))) {
457                 return( ret );
458             }
459
460         } else {
461             if ( fbitmap == 0 ) {
462                 sd.sd_last += len + 1;
463                 continue;
464             }
465             s_path.m_name = utompath(vol, s_path.u_name);
466             if (AFP_OK != ( ret = getfilparams(vol, fbitmap, &s_path, curdir, 
467                                      data + header , &esz )) ) {
468                 return( ret );
469             }
470         }
471
472         /*
473          * Make sure entry is an even length, possibly with a null
474          * byte on the end.
475          */
476         if ( (esz + header) & 1 ) {
477             *(data + header + esz ) = '\0';
478             esz++;
479         }
480
481         /*
482          * Check if we've exceeded the size limit.
483          */
484         if ( maxsz < sz + esz + header) {
485             if (first) { /* maxsz can't hold a single reply */
486                 return AFPERR_PARAM;
487             }
488             sd.sd_last = start;
489             break;
490         }
491
492         if (first)
493             first = 0;
494
495         sz += esz + header;
496         if (ext) {
497             temp16 = htons( esz + header );
498             memcpy( data, &temp16, sizeof( temp16 ));
499             data += sizeof(temp16);
500         }
501         else {
502             *data++ = esz + header;
503         }
504
505         *data++ = S_ISDIR(s_path.st.st_mode) ? FILDIRBIT_ISDIR : FILDIRBIT_ISFILE;
506         if (ext) {
507              *data++ = 0;
508         }
509         data += esz;
510         actcnt++;
511         sd.sd_last += len + 1;
512     }
513
514     if ( actcnt == 0 ) {
515         sd.sd_did = 0;          /* invalidate sd struct to force re-read */
516         return( AFPERR_NOOBJ );
517     }
518     sd.sd_sindex = sindex + actcnt;
519
520     /*
521      * All done, fill in misc junk in rbuf
522      */
523     fbitmap = htons( fbitmap );
524     memcpy( rbuf, &fbitmap, sizeof( fbitmap ));
525     rbuf += sizeof( fbitmap );
526     dbitmap = htons( dbitmap );
527     memcpy( rbuf, &dbitmap, sizeof( dbitmap ));
528     rbuf += sizeof( dbitmap );
529     actcnt = htons( actcnt );
530     memcpy( rbuf, &actcnt, sizeof( actcnt ));
531     rbuf += sizeof( actcnt );
532     *rbuflen = sz;
533     return( AFP_OK );
534 }
535
536 /* ----------------------------- */
537 int afp_enumerate(obj, ibuf, ibuflen, rbuf, rbuflen )
538 AFPObj       *obj;
539 char         *ibuf, *rbuf;
540 unsigned int ibuflen, *rbuflen;
541 {
542     return enumerate(obj, ibuf,ibuflen ,rbuf,rbuflen , 0);
543 }
544
545 /* ----------------------------- */
546 int afp_enumerate_ext(obj, ibuf, ibuflen, rbuf, rbuflen )
547 AFPObj       *obj;
548 char         *ibuf, *rbuf;
549 unsigned int ibuflen, *rbuflen;
550 {
551     return enumerate(obj, ibuf,ibuflen ,rbuf,rbuflen , 1);
552 }
553
554 /* ----------------------------- */
555 int afp_enumerate_ext2(obj, ibuf, ibuflen, rbuf, rbuflen )
556 AFPObj       *obj;
557 char         *ibuf, *rbuf;
558 unsigned int ibuflen, *rbuflen;
559 {
560     return enumerate(obj, ibuf,ibuflen ,rbuf,rbuflen , 2);
561 }
562