]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/file.c
use log_debug rather than log_info for LOG inside ifdef DEBUG
[netatalk.git] / etc / afpd / file.c
1 /*
2  * $Id: file.c,v 1.118 2009-10-29 13:17:28 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 <utime.h>
32 #include <errno.h>
33 #include <sys/param.h>
34
35 #include <atalk/adouble.h>
36 #include <atalk/vfs.h>
37 #include <atalk/logger.h>
38 #include <atalk/afp.h>
39 #include <atalk/util.h>
40 #include <atalk/cnid.h>
41 #include <atalk/unix.h>
42
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, size_t *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_debug, 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, size_t *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_debug, 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_debug, logtype_afpd, "end getfilparams:");
561 #endif /* DEBUG */
562
563     return( rc );
564 }
565
566 /* ----------------------------- */
567 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
568 {
569     struct adouble      ad, *adp;
570     struct vol          *vol;
571     struct dir          *dir;
572     struct ofork        *of = NULL;
573     char                *path, *upath;
574     int                 creatf, did, openf, retvalue = AFP_OK;
575     u_int16_t           vid;
576     struct path         *s_path;
577     
578     *rbuflen = 0;
579     ibuf++;
580     creatf = (unsigned char) *ibuf++;
581
582     memcpy(&vid, ibuf, sizeof( vid ));
583     ibuf += sizeof( vid );
584
585     if (NULL == ( vol = getvolbyvid( vid )) ) {
586         return( AFPERR_PARAM );
587     }
588
589     if (vol->v_flags & AFPVOL_RO)
590         return AFPERR_VLOCK;
591
592     memcpy(&did, ibuf, sizeof( did));
593     ibuf += sizeof( did );
594
595     if (NULL == ( dir = dirlookup( vol, did )) ) {
596         return afp_errno;
597     }
598
599     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
600         return get_afp_errno(AFPERR_PARAM);
601     }
602
603     if ( *s_path->m_name == '\0' ) {
604         return( AFPERR_BADTYPE );
605     }
606
607     upath = s_path->u_name;
608     
609     /* if upath is deleted we already in trouble anyway */
610     if ((of = of_findname(s_path))) {
611         adp = of->of_ad;
612     } else {
613         ad_init(&ad, vol->v_adouble, vol->v_ad_options);
614         adp = &ad;
615     }
616     if ( creatf) {
617         /* on a hard create, fail if file exists and is open */
618         if (of)
619             return AFPERR_BUSY;
620         openf = O_RDWR|O_CREAT|O_TRUNC;
621     } else {
622         /* on a soft create, if the file is open then ad_open won't fail
623            because open syscall is not called
624         */
625         if (of) {
626                 return AFPERR_EXIST;
627         }
628         openf = O_RDWR|O_CREAT|O_EXCL;
629     }
630
631     if ( ad_open( upath, vol_noadouble(vol)|ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF|ADFLAGS_CREATE,
632                   openf, 0666, adp) < 0 ) {
633         switch ( errno ) {
634         case EROFS:
635             return AFPERR_VLOCK;
636         case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
637             return ( AFPERR_NOOBJ );
638         case EEXIST :
639             return( AFPERR_EXIST );
640         case EACCES :
641             return( AFPERR_ACCESS );
642         case EDQUOT:
643         case ENOSPC :
644             return( AFPERR_DFULL );
645         default :
646             return( AFPERR_PARAM );
647         }
648     }
649     if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */
650          /* on noadouble volumes, just creating the data fork is ok */
651          if (vol_noadouble(vol)) {
652              ad_close( adp, ADFLAGS_DF );
653              goto createfile_done;
654          }
655          /* FIXME with hard create on an existing file, we already
656           * corrupted the data file.
657           */
658          netatalk_unlink( upath );
659          ad_close( adp, ADFLAGS_DF );
660          return AFPERR_ACCESS;
661     }
662
663     path = s_path->m_name;
664     ad_setname(adp, path);
665     ad_flush( adp);
666     ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
667
668 createfile_done:
669     curdir->offcnt++;
670
671 #ifdef DROPKLUDGE
672     if (vol->v_flags & AFPVOL_DROPBOX) {
673         retvalue = matchfile2dirperms(upath, vol, did);
674     }
675 #endif /* DROPKLUDGE */
676
677     setvoltime(obj, vol );
678
679     return (retvalue);
680 }
681
682 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
683 {
684     struct vol  *vol;
685     struct dir  *dir;
686     struct path *s_path;
687     int         did, rc;
688     u_int16_t   vid, bitmap;
689
690     *rbuflen = 0;
691     ibuf += 2;
692
693     memcpy(&vid, ibuf, sizeof( vid ));
694     ibuf += sizeof( vid );
695     if (NULL == ( vol = getvolbyvid( vid )) ) {
696         return( AFPERR_PARAM );
697     }
698
699     if (vol->v_flags & AFPVOL_RO)
700         return AFPERR_VLOCK;
701
702     memcpy(&did, ibuf, sizeof( did ));
703     ibuf += sizeof( did );
704     if (NULL == ( dir = dirlookup( vol, did )) ) {
705         return afp_errno; /* was AFPERR_NOOBJ */
706     }
707
708     memcpy(&bitmap, ibuf, sizeof( bitmap ));
709     bitmap = ntohs( bitmap );
710     ibuf += sizeof( bitmap );
711
712     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
713         return get_afp_errno(AFPERR_PARAM);
714     }
715
716     if (path_isadir(s_path)) {
717         return( AFPERR_BADTYPE ); /* it's a directory */
718     }
719
720     if ( s_path->st_errno != 0 ) {
721         return( AFPERR_NOOBJ );
722     }
723
724     if ((u_long)ibuf & 1 ) {
725         ibuf++;
726     }
727
728     if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
729         setvoltime(obj, vol );
730     }
731
732     return( rc );
733 }
734
735 /*
736  * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic  
737  * 
738 */
739 extern struct path Cur_Path;
740
741 int setfilparams(struct vol *vol,
742                  struct path *path, u_int16_t f_bitmap, char *buf )
743 {
744     struct adouble      ad, *adp;
745     struct extmap       *em;
746     int                 bit, isad = 1, err = AFP_OK;
747     char                *upath;
748     u_char              achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
749     u_int16_t           ashort, bshort;
750     u_int32_t           aint;
751     u_int32_t           upriv;
752     u_int16_t           upriv_bit = 0;
753     
754     struct utimbuf      ut;
755
756     int                 change_mdate = 0;
757     int                 change_parent_mdate = 0;
758     int                 newdate = 0;
759     struct timeval      tv;
760     uid_t               f_uid;
761     gid_t               f_gid;
762     u_int16_t           bitmap = f_bitmap;
763     u_int32_t           cdate,bdate;
764     u_char              finder_buf[32];
765
766 #ifdef DEBUG
767     LOG(log_debug, logtype_afpd, "begin setfilparams:");
768 #endif /* DEBUG */
769
770     upath = path->u_name;
771     adp = of_ad(vol, path, &ad);
772     
773
774     if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
775         return AFPERR_ACCESS;
776     }
777
778     /* with unix priv maybe we have to change adouble file priv first */
779     bit = 0;
780     while ( bitmap != 0 ) {
781         while (( bitmap & 1 ) == 0 ) {
782             bitmap = bitmap>>1;
783             bit++;
784         }
785         switch(  bit ) {
786         case FILPBIT_ATTR :
787             change_mdate = 1;
788             memcpy(&ashort, buf, sizeof( ashort ));
789             buf += sizeof( ashort );
790             break;
791         case FILPBIT_CDATE :
792             change_mdate = 1;
793             memcpy(&cdate, buf, sizeof(cdate));
794             buf += sizeof( cdate );
795             break;
796         case FILPBIT_MDATE :
797             memcpy(&newdate, buf, sizeof( newdate ));
798             buf += sizeof( newdate );
799             break;
800         case FILPBIT_BDATE :
801             change_mdate = 1;
802             memcpy(&bdate, buf, sizeof( bdate));
803             buf += sizeof( bdate );
804             break;
805         case FILPBIT_FINFO :
806             change_mdate = 1;
807             memcpy(finder_buf, buf, 32 );
808             buf += 32;
809             break;
810         case FILPBIT_UNIXPR :
811             if (!vol_unix_priv(vol)) {
812                 /* this volume doesn't use unix priv */
813                 err = AFPERR_BITMAP;
814                 bitmap = 0;
815                 break;
816             }
817             change_mdate = 1;
818             change_parent_mdate = 1;
819
820             memcpy( &aint, buf, sizeof( aint ));
821             f_uid = ntohl (aint);
822             buf += sizeof( aint );
823             memcpy( &aint, buf, sizeof( aint ));
824             f_gid = ntohl (aint);
825             buf += sizeof( aint );
826             setfilowner(vol, f_uid, f_gid, path);
827
828             memcpy( &upriv, buf, sizeof( upriv ));
829             buf += sizeof( upriv );
830             upriv = ntohl (upriv);
831             if ((upriv & S_IWUSR)) {
832                 setfilunixmode(vol, path, upriv);
833             }
834             else {
835                 /* do it later */
836                 upriv_bit = 1;
837             }
838             break;
839         case FILPBIT_PDINFO :
840             if (afp_version < 30) { /* else it's UTF8 name */
841                 achar = *buf;
842                 buf += 2;
843                 /* Keep special case to support crlf translations */
844                 if ((unsigned int) achar == 0x04) {
845                     fdType = (u_char *)"TEXT";
846                     buf += 2;
847                 } else {
848                     xyy[0] = ( u_char ) 'p';
849                     xyy[1] = achar;
850                     xyy[3] = *buf++;
851                     xyy[2] = *buf++;
852                     fdType = xyy;
853                 }
854                 break;
855             }
856             /* fallthrough */
857         default :
858             err = AFPERR_BITMAP;
859             /* break while loop */
860             bitmap = 0;
861             break;
862         }
863
864         bitmap = bitmap>>1;
865         bit++;
866     }
867
868     /* second try with adouble open 
869     */
870     if ( ad_open_metadata( upath, vol_noadouble(vol), O_CREAT, adp) < 0) {
871         LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
872         /*
873          * For some things, we don't need an adouble header:
874          * - change of modification date
875          * - UNIX privs (Bug-ID #2863424)
876          */
877         if ( (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
878             LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
879             return vol_noadouble(vol) ? AFP_OK : AFPERR_ACCESS;
880         }
881         LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
882         isad = 0;
883     } else if ((ad_get_HF_flags( adp ) & O_CREAT) ) {
884         ad_setname(adp, path->m_name);
885     }
886     
887     bit = 0;
888     bitmap = f_bitmap;
889     while ( bitmap != 0 ) {
890         while (( bitmap & 1 ) == 0 ) {
891             bitmap = bitmap>>1;
892             bit++;
893         }
894
895         switch(  bit ) {
896         case FILPBIT_ATTR :
897             ad_getattr(adp, &bshort);
898             if ((bshort & htons(ATTRBIT_INVISIBLE)) !=
899                 (ashort & htons(ATTRBIT_INVISIBLE) & htons(ATTRBIT_SETCLR)) )
900                 change_parent_mdate = 1;
901             if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
902                 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
903             } else {
904                 bshort &= ~ashort;
905             }
906             ad_setattr(adp, bshort);
907             break;
908         case FILPBIT_CDATE :
909             ad_setdate(adp, AD_DATE_CREATE, cdate);
910             break;
911         case FILPBIT_MDATE :
912             break;
913         case FILPBIT_BDATE :
914             ad_setdate(adp, AD_DATE_BACKUP, bdate);
915             break;
916         case FILPBIT_FINFO :
917             if (default_type( ad_entry( adp, ADEID_FINDERI ))
918                     && ( 
919                      ((em = getextmap( path->m_name )) &&
920                       !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
921                       !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
922                      || ((em = getdefextmap()) &&
923                       !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
924                       !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
925             )) {
926                 memcpy(finder_buf, ufinderi, 8 );
927             }
928             memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
929             break;
930         case FILPBIT_UNIXPR :
931             if (upriv_bit) {
932                 setfilunixmode(vol, path, upriv);
933             }
934             break;
935         case FILPBIT_PDINFO :
936             if (afp_version < 30) { /* else it's UTF8 name */
937                 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
938                 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
939                 break;
940             }
941             /* fallthrough */
942         default :
943             err = AFPERR_BITMAP;
944             goto setfilparam_done;
945         }
946         bitmap = bitmap>>1;
947         bit++;
948     }
949
950 setfilparam_done:
951     if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
952        newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
953     }
954     if (newdate) {
955        if (isad)
956           ad_setdate(adp, AD_DATE_MODIFY, newdate);
957        ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
958        utime(upath, &ut);
959     }
960
961     if (isad) {
962         ad_flush( adp);
963         ad_close_metadata( adp);
964
965     }
966
967     if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
968         newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
969         bitmap = 1<<FILPBIT_MDATE;
970         setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
971     }
972
973 #ifdef DEBUG
974     LOG(log_debug, logtype_afpd, "end setfilparams:");
975 #endif /* DEBUG */
976     return err;
977 }
978
979 /*
980  * renamefile and copyfile take the old and new unix pathnames
981  * and the new mac name.
982  *
983  * src         the source path 
984  * dst         the dest filename in current dir
985  * newname     the dest mac name
986  * adp         adouble struct of src file, if open, or & zeroed one
987  *
988  */
989 int renamefile(const struct vol *vol, char *src, char *dst, char *newname, struct adouble *adp)
990 {
991     int         rc;
992
993 #ifdef DEBUG
994     LOG(log_debug, logtype_afpd, "begin renamefile:");
995 #endif /* DEBUG */
996
997     if ( unix_rename( src, dst ) < 0 ) {
998         switch ( errno ) {
999         case ENOENT :
1000             return( AFPERR_NOOBJ );
1001         case EPERM:
1002         case EACCES :
1003             return( AFPERR_ACCESS );
1004         case EROFS:
1005             return AFPERR_VLOCK;
1006         case EXDEV :                    /* Cross device move -- try copy */
1007            /* NOTE: with open file it's an error because after the copy we will 
1008             * get two files, it's fixable for our process (eg reopen the new file, get the
1009             * locks, and so on. But it doesn't solve the case with a second process
1010             */
1011             if (adp->ad_open_forks) {
1012                 /* FIXME  warning in syslog so admin'd know there's a conflict ?*/
1013                 return AFPERR_OLOCK; /* little lie */
1014             }
1015             if (AFP_OK != ( rc = copyfile(vol, vol, src, dst, newname, NULL )) ) {
1016                 /* on error copyfile delete dest */
1017                 return( rc );
1018             }
1019             return deletefile(vol, src, 0);
1020         default :
1021             return( AFPERR_PARAM );
1022         }
1023     }
1024
1025     if (vol->vfs->vfs_renamefile(vol, src, dst) < 0 ) {
1026         int err;
1027         
1028         err = errno;        
1029         /* try to undo the data fork rename,
1030          * we know we are on the same device 
1031         */
1032         if (err) {
1033             unix_rename( dst, src ); 
1034             /* return the first error */
1035             switch ( err) {
1036             case ENOENT :
1037                 return AFPERR_NOOBJ;
1038             case EPERM:
1039             case EACCES :
1040                 return AFPERR_ACCESS ;
1041             case EROFS:
1042                 return AFPERR_VLOCK;
1043             default :
1044                 return AFPERR_PARAM ;
1045             }
1046         }
1047     }
1048
1049     /* don't care if we can't open the newly renamed ressource fork
1050      */
1051     if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
1052         ad_setname(adp, newname);
1053         ad_flush( adp );
1054         ad_close( adp, ADFLAGS_HF );
1055     }
1056 #ifdef DEBUG
1057     LOG(log_debug, logtype_afpd, "end renamefile:");
1058 #endif /* DEBUG */
1059
1060     return( AFP_OK );
1061 }
1062
1063 /* ---------------- 
1064    convert a Mac long name to an utf8 name,
1065 */
1066 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1067 {
1068 size_t    outlen;
1069
1070     if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1071         return -1;
1072     }
1073     return outlen;
1074 }
1075
1076 /* ---------------- */
1077 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1078 {
1079 char        type = *ibuf;
1080 size_t      plen = 0;
1081 u_int16_t   len16;
1082 u_int32_t   hint;
1083
1084     if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1085         return -1;
1086     }
1087     ibuf++;
1088     switch (type) {
1089     case 2:
1090         if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1091             if (afp_version >= 30) {
1092                 /* convert it to UTF8 
1093                 */
1094                 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1095                    return -1;
1096             }
1097             else {
1098                 strncpy( newname, ibuf, plen );
1099                 newname[ plen ] = '\0';
1100             }
1101             if (strlen(newname) != plen) {
1102                 /* there's \0 in newname, e.g. it's a pathname not
1103                  * only a filename. 
1104                 */
1105                 return -1;
1106             }
1107         }
1108         break;
1109     case 3:
1110         memcpy(&hint, ibuf, sizeof(hint));
1111         ibuf += sizeof(hint);
1112            
1113         memcpy(&len16, ibuf, sizeof(len16));
1114         ibuf += sizeof(len16);
1115         plen = ntohs(len16);
1116         
1117         if (plen) {
1118             if (plen > AFPOBJ_TMPSIZ) {
1119                 return -1;
1120             }
1121             strncpy( newname, ibuf, plen );
1122             newname[ plen ] = '\0';
1123             if (strlen(newname) != plen) {
1124                 return -1;
1125             }
1126         }
1127         break;
1128     }
1129     return plen;
1130 }
1131
1132 /* -----------------------------------
1133 */
1134 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1135 {
1136     struct vol  *s_vol, *d_vol;
1137     struct dir  *dir;
1138     char        *newname, *p, *upath;
1139     struct path *s_path;
1140     u_int32_t   sdid, ddid;
1141     int         err, retvalue = AFP_OK;
1142     u_int16_t   svid, dvid;
1143
1144     struct adouble ad, *adp;
1145     int denyreadset;
1146     
1147     *rbuflen = 0;
1148     ibuf += 2;
1149
1150     memcpy(&svid, ibuf, sizeof( svid ));
1151     ibuf += sizeof( svid );
1152     if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1153         return( AFPERR_PARAM );
1154     }
1155
1156     memcpy(&sdid, ibuf, sizeof( sdid ));
1157     ibuf += sizeof( sdid );
1158     if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1159         return afp_errno;
1160     }
1161
1162     memcpy(&dvid, ibuf, sizeof( dvid ));
1163     ibuf += sizeof( dvid );
1164     memcpy(&ddid, ibuf, sizeof( ddid ));
1165     ibuf += sizeof( ddid );
1166
1167     if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1168         return get_afp_errno(AFPERR_PARAM);
1169     }
1170     if ( path_isadir(s_path) ) {
1171         return( AFPERR_BADTYPE );
1172     }
1173
1174     /* don't allow copies when the file is open.
1175      * XXX: the spec only calls for read/deny write access.
1176      *      however, copyfile doesn't have any of that info,
1177      *      and locks need to stay coherent. as a result,
1178      *      we just balk if the file is opened already. */
1179
1180     adp = of_ad(s_vol, s_path, &ad);
1181
1182     if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1183         return AFPERR_DENYCONF;
1184     }
1185     denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 || 
1186                   getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1187     ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1188     if (denyreadset) {
1189         return AFPERR_DENYCONF;
1190     }
1191
1192     newname = obj->newtmp;
1193     strcpy( newname, s_path->m_name );
1194
1195     p = ctoupath( s_vol, curdir, newname );
1196     if (!p) {
1197         return AFPERR_PARAM;
1198     
1199     }
1200 #ifdef FORCE_UIDGID
1201     /* FIXME svid != dvid && dvid's user can't read svid */
1202 #endif
1203     if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1204         return( AFPERR_PARAM );
1205     }
1206
1207     if (d_vol->v_flags & AFPVOL_RO)
1208         return AFPERR_VLOCK;
1209
1210     if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1211         return afp_errno;
1212     }
1213
1214     if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1215         return get_afp_errno(AFPERR_NOOBJ); 
1216     }
1217     if ( *s_path->m_name != '\0' ) {
1218         path_error(s_path, AFPERR_PARAM);
1219     }
1220
1221     /* one of the handful of places that knows about the path type */
1222     if (copy_path_name(d_vol, newname, ibuf) < 0) {
1223         return( AFPERR_PARAM );
1224     }
1225     /* newname is always only a filename so curdir *is* its
1226      * parent folder
1227     */
1228     if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1229         return( AFPERR_PARAM );
1230     }
1231     if ( (err = copyfile(s_vol, d_vol, p, upath , newname, adp)) < 0 ) {
1232         return err;
1233     }
1234     curdir->offcnt++;
1235
1236 #ifdef DROPKLUDGE
1237     if (vol->v_flags & AFPVOL_DROPBOX) {
1238         retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1239     }
1240 #endif /* DROPKLUDGE */
1241
1242     setvoltime(obj, d_vol );
1243
1244     return( retvalue );
1245 }
1246
1247 /* ----------------------- */
1248 static int copy_all(const int dfd, const void *buf,
1249                                size_t buflen)
1250 {
1251     ssize_t cc;
1252
1253 #ifdef DEBUG
1254     LOG(log_debug, logtype_afpd, "begin copy_all:");
1255 #endif /* DEBUG */
1256
1257     while (buflen > 0) {
1258         if ((cc = write(dfd, buf, buflen)) < 0) {
1259             switch (errno) {
1260             case EINTR:
1261                 continue;
1262             default:
1263                 return -1;
1264             }
1265         }
1266         buflen -= cc;
1267     }
1268
1269 #ifdef DEBUG
1270     LOG(log_debug, logtype_afpd, "end copy_all:");
1271 #endif /* DEBUG */
1272
1273     return 0;
1274 }
1275
1276 /* -------------------------- 
1277  * copy only the fork data stream
1278 */
1279 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1280 {
1281     ssize_t cc;
1282     int     err = 0;
1283     char    filebuf[8192];
1284     int     sfd, dfd;
1285     
1286     if (eid == ADEID_DFORK) {
1287         sfd = ad_data_fileno(ads);
1288         dfd = ad_data_fileno(add);
1289     }
1290     else {
1291         sfd = ad_reso_fileno(ads);
1292         dfd = ad_reso_fileno(add);
1293     }        
1294
1295     if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1296         return -1;
1297
1298     if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1299         return -1;
1300         
1301 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1302     /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1303     off_t   offset = 0;
1304     size_t  size;
1305     struct stat         st;
1306     #define BUF 128*1024*1024
1307
1308     if (fstat(sfd, &st) == 0) {
1309         
1310         while (1) {
1311             if ( offset >= st.st_size) {
1312                return 0;
1313             }
1314             size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1315             if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1316                 switch (errno) {
1317                 case ENOSYS:
1318                 case EINVAL:  /* there's no guarantee that all fs support sendfile */
1319                     goto no_sendfile;
1320                 default:
1321                     return -1;
1322                 }
1323             }
1324         }
1325     }
1326     no_sendfile:
1327     lseek(sfd, offset, SEEK_SET);
1328 #endif 
1329
1330     while (1) {
1331         if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1332             if (errno == EINTR)
1333                 continue;
1334             err = -1;
1335             break;
1336         }
1337
1338         if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1339             break;
1340         }
1341     }
1342     return err;
1343 }
1344
1345 /* ----------------------------------
1346  * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1347  * because we are doing it elsewhere.
1348  * currently if newname is NULL then adp is NULL. 
1349  */
1350 int copyfile(const struct vol *s_vol, const struct vol*d_vol, 
1351     char *src, char *dst, char *newname, struct adouble *adp)
1352 {
1353     struct adouble      ads, add;
1354     int                 err = 0;
1355     int                 ret_err = 0;
1356     int                 adflags;
1357     int                 stat_result;
1358     struct stat         st;
1359     
1360 #ifdef DEBUG
1361     LOG(log_debug, logtype_afpd, "begin copyfile:");
1362 #endif /* DEBUG */
1363
1364     if (adp == NULL) {
1365         ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options); 
1366         adp = &ads;
1367     }
1368     ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1369     adflags = ADFLAGS_DF;
1370     if (newname) {
1371         adflags |= ADFLAGS_HF;
1372     }
1373
1374     if (ad_open(src , adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1375         ret_err = errno;
1376         goto done;
1377     }
1378
1379     if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1380         /* no resource fork, don't create one for dst file */
1381         adflags &= ~ADFLAGS_HF;
1382     }
1383
1384     stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1385
1386     if (stat_result < 0) {           
1387       /* unlikely but if fstat fails, the default file mode will be 0666. */
1388       st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1389     }
1390
1391     if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1392         ret_err = errno;
1393         ad_close( adp, adflags );
1394         if (EEXIST != ret_err) {
1395             deletefile(d_vol, dst, 0);
1396             goto done;
1397         }
1398         return AFPERR_EXIST;
1399     }
1400     
1401     /*
1402      * XXX if the source and the dest don't use the same resource type it's broken
1403      */
1404     if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1405         /* copy the data fork */
1406         if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1407             err = d_vol->vfs->vfs_copyfile(d_vol, src, dst);
1408         }
1409     }
1410
1411     if (err < 0) {
1412        ret_err = errno;
1413     }
1414
1415     if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1416         /* set the new name in the resource fork */
1417         ad_copy_header(&add, adp);
1418         ad_setname(&add, newname);
1419         ad_flush( &add );
1420     }
1421     ad_close( adp, adflags );
1422
1423     if (ad_close( &add, adflags ) <0) {
1424        ret_err = errno;
1425     } 
1426
1427     if (ret_err) {
1428         deletefile(d_vol, dst, 0);
1429     }
1430     else if (stat_result == 0) {
1431         /* set dest modification date to src date */
1432         struct utimbuf  ut;
1433
1434         ut.actime = ut.modtime = st.st_mtime;
1435         utime(dst, &ut);
1436         /* FIXME netatalk doesn't use resource fork file date
1437          * but maybe we should set its modtime too.
1438         */
1439     }
1440
1441 #ifdef DEBUG
1442     LOG(log_debug, logtype_afpd, "end copyfile:");
1443 #endif /* DEBUG */
1444
1445 done:
1446     switch ( ret_err ) {
1447     case 0:
1448         return AFP_OK;
1449     case EDQUOT:
1450     case EFBIG:
1451     case ENOSPC:
1452         return AFPERR_DFULL;
1453     case ENOENT:
1454         return AFPERR_NOOBJ;
1455     case EACCES:
1456         return AFPERR_ACCESS;
1457     case EROFS:
1458         return AFPERR_VLOCK;
1459     }
1460     return AFPERR_PARAM;
1461 }
1462
1463
1464 /* -----------------------------------
1465    vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1466    checkAttrib:   1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1467
1468    when deletefile is called we don't have lock on it, file is closed (for us)
1469    untrue if called by renamefile
1470    
1471    ad_open always try to open file RDWR first and ad_lock takes care of
1472    WRITE lock on read only file.
1473 */
1474
1475 static int check_attrib(struct adouble *adp)
1476 {
1477 u_int16_t   bshort = 0;
1478
1479         ad_getattr(adp, &bshort);
1480     /*
1481      * Does kFPDeleteInhibitBit (bit 8) set?
1482      */
1483         if ((bshort & htons(ATTRBIT_NODELETE))) {
1484                 return AFPERR_OLOCK;
1485         }
1486     if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1487         return AFPERR_BUSY;
1488         }
1489         return 0;
1490 }
1491
1492 int deletefile(const struct vol *vol, char *file, int checkAttrib)
1493 {
1494     struct adouble      ad;
1495     struct adouble      *adp = &ad;
1496     int                 adflags, err = AFP_OK;
1497
1498 #ifdef DEBUG
1499     LOG(log_debug, logtype_afpd, "begin deletefile:");
1500 #endif /* DEBUG */
1501
1502     /* try to open both forks at once */
1503     adflags = ADFLAGS_DF|ADFLAGS_HF;
1504     if (checkAttrib) {
1505         /* was EACCESS error try to get only metadata */
1506         ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1507         if ( ad_metadata( file , ADFLAGS_OPENFORKS, &ad) == 0 ) {
1508             ad_close( &ad, adflags );
1509             if ((err = check_attrib(&ad))) {
1510                return err;
1511             }
1512         }
1513     }
1514  
1515     while(1) {
1516         ad_init(&ad, vol->v_adouble, vol->v_ad_options);  /* OK */
1517         if ( ad_open( file, adflags, O_RDONLY, 0, &ad ) < 0 ) {
1518             switch (errno) {
1519             case ENOENT:
1520                 if (adflags == ADFLAGS_DF)
1521                     return AFPERR_NOOBJ;
1522                    
1523                 /* that failed. now try to open just the data fork */
1524                 adflags = ADFLAGS_DF;
1525                 continue;
1526
1527             case EACCES:
1528                 adp = NULL; /* maybe it's a file with no write mode for us */
1529                 break;      /* was return AFPERR_ACCESS;*/
1530             case EROFS:
1531                 return AFPERR_VLOCK;
1532             default:
1533                 return( AFPERR_PARAM );
1534             }
1535         }
1536         break;  /* from the while */
1537     }
1538
1539     if (adp && (adflags & ADFLAGS_HF) ) {
1540         /* FIXME we have a pb here because we want to know if a file is open 
1541          * there's a 'priority inversion' if you can't open the ressource fork RW
1542          * you can delete it if it's open because you can't get a write lock.
1543          * 
1544          * ADLOCK_FILELOCK means the whole ressource fork, not only after the 
1545          * metadatas
1546          *
1547          * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1548          */
1549         if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1550             ad_close( &ad, adflags );
1551             return( AFPERR_BUSY );
1552         }
1553     }
1554
1555     if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1556         err = AFPERR_BUSY;
1557     }
1558     else if (!(err = vol->vfs->vfs_deletefile(vol, file)) && !(err = netatalk_unlink( file )) ) {
1559         cnid_t id;
1560         if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) 
1561         {
1562             cnid_delete(vol->v_cdb, id);
1563         }
1564     }
1565     if (adp)
1566         ad_close( &ad, adflags );  /* ad_close removes locks if any */
1567
1568 #ifdef DEBUG
1569     LOG(log_debug, logtype_afpd, "end deletefile:");
1570 #endif /* DEBUG */
1571
1572     return err;
1573 }
1574
1575 /* ------------------------------------ */
1576 /* return a file id */
1577 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1578 {
1579     struct stat         *st;
1580     struct vol          *vol;
1581     struct dir          *dir;
1582     char                *upath;
1583     int                 len;
1584     cnid_t              did, id;
1585     u_short             vid;
1586     struct path         *s_path;
1587
1588     *rbuflen = 0;
1589
1590     ibuf += 2;
1591
1592     memcpy(&vid, ibuf, sizeof(vid));
1593     ibuf += sizeof(vid);
1594
1595     if (NULL == ( vol = getvolbyvid( vid )) ) {
1596         return( AFPERR_PARAM);
1597     }
1598
1599     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1600         return AFPERR_NOOP;
1601     }
1602
1603     if (vol->v_flags & AFPVOL_RO)
1604         return AFPERR_VLOCK;
1605
1606     memcpy(&did, ibuf, sizeof( did ));
1607     ibuf += sizeof(did);
1608
1609     if (NULL == ( dir = dirlookup( vol, did )) ) {
1610         return afp_errno; /* was AFPERR_PARAM */
1611     }
1612
1613     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1614         return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1615     }
1616
1617     if ( path_isadir(s_path) ) {
1618         return( AFPERR_BADTYPE );
1619     }
1620
1621     upath = s_path->u_name;
1622     switch (s_path->st_errno) {
1623         case 0:
1624              break; /* success */
1625         case EPERM:
1626         case EACCES:
1627             return AFPERR_ACCESS;
1628         case ENOENT:
1629             return AFPERR_NOOBJ;
1630         default:
1631             return AFPERR_PARAM;
1632     }
1633     st = &s_path->st;
1634     if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1635         memcpy(rbuf, &id, sizeof(id));
1636         *rbuflen = sizeof(id);
1637         return AFPERR_EXISTID;
1638     }
1639
1640     if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1641         memcpy(rbuf, &id, sizeof(id));
1642         *rbuflen = sizeof(id);
1643         return AFP_OK;
1644     }
1645
1646     return afp_errno;
1647 }
1648
1649 /* ------------------------------- */
1650 struct reenum {
1651     struct vol *vol;
1652     cnid_t     did;
1653 };
1654
1655 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1656 {
1657     struct path   path;
1658     struct reenum *param = data;
1659     struct vol    *vol = param->vol;  
1660     cnid_t        did  = param->did;
1661     cnid_t        aint;
1662     
1663     memset(&path, 0, sizeof(path));
1664
1665     if ( stat(de->d_name, &path.st)<0 )
1666         return 0;
1667     
1668     /* update or add to cnid */
1669     aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1670
1671 #if AD_VERSION > AD_VERSION1
1672     if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1673         struct adouble  ad, *adp;
1674
1675         path.st_errno = 0;
1676         path.st_valid = 1;
1677         path.u_name = de->d_name;
1678             
1679         adp = of_ad(vol, &path, &ad);
1680             
1681         if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1682             return 0;
1683         }
1684         if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1685             ad_flush(adp);
1686         }
1687         ad_close_metadata(adp);
1688     }
1689 #endif /* AD_VERSION > AD_VERSION1 */
1690
1691     return 0;
1692 }
1693
1694 /* --------------------
1695  * Ok the db is out of synch with the dir.
1696  * but if it's a deleted file we don't want to do it again and again.
1697 */
1698 static int
1699 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1700 {
1701     int             ret;
1702     struct reenum   data;
1703     struct stat     st;
1704     
1705     if (vol->v_cdb == NULL) {
1706         return -1;
1707     }
1708     
1709     /* FIXME use of_statdir ? */
1710     if (stat(name, &st)) {
1711         return -1;
1712     }
1713
1714     if (dirreenumerate(dir, &st)) {
1715         /* we already did it once and the dir haven't been modified */
1716         return dir->offcnt;
1717     }
1718     
1719     data.vol = vol;
1720     data.did = dir->d_did;
1721     if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1722         setdiroffcnt(curdir, &st,  ret);
1723         dir->d_flags |= DIRF_CNID;
1724     }
1725
1726     return ret;
1727 }
1728
1729 /* ------------------------------
1730    resolve a file id */
1731 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1732 {
1733     struct vol          *vol;
1734     struct dir          *dir;
1735     char                *upath;
1736     struct path         path;
1737     int                 err, retry=0;
1738     size_t              buflen;
1739     cnid_t              id, cnid;
1740     u_int16_t           vid, bitmap;
1741
1742     static char buffer[12 + MAXPATHLEN + 1];
1743     int len = 12 + MAXPATHLEN + 1;
1744
1745     *rbuflen = 0;
1746     ibuf += 2;
1747
1748     memcpy(&vid, ibuf, sizeof(vid));
1749     ibuf += sizeof(vid);
1750
1751     if (NULL == ( vol = getvolbyvid( vid )) ) {
1752         return( AFPERR_PARAM);
1753     }
1754
1755     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1756         return AFPERR_NOOP;
1757     }
1758
1759     memcpy(&id, ibuf, sizeof( id ));
1760     ibuf += sizeof(id);
1761     cnid = id;
1762     
1763     if (!id) {
1764         /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1765         return AFPERR_NOID;
1766     }
1767 retry:
1768     memset(&path, 0, sizeof(path));
1769     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1770         return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1771     }
1772
1773     if (NULL == ( dir = dirlookup( vol, id )) ) {
1774         return AFPERR_NOID; /* idem AFPERR_PARAM */
1775     }
1776     path.u_name = upath;
1777     if (movecwd(vol, dir) < 0) {
1778         switch (errno) {
1779         case EACCES:
1780         case EPERM:
1781             return AFPERR_ACCESS;
1782         case ENOENT:
1783             return AFPERR_NOID;
1784         default:
1785             return AFPERR_PARAM;
1786         }
1787     }
1788
1789     if ( of_stat(&path) < 0 ) {
1790 #ifdef ESTALE
1791         /* with nfs and our working directory is deleted */
1792         if (errno == ESTALE) {
1793             errno = ENOENT;
1794         }
1795 #endif  
1796         if ( errno == ENOENT && !retry) {
1797             /* cnid db is out of sync, reenumerate the directory and update ids */
1798             reenumerate_id(vol, ".", dir);
1799             id = cnid;
1800             retry = 1;
1801             goto retry;
1802         }
1803         switch (errno) {
1804         case EACCES:
1805         case EPERM:
1806             return AFPERR_ACCESS;
1807         case ENOENT:
1808             return AFPERR_NOID;
1809         default:
1810             return AFPERR_PARAM;
1811         }
1812     }
1813
1814     /* directories are bad */
1815     if (S_ISDIR(path.st.st_mode)) {
1816         /* OS9 and OSX don't return the same error code  */
1817         return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1818     }
1819
1820     memcpy(&bitmap, ibuf, sizeof(bitmap));
1821     bitmap = ntohs( bitmap );
1822     if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1823         return AFPERR_NOID;
1824     }
1825     path.id = cnid;
1826     if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir, 
1827                             rbuf + sizeof(bitmap), &buflen))) {
1828         return err;
1829     }
1830     *rbuflen = buflen + sizeof(bitmap);
1831     memcpy(rbuf, ibuf, sizeof(bitmap));
1832
1833     return AFP_OK;
1834 }
1835
1836 /* ------------------------------ */
1837 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1838 {
1839     struct stat         st;
1840     struct vol          *vol;
1841     struct dir          *dir;
1842     char                *upath;
1843     int                 err;
1844     cnid_t              id;
1845     cnid_t              fileid;
1846     u_short             vid;
1847     static char buffer[12 + MAXPATHLEN + 1];
1848     int len = 12 + MAXPATHLEN + 1;
1849
1850     *rbuflen = 0;
1851     ibuf += 2;
1852
1853     memcpy(&vid, ibuf, sizeof(vid));
1854     ibuf += sizeof(vid);
1855
1856     if (NULL == ( vol = getvolbyvid( vid )) ) {
1857         return( AFPERR_PARAM);
1858     }
1859
1860     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1861         return AFPERR_NOOP;
1862     }
1863
1864     if (vol->v_flags & AFPVOL_RO)
1865         return AFPERR_VLOCK;
1866
1867     memcpy(&id, ibuf, sizeof( id ));
1868     ibuf += sizeof(id);
1869     fileid = id;
1870
1871     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1872         return AFPERR_NOID;
1873     }
1874
1875     if (NULL == ( dir = dirlookup( vol, id )) ) {
1876         return( AFPERR_PARAM );
1877     }
1878
1879     err = AFP_OK;
1880     if ((movecwd(vol, dir) < 0) || (stat(upath, &st) < 0)) {
1881         switch (errno) {
1882         case EACCES:
1883         case EPERM:
1884             return AFPERR_ACCESS;
1885 #ifdef ESTALE
1886         case ESTALE:
1887 #endif  
1888         case ENOENT:
1889             /* still try to delete the id */
1890             err = AFPERR_NOOBJ;
1891             break;
1892         default:
1893             return AFPERR_PARAM;
1894         }
1895     }
1896     else if (S_ISDIR(st.st_mode)) /* directories are bad */
1897         return AFPERR_BADTYPE;
1898
1899     if (cnid_delete(vol->v_cdb, fileid)) {
1900         switch (errno) {
1901         case EROFS:
1902             return AFPERR_VLOCK;
1903         case EPERM:
1904         case EACCES:
1905             return AFPERR_ACCESS;
1906         default:
1907             return AFPERR_PARAM;
1908         }
1909     }
1910
1911     return err;
1912 }
1913
1914 /* ------------------------------ */
1915 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
1916 {
1917     int             ret;
1918
1919     if (path->st_errno) {
1920         switch (path->st_errno) {
1921         case ENOENT:
1922             afp_errno = AFPERR_NOID;
1923             break;
1924         case EPERM:
1925         case EACCES:
1926             afp_errno = AFPERR_ACCESS;
1927             break;
1928         default:
1929             afp_errno = AFPERR_PARAM;
1930             break;
1931         }
1932         return NULL;
1933     }
1934     /* we use file_access both for legacy Mac perm and
1935      * for unix privilege, rename will take care of folder perms
1936     */
1937     if (file_access(path, OPENACC_WR ) < 0) {
1938         afp_errno = AFPERR_ACCESS;
1939         return NULL;
1940     }
1941     
1942     if ((*of = of_findname(path))) {
1943         /* reuse struct adouble so it won't break locks */
1944         adp = (*of)->of_ad;
1945     }
1946     else {
1947         ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
1948         /* META and HF */
1949         if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1950             /* from AFP spec.
1951              * The user must have the Read & Write privilege for both files in order to use this command.
1952              */
1953             ad_close(adp, ADFLAGS_HF);
1954             afp_errno = AFPERR_ACCESS;
1955             return NULL;
1956         }
1957     }
1958     return adp;
1959 }
1960
1961 #define APPLETEMP ".AppleTempXXXXXX"
1962
1963 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1964 {
1965     struct stat         srcst, destst;
1966     struct vol          *vol;
1967     struct dir          *dir, *sdir;
1968     char                *spath, temp[17], *p;
1969     char                *supath, *upath;
1970     struct path         *path;
1971     int                 err;
1972     struct adouble      ads;
1973     struct adouble      add;
1974     struct adouble      *adsp = NULL;
1975     struct adouble      *addp = NULL;
1976     struct ofork        *s_of = NULL;
1977     struct ofork        *d_of = NULL;
1978     int                 crossdev;
1979     
1980     int                 slen, dlen;
1981     u_int32_t           sid, did;
1982     u_int16_t           vid;
1983
1984     uid_t              uid;
1985     gid_t              gid;
1986
1987     *rbuflen = 0;
1988     ibuf += 2;
1989
1990     memcpy(&vid, ibuf, sizeof(vid));
1991     ibuf += sizeof(vid);
1992
1993     if (NULL == ( vol = getvolbyvid( vid )) ) {
1994         return( AFPERR_PARAM);
1995     }
1996
1997     if ((vol->v_flags & AFPVOL_RO))
1998         return AFPERR_VLOCK;
1999
2000     /* source and destination dids */
2001     memcpy(&sid, ibuf, sizeof(sid));
2002     ibuf += sizeof(sid);
2003     memcpy(&did, ibuf, sizeof(did));
2004     ibuf += sizeof(did);
2005
2006     /* source file */
2007     if (NULL == (dir = dirlookup( vol, sid )) ) {
2008         return afp_errno; /* was AFPERR_PARAM */
2009     }
2010
2011     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2012         return get_afp_errno(AFPERR_NOOBJ); 
2013     }
2014
2015     if ( path_isadir(path) ) {
2016         return AFPERR_BADTYPE;   /* it's a dir */
2017     }
2018
2019     /* save some stuff */
2020     srcst = path->st;
2021     sdir = curdir;
2022     spath = obj->oldtmp;
2023     supath = obj->newtmp;
2024     strcpy(spath, path->m_name);
2025     strcpy(supath, path->u_name); /* this is for the cnid changing */
2026     p = absupath( vol, sdir, supath);
2027     if (!p) {
2028         /* pathname too long */
2029         return AFPERR_PARAM ;
2030     }
2031     
2032     ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2033     if (!(adsp = find_adouble( path, &s_of, &ads))) {
2034         return afp_errno;
2035     }
2036
2037     /* ***** from here we may have resource fork open **** */
2038     
2039     /* look for the source cnid. if it doesn't exist, don't worry about
2040      * it. */
2041     sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2042
2043     if (NULL == ( dir = dirlookup( vol, did )) ) {
2044         err = afp_errno; /* was AFPERR_PARAM */
2045         goto err_exchangefile;
2046     }
2047
2048     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2049         err = get_afp_errno(AFPERR_NOOBJ); 
2050         goto err_exchangefile;
2051     }
2052
2053     if ( path_isadir(path) ) {
2054         err = AFPERR_BADTYPE;
2055         goto err_exchangefile;
2056     }
2057
2058     /* FPExchangeFiles is the only call that can return the SameObj
2059      * error */
2060     if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2061         err = AFPERR_SAMEOBJ;
2062         goto err_exchangefile;
2063     }
2064
2065     ad_init(&add, vol->v_adouble, vol->v_ad_options);
2066     if (!(addp = find_adouble( path, &d_of, &add))) {
2067         err = afp_errno;
2068         goto err_exchangefile;
2069     }
2070     destst = path->st;
2071
2072     /* they are not on the same device and at least one is open
2073      * FIXME broken for for crossdev and adouble v2
2074      * return an error 
2075     */
2076     crossdev = (srcst.st_dev != destst.st_dev);
2077     if (/* (d_of || s_of)  && */ crossdev) {
2078         err = AFPERR_MISC;
2079         goto err_exchangefile;
2080     }
2081
2082     /* look for destination id. */
2083     upath = path->u_name;
2084     did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2085
2086     /* construct a temp name.
2087      * NOTE: the temp file will be in the dest file's directory. it
2088      * will also be inaccessible from AFP. */
2089     memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2090     if (!mktemp(temp)) {
2091         err = AFPERR_MISC;
2092         goto err_exchangefile;
2093     }
2094     
2095     if (crossdev) {
2096         /* FIXME we need to close fork for copy, both s_of and d_of are null */
2097        ad_close(adsp, ADFLAGS_HF);
2098        ad_close(addp, ADFLAGS_HF);
2099     }
2100
2101     /* now, quickly rename the file. we error if we can't. */
2102     if ((err = renamefile(vol, p, temp, temp, adsp)) != AFP_OK)
2103         goto err_exchangefile;
2104     of_rename(vol, s_of, sdir, spath, curdir, temp);
2105
2106     /* rename destination to source */
2107     if ((err = renamefile(vol, upath, p, spath, addp)) != AFP_OK)
2108         goto err_src_to_tmp;
2109     of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2110
2111     /* rename temp to destination */
2112     if ((err = renamefile(vol, temp, upath, path->m_name, adsp)) != AFP_OK)
2113         goto err_dest_to_src;
2114     of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2115
2116     /* id's need switching. src -> dest and dest -> src. 
2117      * we need to re-stat() if it was a cross device copy.
2118     */
2119     if (sid) {
2120         cnid_delete(vol->v_cdb, sid);
2121     }
2122     if (did) {
2123         cnid_delete(vol->v_cdb, did);
2124     }
2125     if ((did && ( (crossdev && stat( upath, &srcst) < 0) || 
2126                 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2127        ||
2128        (sid && ( (crossdev && stat(p, &destst) < 0) ||
2129                 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2130     ) {
2131         switch (errno) {
2132         case EPERM:
2133         case EACCES:
2134             err = AFPERR_ACCESS;
2135             break;
2136         default:
2137             err = AFPERR_PARAM;
2138         }
2139         goto err_temp_to_dest;
2140     }
2141     
2142     /* here we need to reopen if crossdev */
2143     if (sid && ad_setid(addp, destst.st_dev, destst.st_ino,  sid, sdir->d_did, vol->v_stamp)) 
2144     {
2145        ad_flush( addp );
2146     }
2147         
2148     if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino,  did, curdir->d_did, vol->v_stamp)) 
2149     {
2150        ad_flush( adsp );
2151     }
2152
2153     /* change perms, src gets dest perm and vice versa */
2154
2155     uid = geteuid();
2156     gid = getegid();
2157     if (seteuid(0)) {
2158         LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2159         err = AFP_OK; /* ignore error */
2160         goto err_temp_to_dest;
2161     }
2162
2163     /*
2164      * we need to exchange ACL entries as well
2165      */
2166     /* exchange_acls(vol, p, upath); */
2167
2168     path->st = srcst;
2169     path->st_valid = 1;
2170     path->st_errno = 0;
2171     path->m_name = NULL;
2172     path->u_name = upath;
2173
2174     setfilunixmode(vol, path, destst.st_mode);
2175     setfilowner(vol, destst.st_uid, destst.st_gid, path);
2176
2177     path->st = destst;
2178     path->st_valid = 1;
2179     path->st_errno = 0;
2180     path->u_name = p;
2181
2182     setfilunixmode(vol, path, srcst.st_mode);
2183     setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2184
2185     if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2186         LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2187         exit(EXITERR_SYS);
2188     }
2189
2190     err = AFP_OK;
2191     goto err_exchangefile;
2192
2193     /* all this stuff is so that we can unwind a failed operation
2194      * properly. */
2195 err_temp_to_dest:
2196     /* rename dest to temp */
2197     renamefile(vol, upath, temp, temp, adsp);
2198     of_rename(vol, s_of, curdir, upath, curdir, temp);
2199
2200 err_dest_to_src:
2201     /* rename source back to dest */
2202     renamefile(vol, p, upath, path->m_name, addp);
2203     of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2204
2205 err_src_to_tmp:
2206     /* rename temp back to source */
2207     renamefile(vol, temp, p, spath, adsp);
2208     of_rename(vol, s_of, curdir, temp, sdir, spath);
2209
2210 err_exchangefile:
2211     if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2212        ad_close(adsp, ADFLAGS_HF);
2213     }
2214     if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2215        ad_close(addp, ADFLAGS_HF);
2216     }
2217
2218     return err;
2219 }