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