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