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