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