]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/enumerate.c
bugfix: adddir() if an ID already exist, remove it from its old location in the
[netatalk.git] / etc / afpd / enumerate.c
1 /*
2  * $Id: enumerate.c,v 1.31 2003-01-19 00:04:37 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 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             /* the old was not in the same folder */
114             dirchildremove(cdir->d_parent, cdir);
115         }
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( AFPERR_NODIR );
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
329     if (NULL == ( o_path = cname( vol, dir, &ibuf )) ) {
330         *rbuflen = 0;
331         return( AFPERR_NODIR );
332     }
333
334     if ( *o_path->m_name != '\0') {
335         *rbuflen = 0;
336         return( AFPERR_BADTYPE );
337     }
338
339     data = rbuf + 3 * sizeof( u_int16_t );
340     sz = 3 * sizeof( u_int16_t );       /* fbitmap, dbitmap, reqcount */
341
342     /*
343      * Read the directory into a pre-malloced buffer, stored
344      *          len <name> \0
345      * The end is indicated by a len of 0.
346      */
347     if ( sindex == 1 || curdir->d_did != sd.sd_did || vid != sd.sd_vid ) {
348         sd.sd_last = sd.sd_buf;
349         if ( !o_path->st_valid && stat( ".", &o_path->st ) < 0 ) {
350             switch (errno) {
351             case EACCES:
352                 return AFPERR_ACCESS;
353             case ENOTDIR:
354                 return AFPERR_BADTYPE;
355             case ENOMEM:
356                 return AFPERR_MISC;
357             default:
358                 return AFPERR_NODIR;
359             }
360         }
361         curdir->ctime  = o_path->st.st_ctime; /* play safe */
362         if ((ret = for_each_dirent(vol, ".", enumerate_loop, (void *)&sd)) < 0) {
363             *rbuflen = 0;
364             switch (errno) {
365             case EACCES:
366                 return AFPERR_ACCESS;
367             case ENOTDIR:
368                 return AFPERR_BADTYPE;
369             case ENOMEM:
370                 return AFPERR_MISC;
371             default:
372                 return AFPERR_NODIR;
373             }
374         }
375         curdir->offcnt = ret;
376         *sd.sd_last = 0;
377
378         sd.sd_last = sd.sd_buf;
379         sd.sd_sindex = 1;
380
381         sd.sd_vid = vid;
382         sd.sd_did = did;
383     }
384
385     /*
386      * Position sd_last as dictated by sindex.
387      */
388     if ( sindex < sd.sd_sindex ) {
389         sd.sd_sindex = 1;
390         sd.sd_last = sd.sd_buf;
391     }
392     while ( sd.sd_sindex < sindex ) {
393         len = *(sd.sd_last)++;
394         if ( len == 0 ) {
395             sd.sd_did = 0;      /* invalidate sd struct to force re-read */
396             *rbuflen = 0;
397             return( AFPERR_NOOBJ );
398         }
399         sd.sd_last += len + 1;
400         sd.sd_sindex++;
401     }
402
403     while (( len = *(sd.sd_last)) != 0 ) {
404         /*
405          * If we've got all we need, send it.
406          */
407         if ( actcnt == reqcnt ) {
408             break;
409         }
410
411         /*
412          * Save the start position, in case we exceed the buffer
413          * limitation, and have to back up one.
414          */
415         start = sd.sd_last;
416         sd.sd_last++;
417
418         if (*sd.sd_last == 0) {
419             /* stat() already failed on this one */
420             sd.sd_last += len + 1;
421             continue;
422         }
423         s_path.u_name = sd.sd_last;
424         if (of_stat( &s_path) < 0 ) {
425             /*
426              * Somebody else plays with the dir, well it can be us with 
427             * "Empty Trash..."
428             */
429
430             /* so the next time it won't try to stat it again
431              * another solution would be to invalidate the cache with 
432              * sd.sd_did = 0 but if it's not ENOENT error it will start again
433              */
434             *sd.sd_last = 0;
435             sd.sd_last += len + 1;
436             curdir->offcnt--;           /* a little lie */
437             continue;
438         }
439
440         /*
441          * If a fil/dir is not a dir, it's a file. This is slightly
442          * inaccurate, since that means /dev/null is a file, /dev/printer
443          * is a file, etc.
444          */
445         if ( S_ISDIR(s_path.st.st_mode)) {
446             if ( dbitmap == 0 ) {
447                 sd.sd_last += len + 1;
448                 continue;
449             }
450             dir = curdir->d_child;
451             s_path.m_name = NULL;
452             while (dir) {
453                 if ( strcmp( dir->d_u_name, s_path.u_name ) == 0 ) {
454                     break;
455                 }
456                 dir = (dir == curdir->d_child->d_prev) ? NULL : dir->d_next;
457             }
458             if (!dir) {
459                 s_path.m_name = utompath(vol, s_path.u_name);
460                 if ((dir = adddir( vol, curdir, &s_path)) == NULL) {
461                     *rbuflen = 0;
462                     return AFPERR_MISC;
463                 }
464             }
465
466             if (( ret = getdirparams(vol, dbitmap, &s_path, dir,
467                                      data + header , &esz )) != AFP_OK ) {
468                 *rbuflen = 0;
469                 return( ret );
470             }
471
472         } else {
473             if ( fbitmap == 0 ) {
474                 sd.sd_last += len + 1;
475                 continue;
476             }
477             s_path.m_name = utompath(vol, s_path.u_name);
478             if (( ret = getfilparams(vol, fbitmap, &s_path, curdir, 
479                                      data + header , &esz )) != AFP_OK ) {
480                 *rbuflen = 0;
481                 return( ret );
482             }
483         }
484
485         /*
486          * Make sure entry is an even length, possibly with a null
487          * byte on the end.
488          */
489         if ( (esz + header) & 1 ) {
490             *(data + header + esz ) = '\0';
491             esz++;
492         }
493
494         /*
495          * Check if we've exceeded the size limit.
496          */
497         if ( maxsz < sz + esz + header) {
498             if (first) { /* maxsz can't hold a single reply */
499                 *rbuflen = 0;
500                 return AFPERR_PARAM;
501             }
502             sd.sd_last = start;
503             break;
504         }
505
506         if (first)
507             first = 0;
508
509         sz += esz + header;
510         if (ext) {
511             temp16 = htons( esz + header );
512             memcpy( data, &temp16, sizeof( temp16 ));
513             data += sizeof(temp16);
514         }
515         else {
516             *data++ = esz + header;
517         }
518
519         *data++ = S_ISDIR(s_path.st.st_mode) ? FILDIRBIT_ISDIR : FILDIRBIT_ISFILE;
520         if (ext) {
521              *data++ = 0;
522         }
523         data += esz;
524         actcnt++;
525         sd.sd_last += len + 1;
526     }
527
528     if ( actcnt == 0 ) {
529         *rbuflen = 0;
530         sd.sd_did = 0;          /* invalidate sd struct to force re-read */
531         return( AFPERR_NOOBJ );
532     }
533     sd.sd_sindex = sindex + actcnt;
534
535     /*
536      * All done, fill in misc junk in rbuf
537      */
538     fbitmap = htons( fbitmap );
539     memcpy( rbuf, &fbitmap, sizeof( fbitmap ));
540     rbuf += sizeof( fbitmap );
541     dbitmap = htons( dbitmap );
542     memcpy( rbuf, &dbitmap, sizeof( dbitmap ));
543     rbuf += sizeof( dbitmap );
544     actcnt = htons( actcnt );
545     memcpy( rbuf, &actcnt, sizeof( actcnt ));
546     rbuf += sizeof( actcnt );
547     *rbuflen = sz;
548     return( AFP_OK );
549 }
550
551 /* ----------------------------- */
552 int afp_enumerate(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 , 0);
558 }
559
560 /* ----------------------------- */
561 int afp_enumerate_ext(obj, ibuf, ibuflen, rbuf, rbuflen )
562 AFPObj       *obj;
563 char         *ibuf, *rbuf;
564 unsigned int ibuflen, *rbuflen;
565 {
566     return enumerate(obj, ibuf,ibuflen ,rbuf,rbuflen , 1);
567 }
568
569 /* ----------------------------- */
570 int afp_enumerate_ext2(obj, ibuf, ibuflen, rbuf, rbuflen )
571 AFPObj       *obj;
572 char         *ibuf, *rbuf;
573 unsigned int ibuflen, *rbuflen;
574 {
575     return enumerate(obj, ibuf,ibuflen ,rbuf,rbuflen , 2);
576 }
577