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