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