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