]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/directory.c
fix a SIGSEGV on ad_setid if d_parent is NULL
[netatalk.git] / etc / afpd / directory.c
1 /*
2  * $Id: directory.c,v 1.71.2.4.2.8 2004-02-09 23:49:31 bfernhomberg Exp $
3  *
4  * Copyright (c) 1990,1993 Regents of The University of Michigan.
5  * All Rights Reserved.  See COPYRIGHT.
6  *
7  * 19 jan 2000 implemented red-black trees for directory lookups
8  * (asun@cobalt.com).
9  */
10
11 #ifdef HAVE_CONFIG_H
12 #include "config.h"
13 #endif /* HAVE_CONFIG_H */
14
15 /* STDC check */
16 #if STDC_HEADERS
17 #include <string.h>
18 #else /* STDC_HEADERS */
19 #ifndef HAVE_STRCHR
20 #define strchr index
21 #define strrchr index
22 #endif /* HAVE_STRCHR */
23 char *strchr (), *strrchr ();
24 #ifndef HAVE_MEMCPY
25 #define memcpy(d,s,n) bcopy ((s), (d), (n))
26 #define memmove(d,s,n) bcopy ((s), (d), (n))
27 #endif /* ! HAVE_MEMCPY */
28 #endif /* STDC_HEADERS */
29 #ifdef HAVE_STRINGS_H
30 #include <strings.h>
31 #endif
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <dirent.h>
35 #ifdef HAVE_FCNTL_H
36 #include <fcntl.h>
37 #endif /* HAVE_FCNTL_H */
38 #include <grp.h>
39 #include <pwd.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <sys/time.h>
43 #include <sys/param.h>
44 #include <errno.h>
45 #include <utime.h>
46 #include <netatalk/endian.h>
47 #include <atalk/adouble.h>
48 #include <atalk/afp.h>
49 #include <atalk/util.h>
50 #include <atalk/cnid.h>
51 #include <atalk/logger.h>
52
53 #include "directory.h"
54 #include "desktop.h"
55 #include "volume.h"
56 #include "fork.h"
57 #include "file.h"
58 #include "filedir.h"
59 #include "globals.h"
60 #include "unix.h"
61 #include "mangle.h"
62
63 struct dir      *curdir;
64 int             afp_errno;
65
66 #define SENTINEL (&sentinel)
67 static struct dir sentinel = { SENTINEL, SENTINEL, NULL, DIRTREE_COLOR_BLACK,
68                                  NULL, NULL, NULL, NULL, NULL, 0, 0, 
69                                  0, 0, NULL, NULL};
70 static struct dir rootpar  = { SENTINEL, SENTINEL, NULL, 0,
71                                  NULL, NULL, NULL, NULL, NULL, 0, 0, 
72                                  0, 0, NULL, NULL};
73
74 /* (from IM: Toolbox Essentials)
75  * dirFinderInfo (DInfo) fields:
76  * field        bytes
77  * frRect       8    folder's window rectangle
78  * frFlags      2    flags
79  * frLocation   4    folder's location in window
80  * frView       2    folder's view (default == closedView (256))
81  *
82  * extended dirFinderInfo (DXInfo) fields:
83  * frScroll     4    scroll position
84  * frOpenChain: 4    directory ID chain of open folders
85  * frScript:    1    script flag and code
86  * frXFlags:    1    reserved
87  * frComment:   2    comment ID
88  * frPutAway:   4    home directory ID
89  */
90
91 /*
92  * redid did assignment for directories. now we use red-black trees.
93  * how exciting.
94  */
95 struct dir *
96             dirsearch( vol, did )
97             const struct vol    *vol;
98 u_int32_t       did;
99 {
100     struct dir  *dir;
101
102
103     /* check for 0 did */
104     if (!did) {
105         afp_errno = AFPERR_PARAM;
106         return NULL;
107     }
108     if ( did == DIRDID_ROOT_PARENT ) {
109         if (!rootpar.d_did)
110             rootpar.d_did = DIRDID_ROOT_PARENT;
111         rootpar.d_child = vol->v_dir;
112         return( &rootpar );
113     }
114
115     dir = vol->v_root;
116     afp_errno = AFPERR_NOOBJ;
117     while ( dir != SENTINEL ) {
118         if (dir->d_did == did)
119             return dir->d_m_name ? dir : NULL;
120         dir = (dir->d_did > did) ? dir->d_left : dir->d_right;
121     }
122     return NULL;
123 }
124
125 /* ------------------- */
126 #ifdef ATACC
127 int path_isadir(struct path *o_path)
128 {
129     return o_path->dir != NULL;
130 #if 0
131     return o_path->m_name == '\0' || /* we are in a it */
132            !o_path->st_valid ||      /* in cache but we can't chdir in it */ 
133            (!o_path->st_errno && S_ISDIR(o_path->st.st_mode)); /* not in cache an can't chdir */
134 #endif
135 }
136 #endif
137
138 int get_afp_errno(const int param)
139 {
140     if (afp_errno != AFPERR_DID1)
141         return afp_errno;
142     return param;
143 }
144
145 /* ------------------- */
146 struct dir *
147             dirsearch_byname( cdir, name )
148             struct dir *cdir;
149             const char  *name;
150 {
151 struct dir *dir;
152
153     if (!strcmp(name, "."))
154         return cdir;
155     dir = cdir->d_child;
156     while (dir) {
157         if ( strcmp( dir->d_u_name, name ) == 0 ) {
158             break;
159         }
160         dir = (dir == cdir->d_child->d_prev) ? NULL : dir->d_next;
161     }
162     return dir;
163 }            
164
165 /* -----------------------------------------
166  * if did is not in the cache resolve it with cnid 
167  * 
168  * FIXME
169  * OSX call it with bogus id, ie file ID not folder ID,
170  * and we are really bad in this case.
171  */
172 struct dir *
173             dirlookup( vol, did )
174             const struct vol    *vol;
175 u_int32_t       did;
176 {
177     struct dir   *ret;
178     char         *upath;
179     cnid_t       id, cnid;
180     static char  path[MAXPATHLEN + 1];
181     size_t len,  pathlen;
182     char         *ptr;
183     static char  buffer[12 + MAXPATHLEN + 1];
184     int          buflen = 12 + MAXPATHLEN + 1;
185     char         *mpath;
186     int          utf8;
187     size_t       maxpath;
188     
189     ret = dirsearch(vol, did);
190     if (ret != NULL || afp_errno == AFPERR_PARAM)
191         return ret;
192
193     utf8 = utf8_encoding();
194     maxpath = (utf8)?MAXPATHLEN -7:255;
195     id = did;
196     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen)) ) {
197         afp_errno = AFPERR_NOOBJ;
198         return NULL;
199     }
200     ptr = path + MAXPATHLEN;
201     if (NULL == ( mpath = utompath(vol, upath, did, utf8) ) ) {
202         afp_errno = AFPERR_NOOBJ;
203         return NULL;
204     }
205     len = strlen(mpath);
206     pathlen = len;          /* no 0 in the last part */
207     len++;
208     strcpy(ptr - len, mpath);
209     ptr -= len;
210     while (1) {
211         ret = dirsearch(vol,id);
212         if (ret != NULL) {
213             break;
214         }
215         cnid = id;
216         if ( NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen))
217              ||
218              NULL == (mpath = utompath(vol, upath, cnid, utf8))
219         ) {
220             afp_errno = AFPERR_NOOBJ;
221             return NULL;
222         }
223
224         len = strlen(mpath) + 1;
225         pathlen += len;
226         if (pathlen > maxpath) {
227             afp_errno = AFPERR_PARAM;
228             return NULL;
229         }
230         strcpy(ptr - len, mpath);
231         ptr -= len;
232     }
233     
234     /* fill the cache, another place where we know about the path type */
235     if (utf8) {
236         u_int16_t temp16;
237         u_int32_t temp;
238
239         ptr -= 2; 
240         temp16 = htons(pathlen);
241         memcpy(ptr, &temp16, sizeof(temp16));
242
243         temp = htonl(kTextEncodingUTF8);
244         ptr -= 4; 
245         memcpy(ptr, &temp, sizeof(temp));
246         ptr--;
247         *ptr = 3;
248     }
249     else {
250         ptr--;
251         *ptr = (unsigned char)pathlen;
252         ptr--;
253         *ptr = 2;
254     }
255     /* cname is not efficient */
256     if (cname( vol, ret, &ptr ) == NULL )
257         return NULL;
258         
259     return dirsearch(vol, did);
260 }
261
262 /* --------------------------- */
263 /* rotate the tree to the left */
264 static void dir_leftrotate(vol, dir)
265 struct vol *vol;
266 struct dir *dir;
267 {
268     struct dir *right = dir->d_right;
269
270     /* whee. move the right's left tree into dir's right tree */
271     dir->d_right = right->d_left;
272     if (right->d_left != SENTINEL)
273         right->d_left->d_back = dir;
274
275     if (right != SENTINEL) {
276         right->d_back = dir->d_back;
277         right->d_left = dir;
278     }
279
280     if (!dir->d_back) /* no parent. move the right tree to the top. */
281         vol->v_root = right;
282     else if (dir == dir->d_back->d_left) /* we were on the left */
283         dir->d_back->d_left = right;
284     else
285         dir->d_back->d_right = right; /* we were on the right */
286
287     /* re-insert dir on the left tree */
288     if (dir != SENTINEL)
289         dir->d_back = right;
290 }
291
292
293
294 /* rotate the tree to the right */
295 static void dir_rightrotate(vol, dir)
296 struct vol *vol;
297 struct dir *dir;
298 {
299     struct dir *left = dir->d_left;
300
301     /* whee. move the left's right tree into dir's left tree */
302     dir->d_left = left->d_right;
303     if (left->d_right != SENTINEL)
304         left->d_right->d_back = dir;
305
306     if (left != SENTINEL) {
307         left->d_back = dir->d_back;
308         left->d_right = dir;
309     }
310
311     if (!dir->d_back) /* no parent. move the left tree to the top. */
312         vol->v_root = left;
313     else if (dir == dir->d_back->d_right) /* we were on the right */
314         dir->d_back->d_right = left;
315     else
316         dir->d_back->d_left = left; /* we were on the left */
317
318     /* re-insert dir on the right tree */
319     if (dir != SENTINEL)
320         dir->d_back = left;
321 }
322
323 #if 0
324 /* recolor after a removal */
325 static struct dir *dir_rmrecolor(vol, dir)
326             struct vol *vol;
327 struct dir *dir;
328 {
329     struct dir *leaf;
330
331     while ((dir != vol->v_root) && (dir->d_color == DIRTREE_COLOR_BLACK)) {
332         /* are we on the left tree? */
333         if (dir == dir->d_back->d_left) {
334             leaf = dir->d_back->d_right; /* get right side */
335             if (leaf->d_color == DIRTREE_COLOR_RED) {
336                 /* we're red. we need to change to black. */
337                 leaf->d_color = DIRTREE_COLOR_BLACK;
338                 dir->d_back->d_color = DIRTREE_COLOR_RED;
339                 dir_leftrotate(vol, dir->d_back);
340                 leaf = dir->d_back->d_right;
341             }
342
343             /* right leaf has black end nodes */
344             if ((leaf->d_left->d_color == DIRTREE_COLOR_BLACK) &&
345                     (leaf->d_right->d_color = DIRTREE_COLOR_BLACK)) {
346                 leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
347                 dir = dir->d_back; /* ascend */
348             } else {
349                 if (leaf->d_right->d_color == DIRTREE_COLOR_BLACK) {
350                     leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
351                     leaf->d_color = DIRTREE_COLOR_RED;
352                     dir_rightrotate(vol, leaf);
353                     leaf = dir->d_back->d_right;
354                 }
355                 leaf->d_color = dir->d_back->d_color;
356                 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
357                 leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
358                 dir_leftrotate(vol, dir->d_back);
359                 dir = vol->v_root;
360             }
361         } else { /* right tree */
362             leaf = dir->d_back->d_left; /* left tree */
363             if (leaf->d_color == DIRTREE_COLOR_RED) {
364                 leaf->d_color = DIRTREE_COLOR_BLACK;
365                 dir->d_back->d_color = DIRTREE_COLOR_RED;
366                 dir_rightrotate(vol, dir->d_back);
367                 leaf = dir->d_back->d_left;
368             }
369
370             /* left leaf has black end nodes */
371             if ((leaf->d_right->d_color == DIRTREE_COLOR_BLACK) &&
372                     (leaf->d_left->d_color = DIRTREE_COLOR_BLACK)) {
373                 leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
374                 dir = dir->d_back; /* ascend */
375             } else {
376                 if (leaf->d_left->d_color == DIRTREE_COLOR_BLACK) {
377                     leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
378                     leaf->d_color = DIRTREE_COLOR_RED;
379                     dir_leftrotate(vol, leaf);
380                     leaf = dir->d_back->d_left;
381                 }
382                 leaf->d_color = dir->d_back->d_color;
383                 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
384                 leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
385                 dir_rightrotate(vol, dir->d_back);
386                 dir = vol->v_root;
387             }
388         }
389     }
390     dir->d_color = DIRTREE_COLOR_BLACK;
391
392     return dir;
393 }
394 #endif /* 0 */
395
396
397 /* remove the node from the tree. this is just like insertion, but
398  * different. actually, it has to worry about a bunch of things that
399  * insertion doesn't care about. */
400 static void dir_remove( vol, dir )
401 struct vol      *vol;
402 struct dir      *dir;
403 {
404 #ifdef REMOVE_NODES
405     struct ofork *of, *last;
406     struct dir *node, *leaf;
407 #endif /* REMOVE_NODES */
408
409     if (!dir || (dir == SENTINEL))
410         return;
411
412     /* i'm not sure if it really helps to delete stuff. */
413 #ifndef REMOVE_NODES 
414     dirfreename(dir);
415     dir->d_m_name = NULL;
416     dir->d_u_name = NULL;
417 #else /* ! REMOVE_NODES */
418
419     /* go searching for a node with at most one child */
420     if ((dir->d_left == SENTINEL) || (dir->d_right == SENTINEL)) {
421         node = dir;
422     } else {
423         node = dir->d_right;
424         while (node->d_left != SENTINEL)
425             node = node->d_left;
426     }
427
428     /* get that child */
429     leaf = (node->d_left != SENTINEL) ? node->d_left : node->d_right;
430
431     /* detach node */
432     leaf->d_back = node->d_back;
433     if (!node->d_back) {
434         vol->v_root = leaf;
435     } else if (node == node->d_back->d_left) { /* left tree */
436         node->d_back->d_left = leaf;
437     } else {
438         node->d_back->d_right = leaf;
439     }
440
441     /* we want to free node, but we also want to free the data in dir.
442     * currently, that's d_name and the directory traversal bits.
443     * we just copy the necessary bits and then fix up all the
444     * various pointers to the directory. needless to say, there are
445     * a bunch of places that store the directory struct. */
446     if (node != dir) {
447         struct dir save, *tmp;
448
449         memcpy(&save, dir, sizeof(save));
450         memcpy(dir, node, sizeof(struct dir));
451
452         /* restore the red-black bits */
453         dir->d_left = save.d_left;
454         dir->d_right = save.d_right;
455         dir->d_back = save.d_back;
456         dir->d_color = save.d_color;
457
458         if (node == vol->v_dir) {/* we may need to fix up this pointer */
459             vol->v_dir = dir;
460             rootpar.d_child = vol->v_dir;
461         } else {
462             /* if we aren't the root directory, we have parents and
463             * siblings to worry about */
464             if (dir->d_parent->d_child == node)
465                 dir->d_parent->d_child = dir;
466             dir->d_next->d_prev = dir;
467             dir->d_prev->d_next = dir;
468         }
469
470         /* fix up children. */
471         tmp = dir->d_child;
472         while (tmp) {
473             tmp->d_parent = dir;
474             tmp = (tmp == dir->d_child->d_prev) ? NULL : tmp->d_next;
475         }
476
477         if (node == curdir) /* another pointer to fixup */
478             curdir = dir;
479
480         /* we also need to fix up oforks. bleah */
481         if ((of = dir->d_ofork)) {
482             last = of->of_d_prev;
483             while (of) {
484                 of->of_dir = dir;
485                 of = (last == of) ? NULL : of->of_d_next;
486             }
487         }
488
489         /* set the node's d_name */
490         node->d_m_name = save.d_m_name;
491         node->d_u_name = save.d_u_name;
492     }
493
494     if (node->d_color == DIRTREE_COLOR_BLACK)
495         dir_rmrecolor(vol, leaf);
496
497     if (node->d_u_name != node->d_m_name) {
498         free(node->d_u_name);
499     }
500     free(node->d_m_name);
501     free(node);
502 #endif /* ! REMOVE_NODES */
503 }
504
505 /* ---------------------------------------
506  * remove the node and its childs from the tree
507  *
508  * FIXME what about opened forks with refs to it?
509  * it's an afp specs violation because you can't delete
510  * an opened forks. Now afpd doesn't care about forks opened by other 
511  * process. It's fixable within afpd if fnctl_lock, doable with smb and
512  * next to impossible for nfs and local filesystem access.
513  */
514 static void dir_invalidate( vol, dir )
515 const struct vol *vol;
516 struct dir *dir;
517 {
518     if (curdir == dir) {
519         /* v_root can't be deleted */
520         if (movecwd(vol, vol->v_root) < 0) {
521             LOG(log_error, logtype_afpd, "cname can't chdir to : %s", vol->v_root);
522         }
523     }
524     /* FIXME */
525     dirchildremove(dir->d_parent, dir);
526     dir_remove( vol, dir );
527 }
528
529 /* ------------------------------------ */
530 static struct dir *dir_insert(vol, dir)
531             const struct vol *vol;
532 struct dir *dir;
533 {
534     struct dir  *pdir;
535
536     pdir = vol->v_root;
537     while (pdir->d_did != dir->d_did ) {
538         if ( pdir->d_did > dir->d_did ) {
539             if ( pdir->d_left == SENTINEL ) {
540                 pdir->d_left = dir;
541                 dir->d_back = pdir;
542                 return NULL;
543             }
544             pdir = pdir->d_left;
545         } else {
546             if ( pdir->d_right == SENTINEL ) {
547                 pdir->d_right = dir;
548                 dir->d_back = pdir;
549                 return NULL;
550             }
551             pdir = pdir->d_right;
552         }
553     }
554     return pdir;
555 }
556
557
558 /*
559  * attempt to extend the current dir. tree to include path
560  * as a side-effect, movecwd to that point and return the new dir
561  */
562 static struct dir *
563             extenddir( vol, dir, path )
564 struct vol      *vol;
565 struct dir      *dir;
566 struct path *path;
567 {
568     char *save_m_name;
569
570     if ( path->u_name == NULL) {
571         path->u_name = mtoupath(vol, path->m_name, dir->d_did, (path->m_type==3) );
572     }
573     path->dir = NULL;
574
575     if ( path->u_name == NULL) {
576         afp_errno = AFPERR_PARAM;
577         return NULL;
578     }
579     if (of_stat( path ) != 0 ) {
580         return( NULL );
581     }
582
583     if (!S_ISDIR(path->st.st_mode)) {
584         return( NULL );
585     }
586
587     /* FIXME: if this is an AFP3 connection and path->m_type != 3 we might screw dircache here */
588     if ( utf8_encoding() && path->m_type != 3)
589     {
590         save_m_name = path->m_name;
591         path->m_name = NULL;
592         if (( dir = adddir( vol, dir, path)) == NULL ) {
593             free(save_m_name);
594             return( NULL );
595         }
596         path->m_name = save_m_name;
597         LOG(log_debug, logtype_afpd, "AFP3 connection, mismatch in mtype: %u, unix: %s, mac: %s",
598             path->m_type, path->u_name, path->m_name);
599     }
600     else { 
601         if (( dir = adddir( vol, dir, path)) == NULL ) {
602             return( NULL );
603         }
604     }
605
606     path->dir = dir;
607     if ( movecwd( vol, dir ) < 0 ) {
608         return( NULL );
609     }
610
611     return( dir );
612 }
613
614 /* -------------------
615    system rmdir with afp error code.
616    ENOENT is not an error.
617  */
618 static int netatalk_rmdir(const char *name)
619 {
620     if (rmdir(name) < 0) {
621         switch ( errno ) {
622         case ENOENT :
623             break;
624         case ENOTEMPTY : 
625             return AFPERR_DIRNEMPT;
626         case EPERM:
627         case EACCES :
628             return AFPERR_ACCESS;
629         case EROFS:
630             return AFPERR_VLOCK;
631         default :
632             return AFPERR_PARAM;
633         }
634     }
635     return AFP_OK;
636 }
637
638 /* -------------------------
639    appledouble mkdir afp error code.
640 */
641 static int netatalk_mkdir(const char *name)
642 {
643     if (ad_mkdir(name, DIRBITS | 0777) < 0) {
644         switch ( errno ) {
645         case ENOENT :
646             return( AFPERR_NOOBJ );
647         case EROFS :
648             return( AFPERR_VLOCK );
649         case EPERM:
650         case EACCES :
651             return( AFPERR_ACCESS );
652         case EEXIST :
653             return( AFPERR_EXIST );
654         case ENOSPC :
655         case EDQUOT :
656             return( AFPERR_DFULL );
657         default :
658             return( AFPERR_PARAM );
659         }
660     }
661     return AFP_OK;
662 }
663
664 /* -------------------
665    system unlink with afp error code.
666    ENOENT is not an error.
667  */
668 int netatalk_unlink(const char *name)
669 {
670     if (unlink(name) < 0) {
671         switch (errno) {
672         case ENOENT :
673             break;
674         case EROFS:
675             return AFPERR_VLOCK;
676         case EPERM:
677         case EACCES :
678             return AFPERR_ACCESS;
679         default :
680             return AFPERR_PARAM;
681         }
682     }
683     return AFP_OK;
684 }
685
686 /* ------------------- */
687 static int deletedir(char *dir)
688 {
689     char path[MAXPATHLEN + 1];
690     DIR *dp;
691     struct dirent       *de;
692     struct stat st;
693     size_t len;
694     int err = AFP_OK;
695     size_t remain;
696
697     if ((len = strlen(dir)) +2 > sizeof(path))
698         return AFPERR_PARAM;
699
700     /* already gone */
701     if ((dp = opendir(dir)) == NULL)
702         return AFP_OK;
703
704     strcpy(path, dir);
705     strcat(path, "/");
706     len++;
707     remain = sizeof(path) -len -1;
708     while ((de = readdir(dp)) && err == AFP_OK) {
709         /* skip this and previous directory */
710         if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
711             continue;
712
713         if (strlen(de->d_name) > remain) {
714             err = AFPERR_PARAM;
715             break;
716         }
717         strcpy(path + len, de->d_name);
718         if (stat(path, &st)) {
719             continue;
720         }
721         if (S_ISDIR(st.st_mode)) {
722             err = deletedir(path);
723         } else {
724             err = netatalk_unlink(path);
725         }
726     }
727     closedir(dp);
728
729     /* okay. the directory is empty. delete it. note: we already got rid
730        of .AppleDouble.  */
731     if (err == AFP_OK) {
732         err = netatalk_rmdir(dir);
733     }
734     return err;
735 }
736
737 /* do a recursive copy. */
738 static int copydir(char *src, char *dst, int noadouble)
739 {
740     char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
741     DIR *dp;
742     struct dirent       *de;
743     struct stat st;
744     struct utimbuf      ut;
745     size_t slen, dlen;
746     size_t srem, drem;
747     
748     int err;
749
750     /* doesn't exist or the path is too long. */
751     if (((slen = strlen(src)) > sizeof(spath) - 2) ||
752             ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
753             ((dp = opendir(src)) == NULL))
754         return AFPERR_PARAM;
755
756     /* try to create the destination directory */
757     if (AFP_OK != (err = netatalk_mkdir(dst)) ) {
758         closedir(dp);
759         return err;
760     }
761
762     /* set things up to copy */
763     strcpy(spath, src);
764     strcat(spath, "/");
765     slen++;
766     srem = sizeof(spath) - slen -1;
767     
768     strcpy(dpath, dst);
769     strcat(dpath, "/");
770     dlen++;
771     drem = sizeof(dpath) - dlen -1;
772
773     err = AFP_OK;
774     while ((de = readdir(dp))) {
775         /* skip this and previous directory */
776         if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
777             continue;
778
779         if (strlen(de->d_name) > srem) {
780             err = AFPERR_PARAM;
781             break;
782         }
783         strcpy(spath + slen, de->d_name);
784
785         if (stat(spath, &st) == 0) {
786             if (strlen(de->d_name) > drem) {
787                 err = AFPERR_PARAM;
788                 break;
789             }
790             strcpy(dpath + dlen, de->d_name);
791
792             if (S_ISDIR(st.st_mode)) {
793                 if (AFP_OK != (err = copydir(spath, dpath, noadouble)))
794                     goto copydir_done;
795             } else if (AFP_OK != (err = copyfile(spath, dpath, NULL, noadouble))) {
796                 goto copydir_done;
797
798             } else {
799                 /* keep the same time stamp. */
800                 ut.actime = ut.modtime = st.st_mtime;
801                 utime(dpath, &ut);
802             }
803         }
804     }
805
806     /* keep the same time stamp. */
807     if (stat(src, &st) == 0) {
808         ut.actime = ut.modtime = st.st_mtime;
809         utime(dst, &ut);
810     }
811
812 copydir_done:
813     closedir(dp);
814     return err;
815 }
816
817
818 /* --- public functions follow --- */
819
820 /* NOTE: we start off with at least one node (the root directory). */
821 struct dir *dirinsert( vol, dir )
822             struct vol  *vol;
823 struct dir      *dir;
824 {
825     struct dir *node;
826
827     if ((node = dir_insert(vol, dir)))
828         return node;
829
830     /* recolor the tree. the current node is red. */
831     dir->d_color = DIRTREE_COLOR_RED;
832
833     /* parent of this node has to be black. if the parent node
834     * is red, then we have a grandparent. */
835     while ((dir != vol->v_root) &&
836             (dir->d_back->d_color == DIRTREE_COLOR_RED)) {
837         /* are we on the left tree? */
838         if (dir->d_back == dir->d_back->d_back->d_left) {
839             node = dir->d_back->d_back->d_right;  /* get the right node */
840             if (node->d_color == DIRTREE_COLOR_RED) {
841                 /* we're red. we need to change to black. */
842                 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
843                 node->d_color = DIRTREE_COLOR_BLACK;
844                 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
845                 dir = dir->d_back->d_back; /* finished. go up. */
846             } else {
847                 if (dir == dir->d_back->d_right) {
848                     dir = dir->d_back;
849                     dir_leftrotate(vol, dir);
850                 }
851                 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
852                 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
853                 dir_rightrotate(vol, dir->d_back->d_back);
854             }
855         } else {
856             node = dir->d_back->d_back->d_left;
857             if (node->d_color == DIRTREE_COLOR_RED) {
858                 /* we're red. we need to change to black. */
859                 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
860                 node->d_color = DIRTREE_COLOR_BLACK;
861                 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
862                 dir = dir->d_back->d_back; /* finished. ascend */
863             } else {
864                 if (dir == dir->d_back->d_left) {
865                     dir = dir->d_back;
866                     dir_rightrotate(vol, dir);
867                 }
868                 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
869                 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
870                 dir_leftrotate(vol, dir->d_back->d_back);
871             }
872         }
873     }
874
875     vol->v_root->d_color = DIRTREE_COLOR_BLACK;
876     return NULL;
877 }
878
879 /* free everything down. we don't bother to recolor as this is only
880  * called to free the entire tree */
881 void dirfreename(struct dir *dir)
882 {
883     if (dir->d_u_name != dir->d_m_name) {
884         free(dir->d_u_name);
885     }
886     free(dir->d_m_name);
887 }
888
889 void dirfree( dir )
890 struct dir      *dir;
891 {
892     if (!dir || (dir == SENTINEL))
893         return;
894
895     if ( dir->d_left != SENTINEL ) {
896         dirfree( dir->d_left );
897     }
898     if ( dir->d_right != SENTINEL ) {
899         dirfree( dir->d_right );
900     }
901
902     if (dir != SENTINEL) {
903         dirfreename(dir);
904         free( dir );
905     }
906 }
907
908 /* --------------------------------------------
909  * most of the time mac name and unix name are the same 
910 */
911 struct dir *dirnew(const char *m_name, const char *u_name)
912 {
913     struct dir *dir;
914
915     dir = (struct dir *) calloc(1, sizeof( struct dir ));
916     if (!dir)
917         return NULL;
918
919     if ((dir->d_m_name = strdup(m_name)) == NULL) {
920         free(dir);
921         return NULL;
922     }
923
924     if (m_name == u_name) {
925         dir->d_u_name = dir->d_m_name;
926     }
927     else if ((dir->d_u_name = strdup(u_name)) == NULL) {
928         free(dir->d_m_name);
929         free(dir);
930         return NULL;
931     }
932
933     dir->d_left = dir->d_right = SENTINEL;
934     dir->d_next = dir->d_prev = dir;
935     return dir;
936 }
937
938 /* -------------------------------------------------- */
939 /* cname 
940  return
941  if it's a filename:
942       in extenddir:
943          compute unix name
944          stat the file or errno 
945       return 
946          filename
947          curdir: filename parent directory
948          
949  if it's a dirname:
950       not in the cache
951           in extenddir
952               compute unix name
953               stat the dir or errno
954           return
955               if chdir error 
956                  dirname 
957                  curdir: dir parent directory
958               sinon
959                  dirname: ""
960                  curdir: dir   
961       in the cache 
962           return
963               if chdir error
964                  dirname
965                  curdir: dir parent directory 
966               else
967                  dirname: ""
968                  curdir: dir   
969                  
970 */
971 struct path *
972 cname( vol, dir, cpath )
973 const struct vol        *vol;
974 struct dir      *dir;
975 char    **cpath;
976 {
977     struct dir             *cdir;
978     static char            path[ MAXPATHLEN + 1];
979     static struct path ret;
980
981     char                *data, *p;
982     int                 extend = 0;
983     int                 len;
984     u_int32_t   hint;
985     u_int16_t   len16;
986     int         size = 0;
987     char        sep;
988         
989     data = *cpath;
990     afp_errno = AFPERR_NOOBJ;
991     switch (ret.m_type = *data) { /* path type */
992     case 2:
993        data++;
994        len = (unsigned char) *data++;
995        size = 2;
996        sep = 0;
997        break;
998     case 3:
999        if (afp_version >= 30) {
1000            data++;
1001            memcpy(&hint, data, sizeof(hint));
1002            hint = ntohl(hint);
1003            data += sizeof(hint);
1004            
1005            memcpy(&len16, data, sizeof(len16));
1006            len = ntohs(len16);
1007            data += 2;
1008            size = 7;
1009            sep = 0; /* '/';*/
1010            break;
1011         }
1012         /* else it's an error */
1013     default:
1014         afp_errno = AFPERR_PARAM;
1015         return( NULL );
1016     }
1017     *cpath += len + size;
1018     *path = '\0';
1019     ret.m_name = path;
1020     ret.st_errno = 0;
1021     ret.st_valid = 0;
1022     ret.dir = NULL;
1023     for ( ;; ) {
1024         if ( len == 0 ) {
1025             if (movecwd( vol, dir ) < 0 ) {
1026                 /* it's tricky:
1027                    movecwd failed some of dir path are not there anymore.
1028                    FIXME Is it true with other errors?
1029                    so we remove dir from the cache 
1030                 */
1031                 if (dir->d_did == DIRDID_ROOT_PARENT)
1032                     return NULL;
1033                 if (afp_errno == AFPERR_ACCESS) {
1034                     if ( movecwd( vol, dir->d_parent ) < 0 ) {
1035                          return NULL;                   
1036                     }
1037                     ret.m_name = dir->d_m_name;
1038                     ret.u_name = dir->d_u_name;
1039                     ret.dir = dir;
1040                     return &ret;
1041                 } else if (afp_errno == AFPERR_NOOBJ) {
1042                     if ( movecwd( vol, dir->d_parent ) < 0 ) {
1043                          return NULL;                   
1044                     }
1045                     strcpy(path, dir->d_m_name);
1046                     if (dir->d_m_name == dir->d_u_name) {
1047                         ret.u_name = path;
1048                     }
1049                     else {
1050                         size_t tp = strlen(path)+1;
1051
1052                         strcpy(path +tp, dir->d_u_name);
1053                         ret.u_name =  path +tp;
1054                         
1055                     }
1056                     dir_invalidate(vol, dir);
1057                     return &ret;
1058                 }
1059                 dir_invalidate(vol, dir);
1060                 return NULL;
1061             }
1062             if (*path == '\0') {
1063                ret.u_name = ".";
1064                ret.dir = dir;
1065             }               
1066             return &ret;
1067         } /* if (len == 0) */
1068
1069         if (*data == sep ) {
1070             data++;
1071             len--;
1072         }
1073         while (*data == sep && len > 0 ) {
1074             if ( dir->d_parent == NULL ) {
1075                 return NULL;
1076             }
1077             dir = dir->d_parent;
1078             data++;
1079             len--;
1080         }
1081
1082         /* would this be faster with strlen + strncpy? */
1083         p = path;
1084         while ( *data != sep && len > 0 ) {
1085             *p++ = *data++;
1086             if (p > &path[ MAXPATHLEN]) {
1087                 afp_errno = AFPERR_PARAM;
1088                 return( NULL );
1089             }
1090             len--;
1091         }
1092
1093         /* short cut bits by chopping off a trailing \0. this also
1094                   makes the traversal happy w/ filenames at the end of the
1095                   cname. */
1096         if (len == 1)
1097             len--;
1098
1099         *p = '\0';
1100
1101         if ( p != path ) { /* we got something */
1102             ret.u_name = NULL;
1103             if (afp_version >= 30) {
1104                 /* check for OS X mangled filename :( */
1105                 char *t;
1106                 cnid_t fileid;
1107             
1108                 t = demangle_osx(vol, path, dir->d_did, &fileid);
1109                 if (t != path) {
1110                     ret.u_name = t;
1111                     /* duplicate work but we can't reuse all convert_char we did in demagnle_osx 
1112                      * flags weren't the same
1113                     */
1114                     if ( (t = utompath(vol, ret.u_name, fileid, utf8_encoding())) ) {
1115                         /* at last got our view of mac name */
1116                         strcpy(path,t);
1117                     }                    
1118                 }
1119             }
1120             if ( !extend ) {
1121                 cdir = dir->d_child;
1122                 while (cdir) {
1123                     if ( strcmp( cdir->d_m_name, path ) == 0 ) {
1124                         break;
1125                     }
1126                     cdir = (cdir == dir->d_child->d_prev) ? NULL :
1127                            cdir->d_next;
1128                 }
1129                 if ( cdir == NULL ) {
1130                     ++extend;
1131                     /* if dir == curdir it always succeed,
1132                        even if curdir is deleted. 
1133                        it's not a pb because it will fail in extenddir
1134                     */
1135                     if ( movecwd( vol, dir ) < 0 ) {
1136                         /* dir is not valid anymore 
1137                            we delete dir from the cache and abort.
1138                         */
1139                         if ( dir->d_did == DIRDID_ROOT_PARENT) {
1140                             afp_errno = AFPERR_NOOBJ;
1141                             return NULL;
1142                         }
1143                         if (afp_errno == AFPERR_ACCESS)
1144                             return NULL;
1145                         dir_invalidate(vol, dir);
1146                         return NULL;
1147                     }
1148                     cdir = extenddir( vol, dir, &ret );
1149                 }
1150
1151             } else {
1152                 cdir = extenddir( vol, dir, &ret );
1153             } /* if (!extend) */
1154
1155             if ( cdir == NULL ) {
1156
1157                 if ( len > 0 || !ret.u_name) {
1158                     return NULL;
1159                 }
1160
1161             } else {
1162                 dir = cdir;     
1163                 *path = '\0';
1164             }
1165         } /* if (p != path) */
1166     } /* for (;;) */
1167 }
1168
1169 /*
1170  * Move curdir to dir, with a possible chdir()
1171  */
1172 int movecwd( vol, dir)
1173 const struct vol        *vol;
1174 struct dir      *dir;
1175 {
1176     char path[MAXPATHLEN + 1];
1177     struct dir  *d;
1178     char        *p, *u;
1179     int         n;
1180
1181     if ( dir == curdir ) {
1182         return( 0 );
1183     }
1184     if ( dir->d_did == DIRDID_ROOT_PARENT) {
1185         afp_errno = AFPERR_DID1; /* AFPERR_PARAM;*/
1186         return( -1 );
1187     }
1188
1189     p = path + sizeof(path) - 1;
1190     *p-- = '\0';
1191     *p = '.';
1192     for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) {
1193         u = d->d_u_name;
1194         if (!u) {
1195             /* parent directory is deleted */
1196             afp_errno = AFPERR_NOOBJ;
1197             return -1;
1198         }
1199         n = strlen( u );
1200         if (p -n -1 < path) {
1201             afp_errno = AFPERR_PARAM;
1202             return -1;
1203         }
1204         *--p = '/';
1205         p -= n;
1206         strncpy( p, u, n );
1207     }
1208     if ( d != curdir ) {
1209         n = strlen( vol->v_path );
1210         if (p -n -1 < path) {
1211             afp_errno = AFPERR_PARAM;
1212             return -1;
1213         }
1214         *--p = '/';
1215         p -= n;
1216         strncpy( p, vol->v_path, n );
1217     }
1218     if ( chdir( p ) < 0 ) {
1219         switch (errno) {
1220         case EACCES:
1221         case EPERM:
1222             afp_errno = AFPERR_ACCESS;
1223             break;
1224         default:
1225             afp_errno = AFPERR_NOOBJ;
1226         
1227         }
1228         return( -1 );
1229     }
1230     curdir = dir;
1231     return( 0 );
1232 }
1233
1234 /*
1235  * We can't use unix file's perm to support Apple's inherited protection modes.
1236  * If we aren't the file's owner we can't change its perms when moving it and smb
1237  * nfs,... don't even try.
1238 */
1239 #define AFP_CHECK_ACCESS 
1240
1241 int check_access(char *path, int mode)
1242 {
1243 #ifdef AFP_CHECK_ACCESS
1244 struct maccess ma;
1245 char *p;
1246
1247     p = ad_dir(path);
1248     if (!p)
1249        return -1;
1250
1251     accessmode(p, &ma, curdir, NULL);
1252     if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1253         return -1;
1254     if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1255         return -1;
1256 #endif
1257     return 0;
1258 }
1259
1260 /* --------------------- */
1261 int file_access(struct path *path, int mode)
1262 {
1263 struct maccess ma;
1264
1265     accessmode(path->u_name, &ma, curdir, &path->st);
1266     if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1267         return -1;
1268     if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1269         return -1;
1270     return 0;
1271
1272 }
1273
1274 /* ------------------------------ 
1275    (".", curdir)
1276    (name, dir) with curdir:name == dir, from afp_enumerate
1277 */
1278
1279 int getdirparams(const struct vol *vol,
1280                  u_int16_t bitmap, struct path *s_path,
1281                  struct dir *dir, 
1282                  char *buf, int *buflen )
1283 {
1284     struct maccess      ma;
1285     struct adouble      ad;
1286     char                *data, *l_nameoff = NULL, *utf_nameoff = NULL;
1287     int                 bit = 0, isad = 0;
1288     u_int32_t           aint;
1289     u_int16_t           ashort;
1290     int                 ret;
1291     u_int32_t           utf8 = 0;
1292     cnid_t              pdid;
1293     struct stat *st = &s_path->st;
1294     char *upath = s_path->u_name;
1295     
1296     if ((bitmap & ((1 << DIRPBIT_ATTR)  |
1297                    (1 << DIRPBIT_CDATE) |
1298                    (1 << DIRPBIT_MDATE) |
1299                    (1 << DIRPBIT_BDATE) |
1300                    (1 << DIRPBIT_FINFO)))) {
1301
1302         ad_init(&ad, vol->v_adouble);
1303         if ( !ad_open( upath, ADFLAGS_HF|ADFLAGS_DIR, O_RDONLY,
1304                   DIRBITS | 0777, &ad)) {
1305             isad = 1;
1306         }
1307     }
1308     
1309     if ( dir->d_did == DIRDID_ROOT) {
1310         pdid = DIRDID_ROOT_PARENT;
1311     } else if (dir->d_did == DIRDID_ROOT_PARENT) {
1312         pdid = 0;
1313     } else {
1314         pdid = dir->d_parent->d_did;
1315     }
1316     
1317     data = buf;
1318     while ( bitmap != 0 ) {
1319         while (( bitmap & 1 ) == 0 ) {
1320             bitmap = bitmap>>1;
1321             bit++;
1322         }
1323
1324         switch ( bit ) {
1325         case DIRPBIT_ATTR :
1326             if ( isad ) {
1327                 ad_getattr(&ad, &ashort);
1328             } else if (*dir->d_u_name == '.' && strcmp(dir->d_u_name, ".") 
1329                         && strcmp(dir->d_u_name, "..")) {
1330                 ashort = htons(ATTRBIT_INVISIBLE);
1331             } else
1332                 ashort = 0;
1333             ashort |= htons(ATTRBIT_SHARED);
1334             memcpy( data, &ashort, sizeof( ashort ));
1335             data += sizeof( ashort );
1336             break;
1337
1338         case DIRPBIT_PDID :
1339             memcpy( data, &pdid, sizeof( pdid ));
1340             data += sizeof( pdid );
1341             break;
1342
1343         case DIRPBIT_CDATE :
1344             if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
1345                 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1346             memcpy( data, &aint, sizeof( aint ));
1347             data += sizeof( aint );
1348             break;
1349
1350         case DIRPBIT_MDATE :
1351             aint = AD_DATE_FROM_UNIX(st->st_mtime);
1352             memcpy( data, &aint, sizeof( aint ));
1353             data += sizeof( aint );
1354             break;
1355
1356         case DIRPBIT_BDATE :
1357             if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
1358                 aint = AD_DATE_START;
1359             memcpy( data, &aint, sizeof( aint ));
1360             data += sizeof( aint );
1361             break;
1362
1363         case DIRPBIT_FINFO :
1364             if ( isad ) {
1365                 memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
1366             } else { /* no appledouble */
1367                 memset( data, 0, 32 );
1368                 /* set default view -- this also gets done in ad_open() */
1369                 ashort = htons(FINDERINFO_CLOSEDVIEW);
1370                 memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
1371
1372                 /* dot files are by default invisible */
1373                 if (*dir->d_u_name  == '.' && strcmp(dir->d_u_name , ".") &&
1374                         strcmp(dir->d_u_name , "..")) {
1375                     ashort = htons(FINDERINFO_INVISIBLE);
1376                     memcpy(data + FINDERINFO_FRFLAGOFF,
1377                            &ashort, sizeof(ashort));
1378                 }
1379             }
1380             data += 32;
1381             break;
1382
1383         case DIRPBIT_LNAME :
1384             if (dir->d_m_name) /* root of parent can have a null name */
1385                 l_nameoff = data;
1386             else
1387                 memset(data, 0, sizeof(u_int16_t));
1388             data += sizeof( u_int16_t );
1389             break;
1390
1391         case DIRPBIT_SNAME :
1392             memset(data, 0, sizeof(u_int16_t));
1393             data += sizeof( u_int16_t );
1394             break;
1395
1396         case DIRPBIT_DID :
1397             memcpy( data, &dir->d_did, sizeof( aint ));
1398             data += sizeof( aint );
1399             break;
1400
1401         case DIRPBIT_OFFCNT :
1402             ashort = 0;
1403             /* this needs to handle current directory access rights */
1404             if (st->st_ctime == dir->ctime) {
1405                 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1406             }
1407             else if ((ret = for_each_dirent(vol, upath, NULL,NULL)) >= 0) {
1408                 dir->offcnt = ret;
1409                 dir->ctime = st->st_ctime;
1410                 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1411             }
1412             ashort = htons( ashort );
1413             memcpy( data, &ashort, sizeof( ashort ));
1414             data += sizeof( ashort );
1415             break;
1416
1417         case DIRPBIT_UID :
1418             aint = htonl(st->st_uid);
1419             memcpy( data, &aint, sizeof( aint ));
1420             data += sizeof( aint );
1421             break;
1422
1423         case DIRPBIT_GID :
1424             aint = htonl(st->st_gid);
1425             memcpy( data, &aint, sizeof( aint ));
1426             data += sizeof( aint );
1427             break;
1428
1429         case DIRPBIT_ACCESS :
1430             accessmode( upath, &ma, dir , st);
1431
1432             *data++ = ma.ma_user;
1433             *data++ = ma.ma_world;
1434             *data++ = ma.ma_group;
1435             *data++ = ma.ma_owner;
1436             break;
1437
1438             /* Client has requested the ProDOS information block.
1439                Just pass back the same basic block for all
1440                directories. <shirsch@ibm.net> */
1441         case DIRPBIT_PDINFO :                     
1442             if (afp_version >= 30) { /* UTF8 name */
1443                 utf8 = kTextEncodingUTF8;
1444                 if (dir->d_m_name) /* root of parent can have a null name */
1445                     utf_nameoff = data;
1446                 else
1447                     memset(data, 0, sizeof(u_int16_t));
1448                 data += sizeof( u_int16_t );
1449                 aint = 0;
1450                 memcpy(data, &aint, sizeof( aint ));
1451                 data += sizeof( aint );
1452             }
1453             else { /* ProDOS Info Block */
1454                 *data++ = 0x0f;
1455                 *data++ = 0;
1456                 ashort = htons( 0x0200 );
1457                 memcpy( data, &ashort, sizeof( ashort ));
1458                 data += sizeof( ashort );
1459                 memset( data, 0, sizeof( ashort ));
1460                 data += sizeof( ashort );
1461             }
1462             break;
1463
1464         case DIRPBIT_UNIXPR :
1465             aint = htonl(st->st_uid);
1466             memcpy( data, &aint, sizeof( aint ));
1467             data += sizeof( aint );
1468             aint = htonl(st->st_gid);
1469             memcpy( data, &aint, sizeof( aint ));
1470             data += sizeof( aint );
1471         
1472             aint = st->st_mode;
1473             aint = htonl ( aint & ~S_ISGID );  /* Remove SGID, OSX doesn't like it ... */
1474             memcpy( data, &aint, sizeof( aint ));
1475             data += sizeof( aint );
1476
1477             accessmode( upath, &ma, dir , st);
1478
1479             *data++ = ma.ma_user;
1480             *data++ = ma.ma_world;
1481             *data++ = ma.ma_group;
1482             *data++ = ma.ma_owner;
1483             break;
1484
1485
1486         default :
1487             if ( isad ) {
1488                 ad_close( &ad, ADFLAGS_HF );
1489             }
1490             return( AFPERR_BITMAP );
1491         }
1492         bitmap = bitmap>>1;
1493         bit++;
1494     }
1495     if ( l_nameoff ) {
1496         ashort = htons( data - buf );
1497         memcpy( l_nameoff, &ashort, sizeof( ashort ));
1498         data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, 0);
1499     }
1500     if ( utf_nameoff ) {
1501         ashort = htons( data - buf );
1502         memcpy( utf_nameoff, &ashort, sizeof( ashort ));
1503         data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, utf8);
1504     }
1505     if ( isad ) {
1506         ad_close( &ad, ADFLAGS_HF );
1507     }
1508     *buflen = data - buf;
1509     return( AFP_OK );
1510 }
1511
1512 /* ----------------------------- */
1513 int path_error(struct path *path, int error)
1514 {
1515 /* - a dir with access error
1516  * - no error it's a file
1517  * - file not found
1518  */
1519     if (path_isadir(path))
1520         return afp_errno;
1521     if (path->st_errno)
1522         return error;
1523     return AFPERR_BADTYPE ;
1524 }
1525
1526 /* ----------------------------- */
1527 int afp_setdirparams(obj, ibuf, ibuflen, rbuf, rbuflen )
1528 AFPObj      *obj;
1529 char    *ibuf, *rbuf;
1530 int             ibuflen, *rbuflen;
1531 {
1532     struct vol  *vol;
1533     struct dir  *dir;
1534     struct path *path;
1535     u_int16_t   vid, bitmap;
1536     u_int32_t   did;
1537     int         rc;
1538
1539     *rbuflen = 0;
1540     ibuf += 2;
1541     memcpy( &vid, ibuf, sizeof( vid ));
1542     ibuf += sizeof( vid );
1543
1544     if (NULL == ( vol = getvolbyvid( vid )) ) {
1545         return( AFPERR_PARAM );
1546     }
1547
1548     if (vol->v_flags & AFPVOL_RO)
1549         return AFPERR_VLOCK;
1550
1551     memcpy( &did, ibuf, sizeof( did ));
1552     ibuf += sizeof( int );
1553
1554     if (NULL == ( dir = dirlookup( vol, did )) ) {
1555         return afp_errno;
1556     }
1557
1558     memcpy( &bitmap, ibuf, sizeof( bitmap ));
1559     bitmap = ntohs( bitmap );
1560     ibuf += sizeof( bitmap );
1561
1562     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1563         return get_afp_errno(AFPERR_NOOBJ); 
1564     }
1565
1566     if ( *path->m_name != '\0' ) {
1567         return path_error(path, AFPERR_NOOBJ);
1568     }
1569
1570     /*
1571      * If ibuf is odd, make it even.
1572      */
1573     if ((u_long)ibuf & 1 ) {
1574         ibuf++;
1575     }
1576
1577     if (AFP_OK == ( rc = setdirparams(vol, path, bitmap, ibuf )) ) {
1578         setvoltime(obj, vol );
1579     }
1580     return( rc );
1581 }
1582
1583 /*
1584  * cf AFP3.0.pdf page 244 for change_mdate and change_parent_mdate logic  
1585  *
1586  * assume path == '\0' eg. it's a directory in canonical form
1587 */
1588
1589 struct path Cur_Path = {
1590     0,
1591     "",  /* mac name */
1592     ".", /* unix name */
1593     0,  /* stat is not set */
1594     0,  /* */
1595 };
1596
1597 int setdirparams(const struct vol *vol, 
1598                  struct path *path, u_int16_t bitmap, char *buf )
1599 {
1600     struct maccess      ma;
1601     struct adouble      ad;
1602     struct utimbuf      ut;
1603     struct timeval      tv;
1604
1605     char                *upath;
1606     int                 bit = 0, aint, isad = 1;
1607     u_int16_t           ashort, bshort;
1608     int                 err = AFP_OK;
1609     int                 change_mdate = 0;
1610     int                 change_parent_mdate = 0;
1611     int                 newdate = 0;
1612
1613     upath = path->u_name;
1614     ad_init(&ad, vol->v_adouble);
1615
1616     if (ad_open( upath, vol_noadouble(vol)|ADFLAGS_HF|ADFLAGS_DIR,
1617                  O_RDWR|O_CREAT, 0666, &ad) < 0) {
1618         /*
1619          * Check to see what we're trying to set.  If it's anything
1620          * but ACCESS, UID, or GID, give an error.  If it's any of those
1621          * three, we don't need the ad to be open, so just continue.
1622          *
1623          * note: we also don't need to worry about mdate. also, be quiet
1624          *       if we're using the noadouble option.
1625          */
1626         if (!vol_noadouble(vol) && (bitmap &
1627                                     ~((1<<DIRPBIT_ACCESS)|(1<<DIRPBIT_UID)|(1<<DIRPBIT_GID)|
1628                                       (1<<DIRPBIT_MDATE)|(1<<DIRPBIT_PDINFO)))) {
1629             return AFPERR_ACCESS;
1630         }
1631
1632         isad = 0;
1633     } else {
1634         /*
1635          * Check to see if a create was necessary. If it was, we'll want
1636          * to set our name, etc.
1637          */
1638         if ( ad_get_HF_flags( &ad ) & O_CREAT ) {
1639             ad_setentrylen( &ad, ADEID_NAME, strlen( curdir->d_m_name ));
1640             memcpy( ad_entry( &ad, ADEID_NAME ), curdir->d_m_name,
1641                     ad_getentrylen( &ad, ADEID_NAME ));
1642         }
1643     }
1644
1645     while ( bitmap != 0 ) {
1646         while (( bitmap & 1 ) == 0 ) {
1647             bitmap = bitmap>>1;
1648             bit++;
1649         }
1650
1651         switch( bit ) {
1652         case DIRPBIT_ATTR :
1653             change_mdate = 1;
1654             if (isad) {
1655                 memcpy( &ashort, buf, sizeof( ashort ));
1656                 ad_getattr(&ad, &bshort);
1657                 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1658                     bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1659                 } else {
1660                     bshort &= ~ashort;
1661                 }
1662                 ad_setattr(&ad, bshort);
1663                 if ((ashort & htons(ATTRBIT_INVISIBLE)))
1664                    change_parent_mdate = 1;
1665             }
1666             buf += sizeof( ashort );
1667             break;
1668
1669         case DIRPBIT_CDATE :
1670             change_mdate = 1;
1671             if (isad) {
1672                 memcpy(&aint, buf, sizeof(aint));
1673                 ad_setdate(&ad, AD_DATE_CREATE, aint);
1674             }
1675             buf += sizeof( aint );
1676             break;
1677
1678         case DIRPBIT_MDATE :
1679             memcpy(&newdate, buf, sizeof(newdate));
1680             buf += sizeof( newdate );
1681             break;
1682
1683         case DIRPBIT_BDATE :
1684             change_mdate = 1;
1685             if (isad) {
1686                 memcpy(&aint, buf, sizeof(aint));
1687                 ad_setdate(&ad, AD_DATE_BACKUP, aint);
1688             }
1689             buf += sizeof( aint );
1690             break;
1691
1692         case DIRPBIT_FINFO :
1693             change_mdate = 1;
1694             /*
1695              * Alright, we admit it, this is *really* sick!
1696              * The 4 bytes that we don't copy, when we're dealing
1697              * with the root of a volume, are the directory's
1698              * location information. This eliminates that annoying
1699              * behavior one sees when mounting above another mount
1700              * point.
1701              */
1702             if (isad) {
1703                 if (  curdir->d_did == DIRDID_ROOT ) {
1704                     memcpy( ad_entry( &ad, ADEID_FINDERI ), buf, 10 );
1705                     memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, buf + 14, 18 );
1706                 } else {
1707                     memcpy( ad_entry( &ad, ADEID_FINDERI ), buf, 32 );
1708                 }
1709             }
1710             buf += 32;
1711             break;
1712
1713         case DIRPBIT_UID :      /* What kind of loser mounts as root? */
1714             change_parent_mdate = 1;
1715             memcpy( &aint, buf, sizeof(aint));
1716             buf += sizeof( aint );
1717             if ( (curdir->d_did == DIRDID_ROOT) &&
1718                     (setdeskowner( ntohl(aint), -1 ) < 0)) {
1719                 switch ( errno ) {
1720                 case EPERM :
1721                 case EACCES :
1722                     err = AFPERR_ACCESS;
1723                     goto setdirparam_done;
1724                     break;
1725                 case EROFS :
1726                     err = AFPERR_VLOCK;
1727                     goto setdirparam_done;
1728                     break;
1729                 default :
1730                     LOG(log_error, logtype_afpd, "setdirparam: setdeskowner: %s",
1731                         strerror(errno) );
1732                     if (!isad) {
1733                         err = AFPERR_PARAM;
1734                         goto setdirparam_done;
1735                     }
1736                     break;
1737                 }
1738             }
1739             if ( setdirowner( ntohl(aint), -1, vol_noadouble(vol) ) < 0 ) {
1740                 switch ( errno ) {
1741                 case EPERM :
1742                 case EACCES :
1743                     err = AFPERR_ACCESS;
1744                     goto setdirparam_done;
1745                     break;
1746                 case EROFS :
1747                     err = AFPERR_VLOCK;
1748                     goto setdirparam_done;
1749                     break;
1750                 default :
1751                     LOG(log_error, logtype_afpd, "setdirparam: setdirowner: %s",
1752                         strerror(errno) );
1753                     break;
1754                 }
1755             }
1756             break;
1757         case DIRPBIT_GID :
1758             change_parent_mdate = 1;
1759             memcpy( &aint, buf, sizeof( aint ));
1760             buf += sizeof( aint );
1761             if (curdir->d_did == DIRDID_ROOT)
1762                 setdeskowner( -1, ntohl(aint) );
1763
1764 #if 0       /* don't error if we can't set the desktop owner. */
1765             switch ( errno ) {
1766             case EPERM :
1767             case EACCES :
1768                 err = AFPERR_ACCESS;
1769                 goto setdirparam_done;
1770                 break;
1771             case EROFS :
1772                 err = AFPERR_VLOCK;
1773                 goto setdirparam_done;
1774                 break;
1775             default :
1776                 LOG(log_error, logtype_afpd, "setdirparam: setdeskowner: %m" );
1777                 if (!isad) {
1778                     err = AFPERR_PARAM;
1779                     goto setdirparam_done;
1780                 }
1781                 break;
1782             }
1783 #endif /* 0 */
1784
1785             if ( setdirowner( -1, ntohl(aint), vol_noadouble(vol) ) < 0 ) {
1786                 switch ( errno ) {
1787                 case EPERM :
1788                 case EACCES :
1789                     err = AFPERR_ACCESS;
1790                     goto setdirparam_done;
1791                     break;
1792                 case EROFS :
1793                     err = AFPERR_VLOCK;
1794                     goto setdirparam_done;
1795                     break;
1796                 default :
1797                     LOG(log_error, logtype_afpd, "setdirparam: setdirowner: %s",
1798                         strerror(errno) );
1799                     break;
1800                 }
1801             }
1802             break;
1803
1804         case DIRPBIT_ACCESS :
1805             change_mdate = 1;
1806             change_parent_mdate = 1;
1807             ma.ma_user = *buf++;
1808             ma.ma_world = *buf++;
1809             ma.ma_group = *buf++;
1810             ma.ma_owner = *buf++;
1811
1812             if (curdir->d_did == DIRDID_ROOT)
1813                 setdeskmode(mtoumode( &ma ));
1814 #if 0 /* don't error if we can't set the desktop mode */
1815             switch ( errno ) {
1816             case EPERM :
1817             case EACCES :
1818                 err = AFPERR_ACCESS;
1819                 goto setdirparam_done;
1820             case EROFS :
1821                 err = AFPERR_VLOCK;
1822                 goto setdirparam_done;
1823             default :
1824                 LOG(log_error, logtype_afpd, "setdirparam: setdeskmode: %s",
1825                     strerror(errno) );
1826                 break;
1827                 err = AFPERR_PARAM;
1828                 goto setdirparam_done;
1829             }
1830 #endif /* 0 */
1831
1832             if ( setdirmode( mtoumode( &ma ), vol_noadouble(vol),
1833                          (vol->v_flags & AFPVOL_DROPBOX)) < 0 ) {
1834                 switch ( errno ) {
1835                 case EPERM :
1836                 case EACCES :
1837                     err = AFPERR_ACCESS;
1838                     goto setdirparam_done;
1839                 case EROFS :
1840                     err = AFPERR_VLOCK;
1841                     goto setdirparam_done;
1842                 default :
1843                     LOG(log_error, logtype_afpd, "setdirparam: setdirmode: %s",
1844                         strerror(errno) );
1845                     err = AFPERR_PARAM;
1846                     goto setdirparam_done;
1847                 }
1848             }
1849             break;
1850
1851         /* Ignore what the client thinks we should do to the
1852            ProDOS information block.  Skip over the data and
1853            report nothing amiss. <shirsch@ibm.net> */
1854         case DIRPBIT_PDINFO :
1855             if (afp_version < 30) {
1856                 buf += 6;
1857                 break;
1858             }
1859
1860         case DIRPBIT_UNIXPR :
1861             /* Skip UID and GID for now, there seems to be now way to set them from an OSX client anyway */
1862             buf += sizeof( aint );
1863             buf += sizeof( aint );
1864
1865             change_mdate = 1;
1866             change_parent_mdate = 1;
1867             memcpy( &aint, buf, sizeof( aint ));
1868             buf += sizeof( aint );
1869             aint = ntohl (aint);
1870             if (curdir->d_did == DIRDID_ROOT)
1871                 setdeskmode( aint );
1872 #if 0 /* don't error if we can't set the desktop mode */
1873             switch ( errno ) {
1874             case EPERM :
1875             case EACCES :
1876                 err = AFPERR_ACCESS;
1877                 goto setdirparam_done;
1878             case EROFS :
1879                 err = AFPERR_VLOCK;
1880                 goto setdirparam_done;
1881             default :
1882                 LOG(log_error, logtype_afpd, "setdirparam: setdeskmode: %s",
1883                     strerror(errno) );
1884                 break;
1885                 err = AFPERR_PARAM;
1886                 goto setdirparam_done;
1887             }
1888 #endif /* 0 */
1889
1890             if ( setdirunixmode( aint, vol_noadouble(vol),
1891                          (vol->v_flags & AFPVOL_DROPBOX)) < 0 ) {
1892                 switch ( errno ) {
1893                 case EPERM :
1894                 case EACCES :
1895                     err = AFPERR_ACCESS;
1896                     goto setdirparam_done;
1897                 case EROFS :
1898                     err = AFPERR_VLOCK;
1899                     goto setdirparam_done;
1900                 default :
1901                     LOG(log_error, logtype_afpd, "setdirparam: setdirmode: %s",
1902                         strerror(errno) );
1903                     err = AFPERR_PARAM;
1904                     goto setdirparam_done;
1905                 }
1906             }
1907             break;
1908
1909         default :
1910             err = AFPERR_BITMAP;
1911             goto setdirparam_done;
1912             break;
1913         }
1914
1915         bitmap = bitmap>>1;
1916         bit++;
1917     }
1918
1919 setdirparam_done:
1920     if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1921        newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1922     }
1923     if (newdate) {
1924        if (isad)
1925           ad_setdate(&ad, AD_DATE_MODIFY, newdate);
1926        ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1927        utime(upath, &ut);
1928     }
1929
1930     if ( isad ) {
1931         if (path->st_valid && !path->st_errno) {
1932             struct stat *st = &path->st;
1933
1934             if (curdir->d_parent) {
1935                 ad_setid(&ad,(vol->v_flags & AFPVOL_NODEV)?0:st->st_dev, 
1936                          st->st_ino,  curdir->d_did, curdir->d_parent->d_did, vol->v_stamp);
1937             }
1938         }
1939     
1940         ad_flush( &ad, ADFLAGS_HF );
1941         ad_close( &ad, ADFLAGS_HF );
1942     }
1943
1944     if (change_parent_mdate && curdir->d_did != DIRDID_ROOT
1945             && gettimeofday(&tv, NULL) == 0) {
1946        if (!movecwd(vol, curdir->d_parent)) {
1947            newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1948            bitmap = 1<<DIRPBIT_MDATE;
1949            setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1950            /* should we reset curdir ?*/
1951        }
1952     }
1953
1954     return err;
1955 }
1956
1957 int afp_createdir(obj, ibuf, ibuflen, rbuf, rbuflen )
1958 AFPObj      *obj;
1959 char    *ibuf, *rbuf;
1960 int             ibuflen, *rbuflen;
1961 {
1962     struct adouble      ad;
1963     struct vol          *vol;
1964     struct dir          *dir;
1965     char                *upath;
1966     struct path         *s_path;
1967     u_int32_t           did;
1968     u_int16_t           vid;
1969     int                 err;
1970     
1971     *rbuflen = 0;
1972     ibuf += 2;
1973
1974     memcpy( &vid, ibuf, sizeof( vid ));
1975     ibuf += sizeof( vid );
1976     if (NULL == ( vol = getvolbyvid( vid )) ) {
1977         return( AFPERR_PARAM );
1978     }
1979
1980     if (vol->v_flags & AFPVOL_RO)
1981         return AFPERR_VLOCK;
1982
1983     memcpy( &did, ibuf, sizeof( did ));
1984     ibuf += sizeof( did );
1985     if (NULL == ( dir = dirlookup( vol, did )) ) {
1986         return afp_errno; /* was AFPERR_NOOBJ */
1987     }
1988     /* for concurrent access we need to be sure we are not in the
1989      * folder we want to create...
1990     */
1991     movecwd(vol, dir);
1992     
1993     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1994         return get_afp_errno(AFPERR_PARAM);
1995     }
1996     /* cname was able to move curdir to it! */
1997     if (*s_path->m_name == '\0')
1998         return AFPERR_EXIST;
1999
2000     upath = s_path->u_name;
2001     if (0 != (err = check_name(vol, upath))) {
2002        return err;
2003     }
2004
2005     if (AFP_OK != (err = netatalk_mkdir( upath))) {
2006         return err;
2007     }
2008
2009     if (of_stat(s_path) < 0) {
2010         return AFPERR_MISC;
2011     }
2012     curdir->offcnt++;
2013     if ((dir = adddir( vol, curdir, s_path)) == NULL) {
2014         return AFPERR_MISC;
2015     }
2016
2017     if ( movecwd( vol, dir ) < 0 ) {
2018         return( AFPERR_PARAM );
2019     }
2020
2021     ad_init(&ad, vol->v_adouble);
2022     if (ad_open( ".", vol_noadouble(vol)|ADFLAGS_HF|ADFLAGS_DIR,
2023                  O_RDWR|O_CREAT, 0666, &ad ) < 0)  {
2024         if (vol_noadouble(vol))
2025             goto createdir_done;
2026         return( AFPERR_ACCESS );
2027     }
2028
2029     ad_setentrylen( &ad, ADEID_NAME, strlen( s_path->m_name ));
2030     memcpy( ad_entry( &ad, ADEID_NAME ), s_path->m_name,
2031             ad_getentrylen( &ad, ADEID_NAME ));
2032
2033     ad_setid( &ad, (vol->v_flags & AFPVOL_NODEV)?0:s_path->st.st_dev,
2034                    s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
2035
2036     ad_flush( &ad, ADFLAGS_HF );
2037     ad_close( &ad, ADFLAGS_HF );
2038
2039 createdir_done:
2040     memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
2041     *rbuflen = sizeof( u_int32_t );
2042     setvoltime(obj, vol );
2043     return( AFP_OK );
2044 }
2045
2046 /*
2047  * dst       new unix filename (not a pathname)
2048  * newname   new mac name
2049  * newparent curdir
2050  *
2051 */
2052 int renamedir(src, dst, dir, newparent, newname, noadouble)
2053 char    *src, *dst, *newname;
2054 struct dir      *dir, *newparent;
2055 const int noadouble;
2056 {
2057     struct adouble      ad;
2058     struct dir          *parent;
2059     char                *buf;
2060     int                 len, err;
2061         
2062     /* existence check moved to afp_moveandrename */
2063     if ( unix_rename( src, dst ) < 0 ) {
2064         switch ( errno ) {
2065         case ENOENT :
2066             return( AFPERR_NOOBJ );
2067         case EACCES :
2068             return( AFPERR_ACCESS );
2069         case EROFS:
2070             return AFPERR_VLOCK;
2071         case EINVAL:
2072             /* tried to move directory into a subdirectory of itself */
2073             return AFPERR_CANTMOVE;
2074         case EXDEV:
2075             /* this needs to copy and delete. bleah. that means we have
2076              * to deal with entire directory hierarchies. */
2077             if ((err = copydir(src, dst, noadouble)) < 0) {
2078                 deletedir(dst);
2079                 return err;
2080             }
2081             if ((err = deletedir(src)) < 0)
2082                 return err;
2083             break;
2084         default :
2085             return( AFPERR_PARAM );
2086         }
2087     }
2088
2089     ad_init(&ad, 0);    /* FIXME */
2090
2091     len = strlen( newname );
2092     /* rename() succeeded so we need to update our tree even if we can't open
2093      * .Parent
2094     */
2095     if ( !ad_open( dst, ADFLAGS_HF|ADFLAGS_DIR, O_RDWR, 0, &ad)) {
2096         ad_setentrylen( &ad, ADEID_NAME, len );
2097         memcpy( ad_entry( &ad, ADEID_NAME ), newname, len );
2098         ad_flush( &ad, ADFLAGS_HF );
2099         ad_close( &ad, ADFLAGS_HF );
2100     }
2101     
2102     if (dir->d_m_name == dir->d_u_name)
2103         dir->d_u_name = NULL;
2104
2105     if ((buf = (char *) realloc( dir->d_m_name, len + 1 )) == NULL ) {
2106         LOG(log_error, logtype_afpd, "renamedir: realloc mac name: %s", strerror(errno) );
2107         /* FIXME : fatal ? */
2108         return AFPERR_MISC;
2109     }
2110     dir->d_m_name = buf;
2111     strcpy( dir->d_m_name, newname );
2112
2113     if (newname == dst) {
2114         free(dir->d_u_name);
2115         dir->d_u_name = dir->d_m_name;
2116     }
2117     else {
2118         if ((buf = (char *) realloc( dir->d_u_name, strlen(dst) + 1 )) == NULL ) {
2119             LOG(log_error, logtype_afpd, "renamedir: realloc unix name: %s", strerror(errno) );
2120             return AFPERR_MISC;
2121         }
2122         dir->d_u_name = buf;
2123         strcpy( dir->d_u_name, dst );
2124     }
2125
2126     if (( parent = dir->d_parent ) == NULL ) {
2127         return( AFP_OK );
2128     }
2129     if ( parent == newparent ) {
2130         return( AFP_OK );
2131     }
2132
2133     /* detach from old parent and add to new one. */
2134     dirchildremove(parent, dir);
2135     dir->d_parent = newparent;
2136     dirchildadd(newparent, dir);
2137     return( AFP_OK );
2138 }
2139
2140 #define DOT_APPLEDOUBLE_LEN 13
2141 /* delete an empty directory */
2142 int deletecurdir( vol, path, pathlen )
2143 const struct vol        *vol;
2144 char *path;
2145 int pathlen;
2146 {
2147     struct dirent *de;
2148     struct stat st;
2149     struct dir  *fdir;
2150     DIR *dp;
2151     struct adouble      ad;
2152     u_int16_t           ashort;
2153     int err;
2154
2155     if ( curdir->d_parent == NULL ) {
2156         return( AFPERR_ACCESS );
2157     }
2158
2159     fdir = curdir;
2160
2161     ad_init(&ad, vol->v_adouble);
2162     if ( ad_open( ".", ADFLAGS_HF|ADFLAGS_DIR, O_RDONLY,
2163                   DIRBITS | 0777, &ad) == 0 ) {
2164
2165         ad_getattr(&ad, &ashort);
2166         ad_close( &ad, ADFLAGS_HF );
2167         if ((ashort & htons(ATTRBIT_NODELETE))) {
2168             return  AFPERR_OLOCK;
2169         }
2170     }
2171
2172     /* delete stray .AppleDouble files. this happens to get .Parent files
2173        as well. */
2174     if ((dp = opendir(".AppleDouble"))) {
2175         strcpy(path, ".AppleDouble/");
2176         while ((de = readdir(dp))) {
2177             /* skip this and previous directory */
2178             if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
2179                 continue;
2180
2181             /* bail if the file exists in the current directory.
2182              * note: this will not fail with dangling symlinks */
2183             if (stat(de->d_name, &st) == 0) {
2184                 closedir(dp);
2185                 return AFPERR_DIRNEMPT;
2186             }
2187
2188             strcpy(path + DOT_APPLEDOUBLE_LEN, de->d_name);
2189             if ((err = netatalk_unlink(path))) {
2190                 closedir(dp);
2191                 return err;
2192             }
2193         }
2194         closedir(dp);
2195     }
2196
2197     if ( (err = netatalk_rmdir( ".AppleDouble" ))  ) {
2198         return err;
2199     }
2200
2201     /* now get rid of dangling symlinks */
2202     if ((dp = opendir("."))) {
2203         while ((de = readdir(dp))) {
2204             /* skip this and previous directory */
2205             if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
2206                 continue;
2207
2208             /* bail if it's not a symlink */
2209             if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
2210                 closedir(dp);
2211                 return AFPERR_DIRNEMPT;
2212             }
2213
2214             if ((err = netatalk_unlink(de->d_name))) {
2215                 closedir(dp);
2216                 return err;
2217             }
2218         }
2219     }
2220
2221     if ( movecwd( vol, curdir->d_parent ) < 0 ) {
2222         err = afp_errno;
2223         goto delete_done;
2224     }
2225
2226     if ( !(err = netatalk_rmdir(fdir->d_u_name))) {
2227         dirchildremove(curdir, fdir);
2228         cnid_delete(vol->v_cdb, fdir->d_did);
2229         dir_remove( vol, fdir );
2230         err = AFP_OK;
2231     }
2232 delete_done:
2233     if (dp) {
2234         /* inode is used as key for cnid.
2235          * Close the descriptor only after cnid_delete
2236          * has been called. 
2237         */
2238         closedir(dp);
2239     }
2240     return err;
2241 }
2242
2243 int afp_mapid(obj, ibuf, ibuflen, rbuf, rbuflen )
2244 AFPObj      *obj;
2245 char    *ibuf, *rbuf;
2246 int             ibuflen, *rbuflen;
2247 {
2248     struct passwd       *pw;
2249     struct group        *gr;
2250     char                *name;
2251     u_int32_t           id;
2252     int                 len, sfunc;
2253     int         utf8 = 0;
2254     
2255     ibuf++;
2256     sfunc = (unsigned char) *ibuf++;
2257     memcpy( &id, ibuf, sizeof( id ));
2258
2259     id = ntohl(id);
2260     *rbuflen = 0;
2261
2262     if (sfunc == 3 || sfunc == 4) {
2263         if (afp_version < 30) {
2264             return( AFPERR_PARAM );
2265         }
2266         utf8 = 1;
2267     }
2268     if ( id != 0 ) {
2269         switch ( sfunc ) {
2270         case 1 :
2271         case 3 :/* unicode */
2272             if (( pw = getpwuid( id )) == NULL ) {
2273                 return( AFPERR_NOITEM );
2274             }
2275             len = convert_string_allocate( obj->options.unixcharset, ((sfunc == 1)?obj->options.maccharset:CH_UTF8_MAC),
2276                                             pw->pw_name, strlen(pw->pw_name), &name);
2277             break;
2278
2279         case 2 :
2280         case 4 : /* unicode */
2281             if (NULL == ( gr = (struct group *)getgrgid( id ))) {
2282                 return( AFPERR_NOITEM );
2283             }
2284             len = convert_string_allocate( obj->options.unixcharset, (sfunc == 1)?obj->options.maccharset:CH_UTF8_MAC,
2285                                             gr->gr_name, strlen(gr->gr_name), &name);
2286             break;
2287
2288         default :
2289             return( AFPERR_PARAM );
2290         }
2291         len = strlen( name );
2292
2293     } else {
2294         len = 0;
2295         name = NULL;
2296     }
2297     if (utf8) {
2298         u_int16_t tp = htons(len);
2299         memcpy(rbuf, &tp, sizeof(tp));
2300         rbuf += sizeof(tp);
2301         *rbuflen += 2;
2302     }
2303     else {
2304         *rbuf++ = len;
2305         *rbuflen += 1;
2306     }
2307     if ( len > 0 ) {
2308         memcpy( rbuf, name, len );
2309     }
2310     *rbuflen += len;
2311     if (name)
2312         free(name);
2313     return( AFP_OK );
2314 }
2315
2316 int afp_mapname(obj, ibuf, ibuflen, rbuf, rbuflen )
2317 AFPObj      *obj;
2318 char    *ibuf, *rbuf;
2319 int             ibuflen, *rbuflen;
2320 {
2321     struct passwd       *pw;
2322     struct group        *gr;
2323     int             len, sfunc;
2324     u_int32_t       id;
2325     u_int16_t       ulen;
2326
2327     ibuf++;
2328     sfunc = (unsigned char) *ibuf++;
2329     *rbuflen = 0;
2330     switch ( sfunc ) {
2331     case 1 : 
2332     case 2 : /* unicode */
2333         if (afp_version < 30) {
2334             return( AFPERR_PARAM );
2335         }
2336         memcpy(&ulen, ibuf, sizeof(ulen));
2337         len = ntohs(ulen);
2338         ibuf += 2;
2339         break;
2340     case 3 :
2341     case 4 :
2342         len = (unsigned char) *ibuf++;
2343         break;
2344     default :
2345         return( AFPERR_PARAM );
2346     }
2347
2348     ibuf[ len ] = '\0';
2349
2350     if ( len != 0 ) {
2351         switch ( sfunc ) {
2352         case 1 : /* unicode */
2353         case 3 :
2354             if (NULL == ( pw = (struct passwd *)getpwnam( ibuf )) ) {
2355                 return( AFPERR_NOITEM );
2356             }
2357             id = pw->pw_uid;
2358             break;
2359
2360         case 2 : /* unicode */
2361         case 4 :
2362             if (NULL == ( gr = (struct group *)getgrnam( ibuf ))) {
2363                 return( AFPERR_NOITEM );
2364             }
2365             id = gr->gr_gid;
2366             break;
2367         }
2368     } else {
2369         id = 0;
2370     }
2371     id = htonl(id);
2372     memcpy( rbuf, &id, sizeof( id ));
2373     *rbuflen = sizeof( id );
2374     return( AFP_OK );
2375 }
2376
2377 /* ------------------------------------
2378   variable DID support 
2379 */
2380 int afp_closedir(obj, ibuf, ibuflen, rbuf, rbuflen )
2381 AFPObj      *obj;
2382 char    *ibuf, *rbuf;
2383 int             ibuflen, *rbuflen;
2384 {
2385 #if 0
2386     struct vol   *vol;
2387     struct dir   *dir;
2388     u_int16_t    vid;
2389     u_int32_t    did;
2390 #endif /* 0 */
2391
2392     *rbuflen = 0;
2393
2394     /* do nothing as dids are static for the life of the process. */
2395 #if 0
2396     ibuf += 2;
2397
2398     memcpy(&vid,  ibuf, sizeof( vid ));
2399     ibuf += sizeof( vid );
2400     if (( vol = getvolbyvid( vid )) == NULL ) {
2401         return( AFPERR_PARAM );
2402     }
2403
2404     memcpy( &did, ibuf, sizeof( did ));
2405     ibuf += sizeof( did );
2406     if (( dir = dirlookup( vol, did )) == NULL ) {
2407         return( AFPERR_PARAM );
2408     }
2409
2410     /* dir_remove -- deletedid */
2411 #endif /* 0 */
2412
2413     return AFP_OK;
2414 }
2415
2416 /* did creation gets done automatically 
2417  * there's a pb again with case but move it to cname
2418 */
2419 int afp_opendir(obj, ibuf, ibuflen, rbuf, rbuflen )
2420 AFPObj      *obj;
2421 char    *ibuf, *rbuf;
2422 int             ibuflen, *rbuflen;
2423 {
2424     struct vol          *vol;
2425     struct dir          *parentdir;
2426     struct path         *path;
2427     u_int32_t           did;
2428     u_int16_t           vid;
2429
2430     *rbuflen = 0;
2431     ibuf += 2;
2432
2433     memcpy(&vid, ibuf, sizeof(vid));
2434     ibuf += sizeof( vid );
2435
2436     if (NULL == ( vol = getvolbyvid( vid )) ) {
2437         return( AFPERR_PARAM );
2438     }
2439
2440     memcpy(&did, ibuf, sizeof(did));
2441     ibuf += sizeof(did);
2442
2443     if (NULL == ( parentdir = dirlookup( vol, did )) ) {
2444         return afp_errno;
2445     }
2446
2447     if (NULL == ( path = cname( vol, parentdir, &ibuf )) ) {
2448         return get_afp_errno(AFPERR_PARAM);
2449     }
2450
2451     if ( *path->m_name != '\0' ) {
2452         return path_error(path, AFPERR_NOOBJ);
2453     }
2454
2455     if ( !path->st_valid && of_stat(path ) < 0 ) {
2456         return( AFPERR_NOOBJ );
2457     }
2458     if ( path->st_errno ) {
2459         return( AFPERR_NOOBJ );
2460     }
2461
2462     memcpy(rbuf, &curdir->d_did, sizeof(curdir->d_did));
2463     *rbuflen = sizeof(curdir->d_did);
2464     return AFP_OK;
2465 }