]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/file.c
remove compiler warnings
[netatalk.git] / etc / afpd / file.c
1 /*
2  * $Id: file.c,v 1.107 2008-12-03 18:35:44 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
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
25 #ifndef HAVE_MEMCPY
26 #define memcpy(d,s,n) bcopy ((s), (d), (n))
27 #define memmove(d,s,n) bcopy ((s), (d), (n))
28 #endif /* ! HAVE_MEMCPY */
29 #endif /* STDC_HEADERS */
30
31 #include <atalk/adouble.h>
32 #include <utime.h>
33 #include <dirent.h>
34 #include <errno.h>
35
36 #include <atalk/logger.h>
37 #include <sys/param.h>
38
39
40 #include <atalk/afp.h>
41 #include <atalk/util.h>
42 #include <atalk/cnid.h>
43 #include "directory.h"
44 #include "desktop.h"
45 #include "volume.h"
46 #include "fork.h"
47 #include "file.h"
48 #include "filedir.h"
49 #include "globals.h"
50 #include "unix.h"
51
52 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
53  * field         bytes        subfield    bytes
54  * 
55  * files:
56  * ioFlFndrInfo  16      ->       type    4  type field
57  *                             creator    4  creator field
58  *                               flags    2  finder flags:
59  *                                           alias, bundle, etc.
60  *                            location    4  location in window
61  *                              folder    2  window that contains file
62  * 
63  * ioFlXFndrInfo 16      ->     iconID    2  icon id
64  *                              unused    6  reserved 
65  *                              script    1  script system
66  *                              xflags    1  reserved
67  *                           commentID    2  comment id
68  *                           putawayID    4  home directory id
69  */
70
71 const u_char ufinderi[ADEDLEN_FINDERI] = {
72                               0, 0, 0, 0, 0, 0, 0, 0,
73                               1, 0, 0, 0, 0, 0, 0, 0,
74                               0, 0, 0, 0, 0, 0, 0, 0,
75                               0, 0, 0, 0, 0, 0, 0, 0
76                           };
77
78 static const u_char old_ufinderi[] = {
79                               'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
80                           };
81
82 /* ---------------------- 
83 */
84 static int default_type(void *finder) 
85 {
86     if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
87         return 1;
88     return 0;
89 }
90
91 /* FIXME path : unix or mac name ? (for now it's unix name ) */
92 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data)
93 {
94     struct extmap       *em;
95     void                *ad_finder = NULL;
96     int                 chk_ext = 0;
97     
98     if (adp)
99         ad_finder = ad_entry(adp, ADEID_FINDERI);
100
101     if (ad_finder) {
102         memcpy(data, ad_finder, ADEDLEN_FINDERI);
103         /* default type ? */
104         if (default_type(ad_finder)) 
105             chk_ext = 1;
106     }
107     else {
108         memcpy(data, ufinderi, ADEDLEN_FINDERI);
109         chk_ext = 1;
110         if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
111             u_int16_t ashort;
112             
113             ashort = htons(FINDERINFO_INVISIBLE);
114             memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
115         }
116     }
117     /** Only enter if no appledouble information and no finder information found. */
118     if (chk_ext && (em = getextmap( upath ))) {
119         memcpy(data, em->em_type, sizeof( em->em_type ));
120         memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
121     }
122     return data;
123 }
124
125 /* ---------------------
126 */
127 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, u_int32_t utf8) 
128 {
129     u_int32_t   aint;
130     char        *tp = NULL;
131     char        *src = name;
132     aint = strlen( name );
133
134     if (!utf8) {
135         /* want mac name */
136         if (utf8_encoding()) {
137             /* but name is an utf8 mac name */
138             char *u, *m;
139            
140             /* global static variable... */
141             tp = strdup(name);
142             if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
143                aint = 0;
144             }
145             else {
146                 aint = strlen(m);
147                 src = m;
148             }
149             
150         }
151         if (aint > MACFILELEN)
152             aint = MACFILELEN;
153         *data++ = aint;
154     }
155     else {
156         u_int16_t temp;
157
158         if (aint > 255)  /* FIXME safeguard, anyway if no ascii char it's game over*/
159            aint = 255;
160
161         utf8 = vol->v_mac?htonl(vol->v_mac->kTextEncoding):0;         /* htonl(utf8) */
162         memcpy(data, &utf8, sizeof(utf8));
163         data += sizeof(utf8);
164         
165         temp = htons(aint);
166         memcpy(data, &temp, sizeof(temp));
167         data += sizeof(temp);
168     }
169
170     memcpy( data, src, aint );
171     data += aint;
172     if (tp) {
173         strcpy(name, tp);
174         free(tp);
175     }
176     return data;
177 }
178
179 /*
180  * FIXME: PDINFO is UTF8 and doesn't need adp
181 */
182 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR)  |\
183                                   (1 << FILPBIT_CDATE) |\
184                                   (1 << FILPBIT_MDATE) |\
185                                   (1 << FILPBIT_BDATE) |\
186                                   (1 << FILPBIT_FINFO) |\
187                                   (1 << FILPBIT_RFLEN) |\
188                                   (1 << FILPBIT_EXTRFLEN) |\
189                                   (1 << FILPBIT_PDINFO) |\
190                                   (1 << FILPBIT_UNIXPR)))
191
192 /* -------------------------- */
193 u_int32_t get_id(struct vol *vol, struct adouble *adp,  const struct stat *st,
194              const cnid_t did, char *upath, const int len) 
195 {
196 u_int32_t aint = 0;
197
198 #if AD_VERSION > AD_VERSION1
199
200     if ((aint = ad_getid(adp, st->st_dev, st->st_ino, did, vol->v_stamp))) {
201         return aint;
202     }
203 #endif
204
205     if (vol->v_cdb != NULL) {
206             aint = cnid_add(vol->v_cdb, st, did, upath, len, aint);
207             /* Throw errors if cnid_add fails. */
208             if (aint == CNID_INVALID) {
209             switch (errno) {
210             case CNID_ERR_CLOSE: /* the db is closed */
211                 break;
212             case CNID_ERR_PARAM:
213                 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
214                 afp_errno = AFPERR_PARAM;
215                 return CNID_INVALID;
216             case CNID_ERR_PATH:
217                 afp_errno = AFPERR_PARAM;
218                 return CNID_INVALID;
219             default:
220                 afp_errno = AFPERR_MISC;
221                 return CNID_INVALID;
222             }
223         }
224 #if AD_VERSION > AD_VERSION1
225         else if (adp ) {
226             /* update the ressource fork
227              * for a folder adp is always null
228              */
229             if (ad_setid(adp, st->st_dev, st->st_ino, aint, did, vol->v_stamp)) {
230                 ad_flush(adp);
231             }
232         }
233 #endif    
234     }
235     return aint;
236 }
237              
238 /* -------------------------- */
239 int getmetadata(struct vol *vol,
240                  u_int16_t bitmap,
241                  struct path *path, struct dir *dir, 
242                  char *buf, int *buflen, struct adouble *adp)
243 {
244     char                *data, *l_nameoff = NULL, *upath;
245     char                *utf_nameoff = NULL;
246     int                 bit = 0;
247     u_int32_t           aint;
248     cnid_t              id = 0;
249     u_int16_t           ashort;
250     u_char              achar, fdType[4];
251     u_int32_t           utf8 = 0;
252     struct stat         *st;
253     struct maccess      ma;
254
255 #ifdef DEBUG
256     LOG(log_info, logtype_afpd, "begin getmetadata:");
257 #endif /* DEBUG */
258
259     upath = path->u_name;
260     st = &path->st;
261
262     data = buf;
263
264     if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
265          || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
266          || (bitmap & (1 << FILPBIT_FNUM))) {
267         if (!path->id)
268             id = get_id(vol, adp, st, dir->d_did, upath, strlen(upath));
269         else 
270             id = path->id;
271         if (id == 0)
272             return afp_errno;
273         if (!path->m_name) {
274             path->m_name = utompath(vol, upath, id, utf8_encoding());
275         }
276     }
277     while ( bitmap != 0 ) {
278         while (( bitmap & 1 ) == 0 ) {
279             bitmap = bitmap>>1;
280             bit++;
281         }
282
283         switch ( bit ) {
284         case FILPBIT_ATTR :
285             if ( adp ) {
286                 ad_getattr(adp, &ashort);
287             } else if (vol_inv_dots(vol) && *upath == '.') {
288                 ashort = htons(ATTRBIT_INVISIBLE);
289             } else
290                 ashort = 0;
291 #if 0
292             /* FIXME do we want a visual clue if the file is read only
293              */
294             struct maccess      ma;
295             accessmode( ".", &ma, dir , NULL);
296             if ((ma.ma_user & AR_UWRITE)) {
297                 accessmode( upath, &ma, dir , st);
298                 if (!(ma.ma_user & AR_UWRITE)) {
299                         ashort |= htons(ATTRBIT_NOWRITE);
300                 }
301             }
302 #endif
303             memcpy(data, &ashort, sizeof( ashort ));
304             data += sizeof( ashort );
305             break;
306
307         case FILPBIT_PDID :
308             memcpy(data, &dir->d_did, sizeof( u_int32_t ));
309             data += sizeof( u_int32_t );
310             break;
311
312         case FILPBIT_CDATE :
313             if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
314                 aint = AD_DATE_FROM_UNIX(st->st_mtime);
315             memcpy(data, &aint, sizeof( aint ));
316             data += sizeof( aint );
317             break;
318
319         case FILPBIT_MDATE :
320             if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
321                 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
322                    aint = AD_DATE_FROM_UNIX(st->st_mtime);
323                 }
324             } else {
325                 aint = AD_DATE_FROM_UNIX(st->st_mtime);
326             }
327             memcpy(data, &aint, sizeof( int ));
328             data += sizeof( int );
329             break;
330
331         case FILPBIT_BDATE :
332             if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
333                 aint = AD_DATE_START;
334             memcpy(data, &aint, sizeof( int ));
335             data += sizeof( int );
336             break;
337
338         case FILPBIT_FINFO :
339             get_finderinfo(vol, upath, adp, (char *)data);
340             data += ADEDLEN_FINDERI;
341             break;
342
343         case FILPBIT_LNAME :
344             l_nameoff = data;
345             data += sizeof( u_int16_t );
346             break;
347
348         case FILPBIT_SNAME :
349             memset(data, 0, sizeof(u_int16_t));
350             data += sizeof( u_int16_t );
351             break;
352
353         case FILPBIT_FNUM :
354             memcpy(data, &id, sizeof( id ));
355             data += sizeof( id );
356             break;
357
358         case FILPBIT_DFLEN :
359             if  (st->st_size > 0xffffffff)
360                aint = 0xffffffff;
361             else
362                aint = htonl( st->st_size );
363             memcpy(data, &aint, sizeof( aint ));
364             data += sizeof( aint );
365             break;
366
367         case FILPBIT_RFLEN :
368             if ( adp ) {
369                 if (adp->ad_rlen > 0xffffffff)
370                     aint = 0xffffffff;
371                 else
372                     aint = htonl( adp->ad_rlen);
373             } else {
374                 aint = 0;
375             }
376             memcpy(data, &aint, sizeof( aint ));
377             data += sizeof( aint );
378             break;
379
380             /* Current client needs ProDOS info block for this file.
381                Use simple heuristic and let the Mac "type" string tell
382                us what the PD file code should be.  Everything gets a
383                subtype of 0x0000 unless the original value was hashed
384                to "pXYZ" when we created it.  See IA, Ver 2.
385                <shirsch@adelphia.net> */
386         case FILPBIT_PDINFO :
387             if (afp_version >= 30) { /* UTF8 name */
388                 utf8 = kTextEncodingUTF8;
389                 utf_nameoff = data;
390                 data += sizeof( u_int16_t );
391                 aint = 0;
392                 memcpy(data, &aint, sizeof( aint ));
393                 data += sizeof( aint );
394             }
395             else {
396                 if ( adp ) {
397                     memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
398
399                     if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
400                         achar = '\x04';
401                         ashort = 0x0000;
402                     }
403                     else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
404                         achar = '\xff';
405                         ashort = 0x0000;
406                     }
407                     else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
408                         achar = '\xb3';
409                         ashort = 0x0000;
410                     }
411                     else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
412                         achar = '\x00';
413                         ashort = 0x0000;
414                     }
415                     else if ( fdType[0] == 'p' ) {
416                         achar = fdType[1];
417                         ashort = (fdType[2] * 256) + fdType[3];
418                     }
419                     else {
420                         achar = '\x00';
421                         ashort = 0x0000;
422                     }
423                 }
424                 else {
425                     achar = '\x00';
426                     ashort = 0x0000;
427                 }
428
429                 *data++ = achar;
430                 *data++ = 0;
431                 memcpy(data, &ashort, sizeof( ashort ));
432                 data += sizeof( ashort );
433                 memset(data, 0, sizeof( ashort ));
434                 data += sizeof( ashort );
435             }
436             break;
437         case FILPBIT_EXTDFLEN:
438             aint = htonl(st->st_size >> 32);
439             memcpy(data, &aint, sizeof( aint ));
440             data += sizeof( aint );
441             aint = htonl(st->st_size);
442             memcpy(data, &aint, sizeof( aint ));
443             data += sizeof( aint );
444             break;
445         case FILPBIT_EXTRFLEN:
446             aint = 0;
447             if (adp) 
448                 aint = htonl(adp->ad_rlen >> 32);
449             memcpy(data, &aint, sizeof( aint ));
450             data += sizeof( aint );
451             if (adp) 
452                 aint = htonl(adp->ad_rlen);
453             memcpy(data, &aint, sizeof( aint ));
454             data += sizeof( aint );
455             break;
456         case FILPBIT_UNIXPR :
457             /* accessmode may change st_mode with ACLs */
458             accessmode( upath, &ma, dir , st);
459
460             aint = htonl(st->st_uid);
461             memcpy( data, &aint, sizeof( aint ));
462             data += sizeof( aint );
463             aint = htonl(st->st_gid);
464             memcpy( data, &aint, sizeof( aint ));
465             data += sizeof( aint );
466
467             /* FIXME: ugly hack
468                type == slnk indicates an OSX style symlink, 
469                we have to add S_IFLNK to the mode, otherwise
470                10.3 clients freak out. */
471
472             aint = st->st_mode;
473             if (adp) {
474                 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
475                 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
476                     aint |= S_IFLNK;
477                 }
478             }
479             aint = htonl(aint);
480
481             memcpy( data, &aint, sizeof( aint ));
482             data += sizeof( aint );
483
484             *data++ = ma.ma_user;
485             *data++ = ma.ma_world;
486             *data++ = ma.ma_group;
487             *data++ = ma.ma_owner;
488             break;
489             
490         default :
491             return( AFPERR_BITMAP );
492         }
493         bitmap = bitmap>>1;
494         bit++;
495     }
496     if ( l_nameoff ) {
497         ashort = htons( data - buf );
498         memcpy(l_nameoff, &ashort, sizeof( ashort ));
499         data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
500     }
501     if ( utf_nameoff ) {
502         ashort = htons( data - buf );
503         memcpy(utf_nameoff, &ashort, sizeof( ashort ));
504         data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
505     }
506     *buflen = data - buf;
507     return (AFP_OK);
508 }
509                 
510 /* ----------------------- */
511 int getfilparams(struct vol *vol,
512                  u_int16_t bitmap,
513                  struct path *path, struct dir *dir, 
514                  char *buf, int *buflen )
515 {
516     struct adouble      ad, *adp;
517     struct ofork        *of;
518     char                    *upath;
519     int                 opened = 0;
520     int rc;    
521
522 #ifdef DEBUG
523     LOG(log_info, logtype_default, "begin getfilparams:");
524 #endif /* DEBUG */
525
526     opened = PARAM_NEED_ADP(bitmap);
527     adp = NULL;
528
529     if (opened) {
530         int flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0;
531         upath = path->u_name;
532         if ((of = of_findname(path))) {
533             adp = of->of_ad;
534         } else {
535             ad_init(&ad, vol->v_adouble, vol->v_ad_options);
536             adp = &ad;
537         }
538
539         if ( ad_metadata( upath, flags, adp) < 0 ) {
540             switch (errno) {
541             case EACCES:
542                 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
543                 upath, strerror(errno));
544                 return AFPERR_ACCESS;
545             case EIO:
546                 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
547                 /* fall through */
548             case ENOENT:
549             default:
550                 adp = NULL;
551                 break;
552             }
553         }
554     }
555     rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
556     if ( adp ) {
557         ad_close_metadata( adp);
558     }
559 #ifdef DEBUG
560     LOG(log_info, logtype_afpd, "end getfilparams:");
561 #endif /* DEBUG */
562
563     return( rc );
564 }
565
566 /* ----------------------------- */
567 int afp_createfile(obj, ibuf, ibuflen, rbuf, rbuflen )
568 AFPObj  *obj;
569 char    *ibuf, *rbuf _U_;
570 int     ibuflen _U_, *rbuflen;
571 {
572     struct adouble      ad, *adp;
573     struct vol          *vol;
574     struct dir          *dir;
575     struct ofork        *of = NULL;
576     char                *path, *upath;
577     int                 creatf, did, openf, retvalue = AFP_OK;
578     u_int16_t           vid;
579     int                 ret;
580     struct path         *s_path;
581     
582 #ifdef DEBUG
583     LOG(log_info, logtype_afpd, "begin afp_createfile:");
584 #endif /* DEBUG */
585
586     *rbuflen = 0;
587     ibuf++;
588     creatf = (unsigned char) *ibuf++;
589
590     memcpy(&vid, ibuf, sizeof( vid ));
591     ibuf += sizeof( vid );
592
593     if (NULL == ( vol = getvolbyvid( vid )) ) {
594         return( AFPERR_PARAM );
595     }
596
597     if (vol->v_flags & AFPVOL_RO)
598         return AFPERR_VLOCK;
599
600     memcpy(&did, ibuf, sizeof( did));
601     ibuf += sizeof( did );
602
603     if (NULL == ( dir = dirlookup( vol, did )) ) {
604         return afp_errno;
605     }
606
607     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
608         return get_afp_errno(AFPERR_PARAM);
609     }
610
611     if ( *s_path->m_name == '\0' ) {
612         return( AFPERR_BADTYPE );
613     }
614
615     upath = s_path->u_name;
616     if (0 != (ret = check_name(vol, upath))) 
617        return  ret;
618     
619     /* if upath is deleted we already in trouble anyway */
620     if ((of = of_findname(s_path))) {
621         adp = of->of_ad;
622     } else {
623         ad_init(&ad, vol->v_adouble, vol->v_ad_options);
624         adp = &ad;
625     }
626     if ( creatf) {
627         /* on a hard create, fail if file exists and is open */
628         if (of)
629             return AFPERR_BUSY;
630         openf = O_RDWR|O_CREAT|O_TRUNC;
631     } else {
632         /* on a soft create, if the file is open then ad_open won't fail
633            because open syscall is not called
634         */
635         if (of) {
636                 return AFPERR_EXIST;
637         }
638         openf = O_RDWR|O_CREAT|O_EXCL;
639     }
640
641     if ( ad_open( upath, vol_noadouble(vol)|ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF|ADFLAGS_CREATE,
642                   openf, 0666, adp) < 0 ) {
643         switch ( errno ) {
644         case EROFS:
645             return AFPERR_VLOCK;
646         case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
647             return ( AFPERR_NOOBJ );
648         case EEXIST :
649             return( AFPERR_EXIST );
650         case EACCES :
651             return( AFPERR_ACCESS );
652         case EDQUOT:
653         case ENOSPC :
654             return( AFPERR_DFULL );
655         default :
656             return( AFPERR_PARAM );
657         }
658     }
659     if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */
660          /* on noadouble volumes, just creating the data fork is ok */
661          if (vol_noadouble(vol)) {
662              ad_close( adp, ADFLAGS_DF );
663              goto createfile_done;
664          }
665          /* FIXME with hard create on an existing file, we already
666           * corrupted the data file.
667           */
668          netatalk_unlink( upath );
669          ad_close( adp, ADFLAGS_DF );
670          return AFPERR_ACCESS;
671     }
672
673     path = s_path->m_name;
674     ad_setname(adp, path);
675     ad_flush( adp);
676     ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
677
678 createfile_done:
679     curdir->offcnt++;
680
681 #ifdef DROPKLUDGE
682     if (vol->v_flags & AFPVOL_DROPBOX) {
683         retvalue = matchfile2dirperms(upath, vol, did);
684     }
685 #endif /* DROPKLUDGE */
686
687     setvoltime(obj, vol );
688
689 #ifdef DEBUG
690     LOG(log_info, logtype_afpd, "end afp_createfile");
691 #endif /* DEBUG */
692
693     return (retvalue);
694 }
695
696 int afp_setfilparams(obj, ibuf, ibuflen, rbuf, rbuflen )
697 AFPObj  *obj;
698 char    *ibuf, *rbuf _U_;
699 int     ibuflen _U_, *rbuflen;
700 {
701     struct vol  *vol;
702     struct dir  *dir;
703     struct path *s_path;
704     int         did, rc;
705     u_int16_t   vid, bitmap;
706
707 #ifdef DEBUG
708     LOG(log_info, logtype_afpd, "begin afp_setfilparams:");
709 #endif /* DEBUG */
710
711     *rbuflen = 0;
712     ibuf += 2;
713
714     memcpy(&vid, ibuf, sizeof( vid ));
715     ibuf += sizeof( vid );
716     if (NULL == ( vol = getvolbyvid( vid )) ) {
717         return( AFPERR_PARAM );
718     }
719
720     if (vol->v_flags & AFPVOL_RO)
721         return AFPERR_VLOCK;
722
723     memcpy(&did, ibuf, sizeof( did ));
724     ibuf += sizeof( did );
725     if (NULL == ( dir = dirlookup( vol, did )) ) {
726         return afp_errno; /* was AFPERR_NOOBJ */
727     }
728
729     memcpy(&bitmap, ibuf, sizeof( bitmap ));
730     bitmap = ntohs( bitmap );
731     ibuf += sizeof( bitmap );
732
733     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
734         return get_afp_errno(AFPERR_PARAM);
735     }
736
737     if (path_isadir(s_path)) {
738         return( AFPERR_BADTYPE ); /* it's a directory */
739     }
740
741     if ( s_path->st_errno != 0 ) {
742         return( AFPERR_NOOBJ );
743     }
744
745     if ((u_long)ibuf & 1 ) {
746         ibuf++;
747     }
748
749     if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
750         setvoltime(obj, vol );
751     }
752
753 #ifdef DEBUG
754     LOG(log_info, logtype_afpd, "end afp_setfilparams:");
755 #endif /* DEBUG */
756
757     return( rc );
758 }
759
760 /*
761  * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic  
762  * 
763 */
764 extern struct path Cur_Path;
765
766 int setfilparams(struct vol *vol,
767                  struct path *path, u_int16_t f_bitmap, char *buf )
768 {
769     struct adouble      ad, *adp;
770     struct extmap       *em;
771     int                 bit, isad = 1, err = AFP_OK;
772     char                *upath;
773     u_char              achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
774     u_int16_t           ashort, bshort;
775     u_int32_t           aint;
776     u_int32_t           upriv;
777     u_int16_t           upriv_bit = 0;
778     
779     struct utimbuf      ut;
780
781     int                 change_mdate = 0;
782     int                 change_parent_mdate = 0;
783     int                 newdate = 0;
784     struct timeval      tv;
785     uid_t               f_uid;
786     gid_t               f_gid;
787     u_int16_t           bitmap = f_bitmap;
788     u_int32_t           cdate,bdate;
789     u_char              finder_buf[32];
790
791 #ifdef DEBUG
792     LOG(log_info, logtype_afpd, "begin setfilparams:");
793 #endif /* DEBUG */
794
795     upath = path->u_name;
796     adp = of_ad(vol, path, &ad);
797     
798
799     if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
800         return AFPERR_ACCESS;
801     }
802
803     /* with unix priv maybe we have to change adouble file priv first */
804     bit = 0;
805     while ( bitmap != 0 ) {
806         while (( bitmap & 1 ) == 0 ) {
807             bitmap = bitmap>>1;
808             bit++;
809         }
810         switch(  bit ) {
811         case FILPBIT_ATTR :
812             change_mdate = 1;
813             memcpy(&ashort, buf, sizeof( ashort ));
814             buf += sizeof( ashort );
815             break;
816         case FILPBIT_CDATE :
817             change_mdate = 1;
818             memcpy(&cdate, buf, sizeof(cdate));
819             buf += sizeof( cdate );
820             break;
821         case FILPBIT_MDATE :
822             memcpy(&newdate, buf, sizeof( newdate ));
823             buf += sizeof( newdate );
824             break;
825         case FILPBIT_BDATE :
826             change_mdate = 1;
827             memcpy(&bdate, buf, sizeof( bdate));
828             buf += sizeof( bdate );
829             break;
830         case FILPBIT_FINFO :
831             change_mdate = 1;
832             memcpy(finder_buf, buf, 32 );
833             buf += 32;
834             break;
835         case FILPBIT_UNIXPR :
836             if (!vol_unix_priv(vol)) {
837                 /* this volume doesn't use unix priv */
838                 err = AFPERR_BITMAP;
839                 bitmap = 0;
840                 break;
841             }
842             change_mdate = 1;
843             change_parent_mdate = 1;
844
845             memcpy( &aint, buf, sizeof( aint ));
846             f_uid = ntohl (aint);
847             buf += sizeof( aint );
848             memcpy( &aint, buf, sizeof( aint ));
849             f_gid = ntohl (aint);
850             buf += sizeof( aint );
851             setfilowner(vol, f_uid, f_gid, path);
852
853             memcpy( &upriv, buf, sizeof( upriv ));
854             buf += sizeof( upriv );
855             upriv = ntohl (upriv);
856             if ((upriv & S_IWUSR)) {
857                 setfilunixmode(vol, path, upriv);
858             }
859             else {
860                 /* do it later */
861                 upriv_bit = 1;
862             }
863             break;
864         case FILPBIT_PDINFO :
865             if (afp_version < 30) { /* else it's UTF8 name */
866                 achar = *buf;
867                 buf += 2;
868                 /* Keep special case to support crlf translations */
869                 if ((unsigned int) achar == 0x04) {
870                     fdType = (u_char *)"TEXT";
871                     buf += 2;
872                 } else {
873                     xyy[0] = ( u_char ) 'p';
874                     xyy[1] = achar;
875                     xyy[3] = *buf++;
876                     xyy[2] = *buf++;
877                     fdType = xyy;
878                 }
879                 break;
880             }
881             /* fallthrough */
882         default :
883             err = AFPERR_BITMAP;
884             /* break while loop */
885             bitmap = 0;
886             break;
887         }
888
889         bitmap = bitmap>>1;
890         bit++;
891     }
892
893     /* second try with adouble open 
894     */
895     if ( ad_open_metadata( upath, vol_noadouble(vol), O_CREAT, adp) < 0) {
896         /* for some things, we don't need an adouble header */
897         if (f_bitmap & ~(1<<FILPBIT_MDATE)) {
898             return vol_noadouble(vol) ? AFP_OK : AFPERR_ACCESS;
899         }
900         isad = 0;
901     } else if ((ad_get_HF_flags( adp ) & O_CREAT) ) {
902         ad_setname(adp, path->m_name);
903     }
904     
905     bit = 0;
906     bitmap = f_bitmap;
907     while ( bitmap != 0 ) {
908         while (( bitmap & 1 ) == 0 ) {
909             bitmap = bitmap>>1;
910             bit++;
911         }
912
913         switch(  bit ) {
914         case FILPBIT_ATTR :
915             ad_getattr(adp, &bshort);
916             if ((bshort & htons(ATTRBIT_INVISIBLE)) !=
917                 (ashort & htons(ATTRBIT_INVISIBLE) & htons(ATTRBIT_SETCLR)) )
918                 change_parent_mdate = 1;
919             if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
920                 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
921             } else {
922                 bshort &= ~ashort;
923             }
924             ad_setattr(adp, bshort);
925             break;
926         case FILPBIT_CDATE :
927             ad_setdate(adp, AD_DATE_CREATE, cdate);
928             break;
929         case FILPBIT_MDATE :
930             break;
931         case FILPBIT_BDATE :
932             ad_setdate(adp, AD_DATE_BACKUP, bdate);
933             break;
934         case FILPBIT_FINFO :
935             if (default_type( ad_entry( adp, ADEID_FINDERI ))
936                     && ( 
937                      ((em = getextmap( path->m_name )) &&
938                       !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
939                       !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
940                      || ((em = getdefextmap()) &&
941                       !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
942                       !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
943             )) {
944                 memcpy(finder_buf, ufinderi, 8 );
945             }
946             memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
947             break;
948         case FILPBIT_UNIXPR :
949             if (upriv_bit) {
950                 setfilunixmode(vol, path, upriv);
951             }
952             break;
953         case FILPBIT_PDINFO :
954             if (afp_version < 30) { /* else it's UTF8 name */
955                 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
956                 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
957                 break;
958             }
959             /* fallthrough */
960         default :
961             err = AFPERR_BITMAP;
962             goto setfilparam_done;
963         }
964         bitmap = bitmap>>1;
965         bit++;
966     }
967
968 setfilparam_done:
969     if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
970        newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
971     }
972     if (newdate) {
973        if (isad)
974           ad_setdate(adp, AD_DATE_MODIFY, newdate);
975        ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
976        utime(upath, &ut);
977     }
978
979     if (isad) {
980         ad_flush( adp);
981         ad_close_metadata( adp);
982
983     }
984
985     if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
986         newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
987         bitmap = 1<<FILPBIT_MDATE;
988         setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
989     }
990
991 #ifdef DEBUG
992     LOG(log_info, logtype_afpd, "end setfilparams:");
993 #endif /* DEBUG */
994     return err;
995 }
996
997 /*
998  * renamefile and copyfile take the old and new unix pathnames
999  * and the new mac name.
1000  *
1001  * src         the source path 
1002  * dst         the dest filename in current dir
1003  * newname     the dest mac name
1004  * adp         adouble struct of src file, if open, or & zeroed one
1005  *
1006  */
1007 int renamefile(vol, src, dst, newname, adp )
1008 const struct vol *vol;
1009 char    *src, *dst, *newname;
1010 struct adouble    *adp;
1011 {
1012     int         rc;
1013
1014 #ifdef DEBUG
1015     LOG(log_info, logtype_afpd, "begin renamefile:");
1016 #endif /* DEBUG */
1017
1018     if ( unix_rename( src, dst ) < 0 ) {
1019         switch ( errno ) {
1020         case ENOENT :
1021             return( AFPERR_NOOBJ );
1022         case EPERM:
1023         case EACCES :
1024             return( AFPERR_ACCESS );
1025         case EROFS:
1026             return AFPERR_VLOCK;
1027         case EXDEV :                    /* Cross device move -- try copy */
1028            /* NOTE: with open file it's an error because after the copy we will 
1029             * get two files, it's fixable for our process (eg reopen the new file, get the
1030             * locks, and so on. But it doesn't solve the case with a second process
1031             */
1032             if (adp->ad_open_forks) {
1033                 /* FIXME  warning in syslog so admin'd know there's a conflict ?*/
1034                 return AFPERR_OLOCK; /* little lie */
1035             }
1036             if (AFP_OK != ( rc = copyfile(vol, vol, src, dst, newname, NULL )) ) {
1037                 /* on error copyfile delete dest */
1038                 return( rc );
1039             }
1040             return deletefile(vol, src, 0);
1041         default :
1042             return( AFPERR_PARAM );
1043         }
1044     }
1045
1046     if (vol->vfs->rf_renamefile(vol, src, dst) < 0 ) {
1047         int err;
1048         
1049         err = errno;        
1050         /* try to undo the data fork rename,
1051          * we know we are on the same device 
1052         */
1053         if (err) {
1054             unix_rename( dst, src ); 
1055             /* return the first error */
1056             switch ( err) {
1057             case ENOENT :
1058                 return AFPERR_NOOBJ;
1059             case EPERM:
1060             case EACCES :
1061                 return AFPERR_ACCESS ;
1062             case EROFS:
1063                 return AFPERR_VLOCK;
1064             default :
1065                 return AFPERR_PARAM ;
1066             }
1067         }
1068     }
1069
1070     /* don't care if we can't open the newly renamed ressource fork
1071      */
1072     if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
1073         ad_setname(adp, newname);
1074         ad_flush( adp );
1075         ad_close( adp, ADFLAGS_HF );
1076     }
1077 #ifdef DEBUG
1078     LOG(log_info, logtype_afpd, "end renamefile:");
1079 #endif /* DEBUG */
1080
1081     return( AFP_OK );
1082 }
1083
1084 /* ---------------- 
1085    convert a Mac long name to an utf8 name,
1086 */
1087 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1088 {
1089 size_t    outlen;
1090
1091     if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1092         return -1;
1093     }
1094     return outlen;
1095 }
1096
1097 /* ---------------- */
1098 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1099 {
1100 char        type = *ibuf;
1101 size_t      plen = 0;
1102 u_int16_t   len16;
1103 u_int32_t   hint;
1104
1105     if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1106         return -1;
1107     }
1108     ibuf++;
1109     switch (type) {
1110     case 2:
1111         if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1112             if (afp_version >= 30) {
1113                 /* convert it to UTF8 
1114                 */
1115                 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1116                    return -1;
1117             }
1118             else {
1119                 strncpy( newname, ibuf, plen );
1120                 newname[ plen ] = '\0';
1121             }
1122             if (strlen(newname) != plen) {
1123                 /* there's \0 in newname, e.g. it's a pathname not
1124                  * only a filename. 
1125                 */
1126                 return -1;
1127             }
1128         }
1129         break;
1130     case 3:
1131         memcpy(&hint, ibuf, sizeof(hint));
1132         ibuf += sizeof(hint);
1133            
1134         memcpy(&len16, ibuf, sizeof(len16));
1135         ibuf += sizeof(len16);
1136         plen = ntohs(len16);
1137         
1138         if (plen) {
1139             if (plen > AFPOBJ_TMPSIZ) {
1140                 return -1;
1141             }
1142             strncpy( newname, ibuf, plen );
1143             newname[ plen ] = '\0';
1144             if (strlen(newname) != plen) {
1145                 return -1;
1146             }
1147         }
1148         break;
1149     }
1150     return plen;
1151 }
1152
1153 /* -----------------------------------
1154 */
1155 int afp_copyfile(obj, ibuf, ibuflen, rbuf, rbuflen )
1156 AFPObj  *obj;
1157 char    *ibuf, *rbuf _U_;
1158 int     ibuflen _U_, *rbuflen;
1159 {
1160     struct vol  *s_vol, *d_vol;
1161     struct dir  *dir;
1162     char        *newname, *p, *upath;
1163     struct path *s_path;
1164     u_int32_t   sdid, ddid;
1165     int         err, retvalue = AFP_OK;
1166     u_int16_t   svid, dvid;
1167
1168     struct adouble ad, *adp;
1169     int denyreadset;
1170     
1171 #ifdef DEBUG
1172     LOG(log_info, logtype_afpd, "begin afp_copyfile:");
1173 #endif /* DEBUG */
1174
1175     *rbuflen = 0;
1176     ibuf += 2;
1177
1178     memcpy(&svid, ibuf, sizeof( svid ));
1179     ibuf += sizeof( svid );
1180     if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1181         return( AFPERR_PARAM );
1182     }
1183
1184     memcpy(&sdid, ibuf, sizeof( sdid ));
1185     ibuf += sizeof( sdid );
1186     if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1187         return afp_errno;
1188     }
1189
1190     memcpy(&dvid, ibuf, sizeof( dvid ));
1191     ibuf += sizeof( dvid );
1192     memcpy(&ddid, ibuf, sizeof( ddid ));
1193     ibuf += sizeof( ddid );
1194
1195     if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1196         return get_afp_errno(AFPERR_PARAM);
1197     }
1198     if ( path_isadir(s_path) ) {
1199         return( AFPERR_BADTYPE );
1200     }
1201
1202     /* don't allow copies when the file is open.
1203      * XXX: the spec only calls for read/deny write access.
1204      *      however, copyfile doesn't have any of that info,
1205      *      and locks need to stay coherent. as a result,
1206      *      we just balk if the file is opened already. */
1207
1208     adp = of_ad(s_vol, s_path, &ad);
1209
1210     if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1211         return AFPERR_DENYCONF;
1212     }
1213     denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 || 
1214                   getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1215     ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1216     if (denyreadset) {
1217         return AFPERR_DENYCONF;
1218     }
1219
1220     newname = obj->newtmp;
1221     strcpy( newname, s_path->m_name );
1222
1223     p = ctoupath( s_vol, curdir, newname );
1224     if (!p) {
1225         return AFPERR_PARAM;
1226     
1227     }
1228 #ifdef FORCE_UIDGID
1229     /* FIXME svid != dvid && dvid's user can't read svid */
1230 #endif
1231     if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1232         return( AFPERR_PARAM );
1233     }
1234
1235     if (d_vol->v_flags & AFPVOL_RO)
1236         return AFPERR_VLOCK;
1237
1238     if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1239         return afp_errno;
1240     }
1241
1242     if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1243         return get_afp_errno(AFPERR_NOOBJ); 
1244     }
1245     if ( *s_path->m_name != '\0' ) {
1246         path_error(s_path, AFPERR_PARAM);
1247     }
1248
1249     /* one of the handful of places that knows about the path type */
1250     if (copy_path_name(d_vol, newname, ibuf) < 0) {
1251         return( AFPERR_PARAM );
1252     }
1253     /* newname is always only a filename so curdir *is* its
1254      * parent folder
1255     */
1256     if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1257         return( AFPERR_PARAM );
1258     }
1259     if ( (err = copyfile(s_vol, d_vol, p, upath , newname, adp)) < 0 ) {
1260         return err;
1261     }
1262     curdir->offcnt++;
1263
1264 #ifdef DROPKLUDGE
1265     if (vol->v_flags & AFPVOL_DROPBOX) {
1266         retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1267     }
1268 #endif /* DROPKLUDGE */
1269
1270     setvoltime(obj, d_vol );
1271
1272 #ifdef DEBUG
1273     LOG(log_info, logtype_afpd, "end afp_copyfile:");
1274 #endif /* DEBUG */
1275
1276     return( retvalue );
1277 }
1278
1279 /* ----------------------- */
1280 static int copy_all(const int dfd, const void *buf,
1281                                size_t buflen)
1282 {
1283     ssize_t cc;
1284
1285 #ifdef DEBUG
1286     LOG(log_info, logtype_afpd, "begin copy_all:");
1287 #endif /* DEBUG */
1288
1289     while (buflen > 0) {
1290         if ((cc = write(dfd, buf, buflen)) < 0) {
1291             switch (errno) {
1292             case EINTR:
1293                 continue;
1294             default:
1295                 return -1;
1296             }
1297         }
1298         buflen -= cc;
1299     }
1300
1301 #ifdef DEBUG
1302     LOG(log_info, logtype_afpd, "end copy_all:");
1303 #endif /* DEBUG */
1304
1305     return 0;
1306 }
1307
1308 /* -------------------------- 
1309  * copy only the fork data stream
1310 */
1311 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1312 {
1313     ssize_t cc;
1314     int     err = 0;
1315     char    filebuf[8192];
1316     int     sfd, dfd;
1317     
1318     if (eid == ADEID_DFORK) {
1319         sfd = ad_data_fileno(ads);
1320         dfd = ad_data_fileno(add);
1321     }
1322     else {
1323         sfd = ad_reso_fileno(ads);
1324         dfd = ad_reso_fileno(add);
1325     }        
1326
1327     if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1328         return -1;
1329
1330     if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1331         return -1;
1332         
1333 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1334     /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1335     off_t   offset = 0;
1336     size_t  size;
1337     struct stat         st;
1338     #define BUF 128*1024*1024
1339
1340     if (fstat(sfd, &st) == 0) {
1341         
1342         while (1) {
1343             if ( offset >= st.st_size) {
1344                return 0;
1345             }
1346             size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1347             if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1348                 switch (errno) {
1349                 case ENOSYS:
1350                 case EINVAL:  /* there's no guarantee that all fs support sendfile */
1351                     goto no_sendfile;
1352                 default:
1353                     return -1;
1354                 }
1355             }
1356         }
1357     }
1358     no_sendfile:
1359     lseek(sfd, offset, SEEK_SET);
1360 #endif 
1361
1362     while (1) {
1363         if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1364             if (errno == EINTR)
1365                 continue;
1366             err = -1;
1367             break;
1368         }
1369
1370         if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1371             break;
1372         }
1373     }
1374     return err;
1375 }
1376
1377 /* ----------------------------------
1378  * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1379  * because we are doing it elsewhere.
1380  * currently if newname is NULL then adp is NULL. 
1381  */
1382 int copyfile(s_vol, d_vol, src, dst, newname, adp )
1383 const struct vol *s_vol, *d_vol;
1384 char    *src, *dst, *newname;
1385 struct adouble *adp;
1386 {
1387     struct adouble      ads, add;
1388     int                 err = 0;
1389     int                 ret_err = 0;
1390     int                 adflags;
1391     int                 stat_result;
1392     struct stat         st;
1393     
1394 #ifdef DEBUG
1395     LOG(log_info, logtype_afpd, "begin copyfile:");
1396 #endif /* DEBUG */
1397
1398     if (adp == NULL) {
1399         ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options); 
1400         adp = &ads;
1401     }
1402     ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1403     adflags = ADFLAGS_DF;
1404     if (newname) {
1405         adflags |= ADFLAGS_HF;
1406     }
1407
1408     if (ad_open(src , adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1409         ret_err = errno;
1410         goto done;
1411     }
1412
1413     if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1414         /* no resource fork, don't create one for dst file */
1415         adflags &= ~ADFLAGS_HF;
1416     }
1417
1418     stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1419
1420     if (stat_result < 0) {           
1421       /* unlikely but if fstat fails, the default file mode will be 0666. */
1422       st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1423     }
1424
1425     if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1426         ret_err = errno;
1427         ad_close( adp, adflags );
1428         if (EEXIST != ret_err) {
1429             deletefile(d_vol, dst, 0);
1430             goto done;
1431         }
1432         return AFPERR_EXIST;
1433     }
1434     /* XXX if the source and the dest don't use the same resource type it's broken
1435     */
1436     if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1437         /* copy the data fork */
1438         err = copy_fork(ADEID_DFORK, &add, adp);
1439     }
1440
1441     if (err < 0) {
1442        ret_err = errno;
1443     }
1444
1445     if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1446         /* set the new name in the resource fork */
1447         ad_copy_header(&add, adp);
1448         ad_setname(&add, newname);
1449         ad_flush( &add );
1450     }
1451     ad_close( adp, adflags );
1452
1453     if (ad_close( &add, adflags ) <0) {
1454        ret_err = errno;
1455     } 
1456
1457     if (ret_err) {
1458         deletefile(d_vol, dst, 0);
1459     }
1460     else if (stat_result == 0) {
1461         /* set dest modification date to src date */
1462         struct utimbuf  ut;
1463
1464         ut.actime = ut.modtime = st.st_mtime;
1465         utime(dst, &ut);
1466         /* FIXME netatalk doesn't use resource fork file date
1467          * but maybe we should set its modtime too.
1468         */
1469     }
1470
1471 #ifdef DEBUG
1472     LOG(log_info, logtype_afpd, "end copyfile:");
1473 #endif /* DEBUG */
1474
1475 done:
1476     switch ( ret_err ) {
1477     case 0:
1478         return AFP_OK;
1479     case EDQUOT:
1480     case EFBIG:
1481     case ENOSPC:
1482         return AFPERR_DFULL;
1483     case ENOENT:
1484         return AFPERR_NOOBJ;
1485     case EACCES:
1486         return AFPERR_ACCESS;
1487     case EROFS:
1488         return AFPERR_VLOCK;
1489     }
1490     return AFPERR_PARAM;
1491 }
1492
1493
1494 /* -----------------------------------
1495    vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1496    checkAttrib:   1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1497
1498    when deletefile is called we don't have lock on it, file is closed (for us)
1499    untrue if called by renamefile
1500    
1501    ad_open always try to open file RDWR first and ad_lock takes care of
1502    WRITE lock on read only file.
1503 */
1504
1505 static int check_attrib(struct adouble *adp)
1506 {
1507 u_int16_t   bshort = 0;
1508
1509         ad_getattr(adp, &bshort);
1510     /*
1511      * Does kFPDeleteInhibitBit (bit 8) set?
1512      */
1513         if ((bshort & htons(ATTRBIT_NODELETE))) {
1514                 return AFPERR_OLOCK;
1515         }
1516     if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1517         return AFPERR_BUSY;
1518         }
1519         return 0;
1520 }
1521
1522 int deletefile( vol, file, checkAttrib )
1523 const struct vol      *vol;
1524 char            *file;
1525 int         checkAttrib;
1526 {
1527     struct adouble      ad;
1528     struct adouble      *adp = &ad;
1529     int                 adflags, err = AFP_OK;
1530
1531 #ifdef DEBUG
1532     LOG(log_info, logtype_afpd, "begin deletefile:");
1533 #endif /* DEBUG */
1534
1535     /* try to open both forks at once */
1536     adflags = ADFLAGS_DF|ADFLAGS_HF;
1537     if (checkAttrib) {
1538         /* was EACCESS error try to get only metadata */
1539         ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1540         if ( ad_metadata( file , ADFLAGS_OPENFORKS, &ad) == 0 ) {
1541             ad_close( &ad, adflags );
1542             if ((err = check_attrib(&ad))) {
1543                return err;
1544             }
1545         }
1546     }
1547  
1548     while(1) {
1549         ad_init(&ad, vol->v_adouble, vol->v_ad_options);  /* OK */
1550         if ( ad_open( file, adflags, O_RDONLY, 0, &ad ) < 0 ) {
1551             switch (errno) {
1552             case ENOENT:
1553                 if (adflags == ADFLAGS_DF)
1554                     return AFPERR_NOOBJ;
1555                    
1556                 /* that failed. now try to open just the data fork */
1557                 adflags = ADFLAGS_DF;
1558                 continue;
1559
1560             case EACCES:
1561                 adp = NULL; /* maybe it's a file with no write mode for us */
1562                 break;      /* was return AFPERR_ACCESS;*/
1563             case EROFS:
1564                 return AFPERR_VLOCK;
1565             default:
1566                 return( AFPERR_PARAM );
1567             }
1568         }
1569         break;  /* from the while */
1570     }
1571
1572     if (adp && (adflags & ADFLAGS_HF) ) {
1573         /* FIXME we have a pb here because we want to know if a file is open 
1574          * there's a 'priority inversion' if you can't open the ressource fork RW
1575          * you can delete it if it's open because you can't get a write lock.
1576          * 
1577          * ADLOCK_FILELOCK means the whole ressource fork, not only after the 
1578          * metadatas
1579          *
1580          * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1581          */
1582         if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1583             ad_close( &ad, adflags );
1584             return( AFPERR_BUSY );
1585         }
1586     }
1587
1588     if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1589         err = AFPERR_BUSY;
1590     }
1591     else if (!(err = vol->vfs->rf_deletefile(vol, file)) && !(err = netatalk_unlink( file )) ) {
1592         cnid_t id;
1593         if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) 
1594         {
1595             cnid_delete(vol->v_cdb, id);
1596         }
1597     }
1598     if (adp)
1599         ad_close( &ad, adflags );  /* ad_close removes locks if any */
1600
1601 #ifdef DEBUG
1602     LOG(log_info, logtype_afpd, "end deletefile:");
1603 #endif /* DEBUG */
1604
1605     return err;
1606 }
1607
1608 /* ------------------------------------ */
1609 /* return a file id */
1610 int afp_createid(obj, ibuf, ibuflen, rbuf, rbuflen )
1611 AFPObj  *obj _U_;
1612 char    *ibuf, *rbuf;
1613 int     ibuflen _U_, *rbuflen;
1614 {
1615     struct stat         *st;
1616     struct vol          *vol;
1617     struct dir          *dir;
1618     char                *upath;
1619     int                 len;
1620     cnid_t              did, id;
1621     u_short             vid;
1622     struct path         *s_path;
1623
1624 #ifdef DEBUG
1625     LOG(log_info, logtype_afpd, "begin afp_createid:");
1626 #endif /* DEBUG */
1627
1628     *rbuflen = 0;
1629
1630     ibuf += 2;
1631
1632     memcpy(&vid, ibuf, sizeof(vid));
1633     ibuf += sizeof(vid);
1634
1635     if (NULL == ( vol = getvolbyvid( vid )) ) {
1636         return( AFPERR_PARAM);
1637     }
1638
1639     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1640         return AFPERR_NOOP;
1641     }
1642
1643     if (vol->v_flags & AFPVOL_RO)
1644         return AFPERR_VLOCK;
1645
1646     memcpy(&did, ibuf, sizeof( did ));
1647     ibuf += sizeof(did);
1648
1649     if (NULL == ( dir = dirlookup( vol, did )) ) {
1650         return afp_errno; /* was AFPERR_PARAM */
1651     }
1652
1653     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1654         return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1655     }
1656
1657     if ( path_isadir(s_path) ) {
1658         return( AFPERR_BADTYPE );
1659     }
1660
1661     upath = s_path->u_name;
1662     switch (s_path->st_errno) {
1663         case 0:
1664              break; /* success */
1665         case EPERM:
1666         case EACCES:
1667             return AFPERR_ACCESS;
1668         case ENOENT:
1669             return AFPERR_NOOBJ;
1670         default:
1671             return AFPERR_PARAM;
1672     }
1673     st = &s_path->st;
1674     if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1675         memcpy(rbuf, &id, sizeof(id));
1676         *rbuflen = sizeof(id);
1677         return AFPERR_EXISTID;
1678     }
1679
1680     if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1681         memcpy(rbuf, &id, sizeof(id));
1682         *rbuflen = sizeof(id);
1683         return AFP_OK;
1684     }
1685
1686 #ifdef DEBUG
1687     LOG(log_info, logtype_afpd, "ending afp_createid...:");
1688 #endif /* DEBUG */
1689     return afp_errno;
1690 }
1691
1692 /* ------------------------------- */
1693 struct reenum {
1694     struct vol *vol;
1695     cnid_t     did;
1696 };
1697
1698 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1699 {
1700     struct path   path;
1701     struct reenum *param = data;
1702     struct vol    *vol = param->vol;  
1703     cnid_t        did  = param->did;
1704     cnid_t        aint;
1705     
1706     memset(&path, 0, sizeof(path));
1707
1708     if ( stat(de->d_name, &path.st)<0 )
1709         return 0;
1710     
1711     /* update or add to cnid */
1712     aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1713
1714 #if AD_VERSION > AD_VERSION1
1715     if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1716         struct adouble  ad, *adp;
1717
1718         path.st_errno = 0;
1719         path.st_valid = 1;
1720         path.u_name = de->d_name;
1721             
1722         adp = of_ad(vol, &path, &ad);
1723             
1724         if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1725             return 0;
1726         }
1727         if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1728             ad_flush(adp);
1729         }
1730         ad_close_metadata(adp);
1731     }
1732 #endif /* AD_VERSION > AD_VERSION1 */
1733
1734     return 0;
1735 }
1736
1737 /* --------------------
1738  * Ok the db is out of synch with the dir.
1739  * but if it's a deleted file we don't want to do it again and again.
1740 */
1741 static int
1742 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1743 {
1744     int             ret;
1745     struct reenum   data;
1746     struct stat     st;
1747     
1748     if (vol->v_cdb == NULL) {
1749         return -1;
1750     }
1751     
1752     /* FIXME use of_statdir ? */
1753     if (stat(name, &st)) {
1754         return -1;
1755     }
1756
1757     if (dirreenumerate(dir, &st)) {
1758         /* we already did it once and the dir haven't been modified */
1759         return dir->offcnt;
1760     }
1761     
1762     data.vol = vol;
1763     data.did = dir->d_did;
1764     if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1765         setdiroffcnt(curdir, &st,  ret);
1766         dir->d_flags |= DIRF_CNID;
1767     }
1768
1769     return ret;
1770 }
1771
1772 /* ------------------------------
1773    resolve a file id */
1774 int afp_resolveid(obj, ibuf, ibuflen, rbuf, rbuflen )
1775 AFPObj  *obj _U_;
1776 char    *ibuf, *rbuf;
1777 int     ibuflen _U_, *rbuflen;
1778 {
1779     struct vol          *vol;
1780     struct dir          *dir;
1781     char                *upath;
1782     struct path         path;
1783     int                 err, buflen, retry=0;
1784     cnid_t              id, cnid;
1785     u_int16_t           vid, bitmap;
1786
1787     static char buffer[12 + MAXPATHLEN + 1];
1788     int len = 12 + MAXPATHLEN + 1;
1789
1790 #ifdef DEBUG
1791     LOG(log_info, logtype_afpd, "begin afp_resolveid:");
1792 #endif /* DEBUG */
1793
1794     *rbuflen = 0;
1795     ibuf += 2;
1796
1797     memcpy(&vid, ibuf, sizeof(vid));
1798     ibuf += sizeof(vid);
1799
1800     if (NULL == ( vol = getvolbyvid( vid )) ) {
1801         return( AFPERR_PARAM);
1802     }
1803
1804     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1805         return AFPERR_NOOP;
1806     }
1807
1808     memcpy(&id, ibuf, sizeof( id ));
1809     ibuf += sizeof(id);
1810     cnid = id;
1811     
1812     if (!id) {
1813         /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1814         return AFPERR_NOID;
1815     }
1816 retry:
1817     memset(&path, 0, sizeof(path));
1818     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1819         return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1820     }
1821
1822     if (NULL == ( dir = dirlookup( vol, id )) ) {
1823         return AFPERR_NOID; /* idem AFPERR_PARAM */
1824     }
1825     path.u_name = upath;
1826     if (movecwd(vol, dir) < 0) {
1827         switch (errno) {
1828         case EACCES:
1829         case EPERM:
1830             return AFPERR_ACCESS;
1831         case ENOENT:
1832             return AFPERR_NOID;
1833         default:
1834             return AFPERR_PARAM;
1835         }
1836     }
1837
1838     if ( of_stat(&path) < 0 ) {
1839 #ifdef ESTALE
1840         /* with nfs and our working directory is deleted */
1841         if (errno == ESTALE) {
1842             errno = ENOENT;
1843         }
1844 #endif  
1845         if ( errno == ENOENT && !retry) {
1846             /* cnid db is out of sync, reenumerate the directory and update ids */
1847             reenumerate_id(vol, ".", dir);
1848             id = cnid;
1849             retry = 1;
1850             goto retry;
1851         }
1852         switch (errno) {
1853         case EACCES:
1854         case EPERM:
1855             return AFPERR_ACCESS;
1856         case ENOENT:
1857             return AFPERR_NOID;
1858         default:
1859             return AFPERR_PARAM;
1860         }
1861     }
1862
1863     /* directories are bad */
1864     if (S_ISDIR(path.st.st_mode))
1865         return AFPERR_BADTYPE;
1866
1867     memcpy(&bitmap, ibuf, sizeof(bitmap));
1868     bitmap = ntohs( bitmap );
1869     if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1870         return AFPERR_NOID;
1871     }
1872     path.id = cnid;
1873     if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir, 
1874                             rbuf + sizeof(bitmap), &buflen))) {
1875         return err;
1876     }
1877     *rbuflen = buflen + sizeof(bitmap);
1878     memcpy(rbuf, ibuf, sizeof(bitmap));
1879
1880 #ifdef DEBUG
1881     LOG(log_info, logtype_afpd, "end afp_resolveid:");
1882 #endif /* DEBUG */
1883
1884     return AFP_OK;
1885 }
1886
1887 /* ------------------------------ */
1888 int afp_deleteid(obj, ibuf, ibuflen, rbuf, rbuflen )
1889 AFPObj  *obj _U_;
1890 char    *ibuf, *rbuf _U_;
1891 int     ibuflen _U_, *rbuflen;
1892 {
1893     struct stat         st;
1894     struct vol          *vol;
1895     struct dir          *dir;
1896     char                *upath;
1897     int                 err;
1898     cnid_t              id;
1899     cnid_t              fileid;
1900     u_short             vid;
1901     static char buffer[12 + MAXPATHLEN + 1];
1902     int len = 12 + MAXPATHLEN + 1;
1903
1904 #ifdef DEBUG
1905     LOG(log_info, logtype_afpd, "begin afp_deleteid:");
1906 #endif /* DEBUG */
1907
1908     *rbuflen = 0;
1909     ibuf += 2;
1910
1911     memcpy(&vid, ibuf, sizeof(vid));
1912     ibuf += sizeof(vid);
1913
1914     if (NULL == ( vol = getvolbyvid( vid )) ) {
1915         return( AFPERR_PARAM);
1916     }
1917
1918     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1919         return AFPERR_NOOP;
1920     }
1921
1922     if (vol->v_flags & AFPVOL_RO)
1923         return AFPERR_VLOCK;
1924
1925     memcpy(&id, ibuf, sizeof( id ));
1926     ibuf += sizeof(id);
1927     fileid = id;
1928
1929     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1930         return AFPERR_NOID;
1931     }
1932
1933     if (NULL == ( dir = dirlookup( vol, id )) ) {
1934         return( AFPERR_PARAM );
1935     }
1936
1937     err = AFP_OK;
1938     if ((movecwd(vol, dir) < 0) || (stat(upath, &st) < 0)) {
1939         switch (errno) {
1940         case EACCES:
1941         case EPERM:
1942             return AFPERR_ACCESS;
1943 #ifdef ESTALE
1944         case ESTALE:
1945 #endif  
1946         case ENOENT:
1947             /* still try to delete the id */
1948             err = AFPERR_NOOBJ;
1949             break;
1950         default:
1951             return AFPERR_PARAM;
1952         }
1953     }
1954     else if (S_ISDIR(st.st_mode)) /* directories are bad */
1955         return AFPERR_BADTYPE;
1956
1957     if (cnid_delete(vol->v_cdb, fileid)) {
1958         switch (errno) {
1959         case EROFS:
1960             return AFPERR_VLOCK;
1961         case EPERM:
1962         case EACCES:
1963             return AFPERR_ACCESS;
1964         default:
1965             return AFPERR_PARAM;
1966         }
1967     }
1968
1969 #ifdef DEBUG
1970     LOG(log_info, logtype_afpd, "end afp_deleteid:");
1971 #endif /* DEBUG */
1972
1973     return err;
1974 }
1975
1976 /* ------------------------------ */
1977 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
1978 {
1979     int             ret;
1980
1981     if (path->st_errno) {
1982         switch (path->st_errno) {
1983         case ENOENT:
1984             afp_errno = AFPERR_NOID;
1985             break;
1986         case EPERM:
1987         case EACCES:
1988             afp_errno = AFPERR_ACCESS;
1989             break;
1990         default:
1991             afp_errno = AFPERR_PARAM;
1992             break;
1993         }
1994         return NULL;
1995     }
1996     /* we use file_access both for legacy Mac perm and
1997      * for unix privilege, rename will take care of folder perms
1998     */
1999     if (file_access(path, OPENACC_WR ) < 0) {
2000         afp_errno = AFPERR_ACCESS;
2001         return NULL;
2002     }
2003     
2004     if ((*of = of_findname(path))) {
2005         /* reuse struct adouble so it won't break locks */
2006         adp = (*of)->of_ad;
2007     }
2008     else {
2009         ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
2010         /* META and HF */
2011         if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2012             /* from AFP spec.
2013              * The user must have the Read & Write privilege for both files in order to use this command.
2014              */
2015             ad_close(adp, ADFLAGS_HF);
2016             afp_errno = AFPERR_ACCESS;
2017             return NULL;
2018         }
2019     }
2020     return adp;
2021 }
2022
2023 #define APPLETEMP ".AppleTempXXXXXX"
2024
2025 int afp_exchangefiles(obj, ibuf, ibuflen, rbuf, rbuflen )
2026 AFPObj  *obj;
2027 char    *ibuf, *rbuf _U_ ;
2028 int     ibuflen _U_, *rbuflen;
2029 {
2030     struct stat         srcst, destst;
2031     struct vol          *vol;
2032     struct dir          *dir, *sdir;
2033     char                *spath, temp[17], *p;
2034     char                *supath, *upath;
2035     struct path         *path;
2036     int                 err;
2037     struct adouble      ads;
2038     struct adouble      add;
2039     struct adouble      *adsp = NULL;
2040     struct adouble      *addp = NULL;
2041     struct ofork        *s_of = NULL;
2042     struct ofork        *d_of = NULL;
2043     int                 crossdev;
2044     
2045     int                 slen, dlen;
2046     u_int32_t           sid, did;
2047     u_int16_t           vid;
2048
2049     uid_t              uid;
2050     gid_t              gid;
2051
2052 #ifdef DEBUG
2053     LOG(log_info, logtype_afpd, "begin afp_exchangefiles:");
2054 #endif /* DEBUG */
2055
2056     *rbuflen = 0;
2057     ibuf += 2;
2058
2059     memcpy(&vid, ibuf, sizeof(vid));
2060     ibuf += sizeof(vid);
2061
2062     if (NULL == ( vol = getvolbyvid( vid )) ) {
2063         return( AFPERR_PARAM);
2064     }
2065
2066     if ((vol->v_flags & AFPVOL_RO))
2067         return AFPERR_VLOCK;
2068
2069     /* source and destination dids */
2070     memcpy(&sid, ibuf, sizeof(sid));
2071     ibuf += sizeof(sid);
2072     memcpy(&did, ibuf, sizeof(did));
2073     ibuf += sizeof(did);
2074
2075     /* source file */
2076     if (NULL == (dir = dirlookup( vol, sid )) ) {
2077         return afp_errno; /* was AFPERR_PARAM */
2078     }
2079
2080     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2081         return get_afp_errno(AFPERR_NOOBJ); 
2082     }
2083
2084     if ( path_isadir(path) ) {
2085         return AFPERR_BADTYPE;   /* it's a dir */
2086     }
2087
2088     /* save some stuff */
2089     srcst = path->st;
2090     sdir = curdir;
2091     spath = obj->oldtmp;
2092     supath = obj->newtmp;
2093     strcpy(spath, path->m_name);
2094     strcpy(supath, path->u_name); /* this is for the cnid changing */
2095     p = absupath( vol, sdir, supath);
2096     if (!p) {
2097         /* pathname too long */
2098         return AFPERR_PARAM ;
2099     }
2100     
2101     ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2102     if (!(adsp = find_adouble( path, &s_of, &ads))) {
2103         return afp_errno;
2104     }
2105
2106     /* ***** from here we may have resource fork open **** */
2107     
2108     /* look for the source cnid. if it doesn't exist, don't worry about
2109      * it. */
2110     sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2111
2112     if (NULL == ( dir = dirlookup( vol, did )) ) {
2113         err = afp_errno; /* was AFPERR_PARAM */
2114         goto err_exchangefile;
2115     }
2116
2117     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2118         err = get_afp_errno(AFPERR_NOOBJ); 
2119         goto err_exchangefile;
2120     }
2121
2122     if ( path_isadir(path) ) {
2123         err = AFPERR_BADTYPE;
2124         goto err_exchangefile;
2125     }
2126
2127     /* FPExchangeFiles is the only call that can return the SameObj
2128      * error */
2129     if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2130         err = AFPERR_SAMEOBJ;
2131         goto err_exchangefile;
2132     }
2133
2134     ad_init(&add, vol->v_adouble, vol->v_ad_options);
2135     if (!(addp = find_adouble( path, &d_of, &add))) {
2136         err = afp_errno;
2137         goto err_exchangefile;
2138     }
2139     destst = path->st;
2140
2141     /* they are not on the same device and at least one is open
2142      * FIXME broken for for crossdev and adouble v2
2143      * return an error 
2144     */
2145     crossdev = (srcst.st_dev != destst.st_dev);
2146     if (/* (d_of || s_of)  && */ crossdev) {
2147         err = AFPERR_MISC;
2148         goto err_exchangefile;
2149     }
2150
2151     /* look for destination id. */
2152     upath = path->u_name;
2153     did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2154
2155     /* construct a temp name.
2156      * NOTE: the temp file will be in the dest file's directory. it
2157      * will also be inaccessible from AFP. */
2158     memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2159     if (!mktemp(temp)) {
2160         err = AFPERR_MISC;
2161         goto err_exchangefile;
2162     }
2163     
2164     if (crossdev) {
2165         /* FIXME we need to close fork for copy, both s_of and d_of are null */
2166        ad_close(adsp, ADFLAGS_HF);
2167        ad_close(addp, ADFLAGS_HF);
2168     }
2169
2170     /* now, quickly rename the file. we error if we can't. */
2171     if ((err = renamefile(vol, p, temp, temp, adsp)) != AFP_OK)
2172         goto err_exchangefile;
2173     of_rename(vol, s_of, sdir, spath, curdir, temp);
2174
2175     /* rename destination to source */
2176     if ((err = renamefile(vol, upath, p, spath, addp)) != AFP_OK)
2177         goto err_src_to_tmp;
2178     of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2179
2180     /* rename temp to destination */
2181     if ((err = renamefile(vol, temp, upath, path->m_name, adsp)) != AFP_OK)
2182         goto err_dest_to_src;
2183     of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2184
2185     /* id's need switching. src -> dest and dest -> src. 
2186      * we need to re-stat() if it was a cross device copy.
2187     */
2188     if (sid) {
2189         cnid_delete(vol->v_cdb, sid);
2190     }
2191     if (did) {
2192         cnid_delete(vol->v_cdb, did);
2193     }
2194     if ((did && ( (crossdev && stat( upath, &srcst) < 0) || 
2195                 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2196        ||
2197        (sid && ( (crossdev && stat(p, &destst) < 0) ||
2198                 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2199     ) {
2200         switch (errno) {
2201         case EPERM:
2202         case EACCES:
2203             err = AFPERR_ACCESS;
2204             break;
2205         default:
2206             err = AFPERR_PARAM;
2207         }
2208         goto err_temp_to_dest;
2209     }
2210     
2211     /* here we need to reopen if crossdev */
2212     if (sid && ad_setid(addp, destst.st_dev, destst.st_ino,  sid, sdir->d_did, vol->v_stamp)) 
2213     {
2214        ad_flush( addp );
2215     }
2216         
2217     if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino,  did, curdir->d_did, vol->v_stamp)) 
2218     {
2219        ad_flush( adsp );
2220     }
2221
2222     /* change perms, src gets dest perm and vice versa */
2223
2224     uid = geteuid();
2225     gid = getegid();
2226     if (seteuid(0)) {
2227         LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2228         err = AFP_OK; /* ignore error */
2229         goto err_temp_to_dest;
2230     }
2231
2232     /*
2233      * we need to exchange ACL entries as well
2234      */
2235     /* exchange_acls(vol, p, upath); */
2236
2237     path->st = srcst;
2238     path->st_valid = 1;
2239     path->st_errno = 0;
2240     path->m_name = NULL;
2241     path->u_name = upath;
2242
2243     setfilunixmode(vol, path, destst.st_mode);
2244     setfilowner(vol, destst.st_uid, destst.st_gid, path);
2245
2246     path->st = destst;
2247     path->st_valid = 1;
2248     path->st_errno = 0;
2249     path->u_name = p;
2250
2251     setfilunixmode(vol, path, srcst.st_mode);
2252     setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2253
2254     if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2255         LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2256         exit(EXITERR_SYS);
2257     }
2258
2259 #ifdef DEBUG
2260     LOG(log_info, logtype_afpd, "ending afp_exchangefiles:");
2261 #endif /* DEBUG */
2262
2263     err = AFP_OK;
2264     goto err_exchangefile;
2265
2266     /* all this stuff is so that we can unwind a failed operation
2267      * properly. */
2268 err_temp_to_dest:
2269     /* rename dest to temp */
2270     renamefile(vol, upath, temp, temp, adsp);
2271     of_rename(vol, s_of, curdir, upath, curdir, temp);
2272
2273 err_dest_to_src:
2274     /* rename source back to dest */
2275     renamefile(vol, p, upath, path->m_name, addp);
2276     of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2277
2278 err_src_to_tmp:
2279     /* rename temp back to source */
2280     renamefile(vol, temp, p, spath, adsp);
2281     of_rename(vol, s_of, curdir, temp, sdir, spath);
2282
2283 err_exchangefile:
2284     if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2285        ad_close(adsp, ADFLAGS_HF);
2286     }
2287     if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2288        ad_close(addp, ADFLAGS_HF);
2289     }
2290
2291     return err;
2292 }