2 * $Id: enumerate.c,v 1.23 2002-10-05 14:04:47 didg Exp $
4 * Copyright (c) 1990,1993 Regents of The University of Michigan.
5 * All Rights Reserved. See COPYRIGHT.
10 #endif /* HAVE_CONFIG_H */
18 #include <atalk/logger.h>
19 #include <sys/types.h>
22 #include <sys/param.h>
24 #include <netatalk/endian.h>
25 #include <atalk/afp.h>
26 #include <atalk/adouble.h>
28 #include <atalk/cnid.h>
31 #include "directory.h"
37 #define min(a,b) ((a)<(b)?(a):(b))
40 adddir( vol, dir, name, namlen, upath, upathlen, st )
47 struct dir *cdir, *edir;
48 #if AD_VERSION > AD_VERSION1
50 #endif /* AD_VERSION > AD_VERSION1 */
53 struct stat lst, *lstp;
54 #endif /* USE_LASTDID */
56 if ((cdir = dirnew(namlen + 1)) == NULL) {
57 LOG(log_error, logtype_afpd, "adddir: malloc: %s", strerror(errno) );
60 strcpy( cdir->d_name, name );
61 cdir->d_name[namlen] = '\0';
65 #if AD_VERSION > AD_VERSION1
66 /* look in AD v2 header */
67 memset(&ad, 0, sizeof(ad));
68 if (ad_open(upath, ADFLAGS_HF|ADFLAGS_DIR, O_RDONLY, 0, &ad) >= 0) {
69 /* if we can parse the AppleDouble header, retrieve the DID entry into cdir->d_did */
70 memcpy(&cdir->d_did, ad_entry(&ad, ADEID_DID), sizeof(cdir->d_did));
71 ad_close(&ad, ADFLAGS_HF);
73 #endif /* AD_VERSION */
77 cdir->d_did = cnid_add(vol->v_db, st, dir->d_did, upath,
78 upathlen, cdir->d_did);
79 /* Fail out if things go bad with CNID. */
80 if (cdir->d_did == CNID_INVALID) {
83 LOG(log_error, logtype_afpd, "adddir: Incorrect parameters passed to cnid_add");
93 if (cdir->d_did == 0) {
95 /* last way of doing DIDs */
96 cdir->d_did = htonl( vol->v_lastdid++ );
97 #else /* USE_LASTDID */
98 lstp = lstat(upath, &lst) < 0 ? st : &lst;
99 /* the old way of doing DIDs (default) */
100 cdir->d_did = htonl( CNID(lstp, 0) );
101 #endif /* USE_LASTDID */
104 if ((edir = dirinsert( vol, cdir ))) {
107 if (strcmp(edir->d_name, cdir->d_name)) {
108 LOG(log_info, logtype_afpd, "WARNING: DID conflict for '%s' and '%s'. Are these the same file?", edir->d_name, cdir->d_name);
115 edir->d_name = cdir->d_name;
120 /* parent/child directories */
121 cdir->d_parent = dir;
122 dirchildadd(dir, cdir);
127 * Struct to save directory reading context in. Used to prevent
128 * O(n^2) searches on a directory.
138 #define SDBUFBRK 1024
140 int afp_enumerate(obj, ibuf, ibuflen, rbuf, rbuflen )
143 int ibuflen, *rbuflen;
146 static struct savedir sd = { 0, 0, 0, NULL, NULL, 0 };
151 int did, ret, esz, len, first = 1;
152 char *path, *data, *end, *start;
153 u_int16_t vid, fbitmap, dbitmap, reqcnt, actcnt = 0;
154 u_int16_t sindex, maxsz, sz = 0;
156 if ( sd.sd_buflen == 0 ) {
157 if (( sd.sd_buf = (char *)malloc( SDBUFBRK )) == NULL ) {
158 LOG(log_error, logtype_afpd, "afp_enumerate: malloc: %s", strerror(errno) );
162 sd.sd_buflen = SDBUFBRK;
167 memcpy( &vid, ibuf, sizeof( vid ));
168 ibuf += sizeof( vid );
170 if (( vol = getvolbyvid( vid )) == NULL ) {
172 return( AFPERR_PARAM );
175 memcpy( &did, ibuf, sizeof( did ));
176 ibuf += sizeof( did );
178 if (( dir = dirlookup( vol, did )) == NULL ) {
180 return( AFPERR_NODIR );
183 memcpy( &fbitmap, ibuf, sizeof( fbitmap ));
184 fbitmap = ntohs( fbitmap );
185 ibuf += sizeof( fbitmap );
187 memcpy( &dbitmap, ibuf, sizeof( dbitmap ));
188 dbitmap = ntohs( dbitmap );
189 ibuf += sizeof( dbitmap );
191 /* check for proper bitmaps -- the stuff in comments is for
192 * variable directory ids. */
193 if (!(fbitmap || dbitmap)
194 /*|| (fbitmap & (1 << FILPBIT_PDID)) ||
195 (dbitmap & (1 << DIRPBIT_PDID))*/) {
197 return AFPERR_BITMAP;
200 memcpy( &reqcnt, ibuf, sizeof( reqcnt ));
201 reqcnt = ntohs( reqcnt );
202 ibuf += sizeof( reqcnt );
204 memcpy( &sindex, ibuf, sizeof( sindex ));
205 sindex = ntohs( sindex );
206 ibuf += sizeof( sindex );
208 memcpy( &maxsz, ibuf, sizeof( maxsz ));
209 maxsz = ntohs( maxsz );
210 ibuf += sizeof( maxsz );
212 maxsz = min(maxsz, *rbuflen);
214 if (( path = cname( vol, dir, &ibuf )) == NULL ) {
216 return( AFPERR_NODIR );
218 data = rbuf + 3 * sizeof( u_int16_t );
219 sz = 3 * sizeof( u_int16_t );
222 * Read the directory into a pre-malloced buffer, stored
224 * The end is indicated by a len of 0.
226 if ( sindex == 1 || curdir->d_did != sd.sd_did || vid != sd.sd_vid ) {
227 sd.sd_last = sd.sd_buf;
229 if (( dp = opendir( mtoupath(vol, path ))) == NULL ) {
231 return (errno == ENOTDIR) ? AFPERR_BADTYPE : AFPERR_NODIR;
234 end = sd.sd_buf + sd.sd_buflen;
235 for ( de = readdir( dp ); de != NULL; de = readdir( dp )) {
236 if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, "."))
239 if (!(validupath(vol, de->d_name)))
242 /* check for vetoed filenames */
243 if (veto_file(vol->v_veto, de->d_name))
246 /* now check against too big a file */
247 if (strlen(utompath(vol, de->d_name)) > MACFILELEN)
250 len = strlen(de->d_name);
251 *(sd.sd_last)++ = len;
253 if ( sd.sd_last + len + 2 > end ) {
257 if ((buf = (char *) realloc( sd.sd_buf, sd.sd_buflen +
258 SDBUFBRK )) == NULL ) {
259 LOG(log_error, logtype_afpd, "afp_enumerate: realloc: %s",
266 sd.sd_buflen += SDBUFBRK;
267 sd.sd_last = ( sd.sd_last - start ) + sd.sd_buf;
268 end = sd.sd_buf + sd.sd_buflen;
271 memcpy( sd.sd_last, de->d_name, len + 1 );
272 sd.sd_last += len + 1;
276 sd.sd_last = sd.sd_buf;
285 * Position sd_last as dictated by sindex.
287 if ( sindex < sd.sd_sindex ) {
289 sd.sd_last = sd.sd_buf;
291 while ( sd.sd_sindex < sindex ) {
292 len = *(sd.sd_last)++;
294 sd.sd_did = -1; /* invalidate sd struct to force re-read */
296 return( AFPERR_NOOBJ );
298 sd.sd_last += len + 1;
302 while (( len = *(sd.sd_last)) != 0 ) {
304 * If we've got all we need, send it.
306 if ( actcnt == reqcnt ) {
311 * Save the start position, in case we exceed the buffer
312 * limitation, and have to back up one.
317 if (*sd.sd_last == 0) {
318 /* stat() already failed on this one */
319 sd.sd_last += len + 1;
323 if (stat( sd.sd_last, &st ) < 0 ) {
325 * Somebody else plays with the dir, well it can be us with
329 /* so the next time it won't try to stat it again
330 * another solution would be to invalidate the cache with
331 * sd.sd_did = -1 but if it's not ENOENT error it will start again
334 sd.sd_last += len + 1;
339 * If a fil/dir is not a dir, it's a file. This is slightly
340 * inaccurate, since that means /dev/null is a file, /dev/printer
343 if ( S_ISDIR(st.st_mode)) {
344 if ( dbitmap == 0 ) {
345 sd.sd_last += len + 1;
348 path = utompath(vol, sd.sd_last);
349 dir = curdir->d_child;
351 if ( strcmp( dir->d_name, path ) == 0 ) {
354 dir = (dir == curdir->d_child->d_prev) ? NULL : dir->d_next;
356 if (!dir && ((dir = adddir( vol, curdir, path, strlen( path ),
357 sd.sd_last, len, &st)) == NULL)) {
363 if (( ret = getdirparams(vol, dbitmap, sd.sd_last, dir,
364 &st, data + 2 * sizeof( u_char ), &esz )) != AFP_OK ) {
370 if ( fbitmap == 0 ) {
371 sd.sd_last += len + 1;
375 if (( ret = getfilparams(vol, fbitmap, utompath(vol, sd.sd_last),
376 curdir, &st, data + 2 * sizeof( u_char ), &esz )) !=
384 * Make sure entry is an even length, possibly with a null
388 *(data + 2 * sizeof( u_char ) + esz ) = '\0';
393 * Check if we've exceeded the size limit.
395 if ( maxsz < sz + esz + 2 * sizeof( u_char )) {
396 if (first) { /* maxsz can't hold a single reply */
407 sz += esz + 2 * sizeof( u_char );
408 *data++ = esz + 2 * sizeof( u_char );
409 *data++ = S_ISDIR(st.st_mode) ? FILDIRBIT_ISDIR : FILDIRBIT_ISFILE;
412 sd.sd_last += len + 1;
417 sd.sd_did = -1; /* invalidate sd struct to force re-read */
418 return( AFPERR_NOOBJ );
420 sd.sd_sindex = sindex + actcnt;
423 * All done, fill in misc junk in rbuf
425 fbitmap = htons( fbitmap );
426 memcpy( rbuf, &fbitmap, sizeof( fbitmap ));
427 rbuf += sizeof( fbitmap );
428 dbitmap = htons( dbitmap );
429 memcpy( rbuf, &dbitmap, sizeof( dbitmap ));
430 rbuf += sizeof( dbitmap );
431 actcnt = htons( actcnt );
432 memcpy( rbuf, &actcnt, sizeof( actcnt ));
433 rbuf += sizeof( actcnt );