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