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