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