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