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