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