]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/file.c
Fix copyfile
[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          /* on noadouble volumes, just creating the data fork is ok */
721          if (vol_noadouble(vol)) {
722              ad_close( &ad, ADFLAGS_DF );
723              goto createfile_done;
724          }
725          /* FIXME with hard create on an existing file, we already
726           * corrupted the data file.
727           */
728          netatalk_unlink( upath );
729          ad_close( &ad, ADFLAGS_DF );
730          return AFPERR_ACCESS;
731     }
732
733     path = s_path->m_name;
734     ad_setname(&ad, path);
735
736     struct stat st;
737     if (lstat(upath, &st) != 0) {
738         LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
739             upath, strerror(errno));
740         ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
741         return AFPERR_MISC;
742     }
743
744     (void)get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath));
745
746     ad_flush(&ad);
747     ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
748     fce_register_new_file(s_path);
749
750 createfile_done:
751     curdir->d_offcnt++;
752
753     setvoltime(obj, vol );
754
755     return (retvalue);
756 }
757
758 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
759 {
760     struct vol  *vol;
761     struct dir  *dir;
762     struct path *s_path;
763     int         did, rc;
764     uint16_t    vid, bitmap;
765
766     *rbuflen = 0;
767     ibuf += 2;
768
769     memcpy(&vid, ibuf, sizeof( vid ));
770     ibuf += sizeof( vid );
771     if (NULL == ( vol = getvolbyvid( vid )) ) {
772         return( AFPERR_PARAM );
773     }
774
775     if (vol->v_flags & AFPVOL_RO)
776         return AFPERR_VLOCK;
777
778     memcpy(&did, ibuf, sizeof( did ));
779     ibuf += sizeof( did );
780     if (NULL == ( dir = dirlookup( vol, did )) ) {
781         return afp_errno; /* was AFPERR_NOOBJ */
782     }
783
784     memcpy(&bitmap, ibuf, sizeof( bitmap ));
785     bitmap = ntohs( bitmap );
786     ibuf += sizeof( bitmap );
787
788     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
789         return get_afp_errno(AFPERR_PARAM);
790     }
791
792     if (path_isadir(s_path)) {
793         return( AFPERR_BADTYPE ); /* it's a directory */
794     }
795
796     if ( s_path->st_errno != 0 ) {
797         return( AFPERR_NOOBJ );
798     }
799
800     if ((u_long)ibuf & 1 ) {
801         ibuf++;
802     }
803
804     if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
805         setvoltime(obj, vol );
806     }
807
808     return( rc );
809 }
810
811 /*
812  * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic  
813  * 
814 */
815 extern struct path Cur_Path;
816
817 int setfilparams(struct vol *vol,
818                  struct path *path, uint16_t f_bitmap, char *buf )
819 {
820     struct adouble      ad, *adp;
821     struct extmap       *em;
822     int                 bit, isad = 1, err = AFP_OK;
823     char                *upath;
824     u_char              achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
825     uint16_t            ashort, bshort, oshort;
826     uint32_t            aint;
827     uint32_t            upriv;
828     uint16_t           upriv_bit = 0;
829     
830     struct utimbuf      ut;
831
832     int                 change_mdate = 0;
833     int                 change_parent_mdate = 0;
834     int                 newdate = 0;
835     struct timeval      tv;
836     uid_t               f_uid;
837     gid_t               f_gid;
838     uint16_t           bitmap = f_bitmap;
839     uint32_t           cdate,bdate;
840     u_char              finder_buf[32];
841
842 #ifdef DEBUG
843     LOG(log_debug9, logtype_afpd, "begin setfilparams:");
844 #endif /* DEBUG */
845
846     adp = of_ad(vol, path, &ad);
847     upath = path->u_name;
848
849     if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
850         return AFPERR_ACCESS;
851     }
852
853     /* with unix priv maybe we have to change adouble file priv first */
854     bit = 0;
855     while ( bitmap != 0 ) {
856         while (( bitmap & 1 ) == 0 ) {
857             bitmap = bitmap>>1;
858             bit++;
859         }
860         switch(  bit ) {
861         case FILPBIT_ATTR :
862             change_mdate = 1;
863             memcpy(&ashort, buf, sizeof( ashort ));
864             buf += sizeof( ashort );
865             break;
866         case FILPBIT_CDATE :
867             change_mdate = 1;
868             memcpy(&cdate, buf, sizeof(cdate));
869             buf += sizeof( cdate );
870             break;
871         case FILPBIT_MDATE :
872             memcpy(&newdate, buf, sizeof( newdate ));
873             buf += sizeof( newdate );
874             break;
875         case FILPBIT_BDATE :
876             change_mdate = 1;
877             memcpy(&bdate, buf, sizeof( bdate));
878             buf += sizeof( bdate );
879             break;
880         case FILPBIT_FINFO :
881             change_mdate = 1;
882             memcpy(finder_buf, buf, 32 );
883             if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
884             // SLFINFO
885                 int fp;
886                 ssize_t len;
887                 int erc=1;
888                 char buf[PATH_MAX+1];
889                 if ((fp=open(path->u_name,O_RDONLY))>=0){
890                     if ((len=read(fp,buf,PATH_MAX+1))){
891                         if (unlink(path->u_name)==0){
892                             buf[len]=0;
893                             erc = symlink(buf, path->u_name);
894                             if (!erc)
895                                 of_stat(path);
896                         }
897                     }
898                     close(fp);
899                 }
900                 if (erc!=0){
901                     err=AFPERR_BITMAP;
902                     goto setfilparam_done;
903                 }
904             }
905             buf += 32;
906             break;
907         case FILPBIT_UNIXPR :
908             if (!vol_unix_priv(vol)) {
909                 /* this volume doesn't use unix priv */
910                 err = AFPERR_BITMAP;
911                 bitmap = 0;
912                 break;
913             }
914             change_mdate = 1;
915             change_parent_mdate = 1;
916
917             memcpy( &aint, buf, sizeof( aint ));
918             f_uid = ntohl (aint);
919             buf += sizeof( aint );
920             memcpy( &aint, buf, sizeof( aint ));
921             f_gid = ntohl (aint);
922             buf += sizeof( aint );
923             setfilowner(vol, f_uid, f_gid, path);
924
925             memcpy( &upriv, buf, sizeof( upriv ));
926             buf += sizeof( upriv );
927             upriv = ntohl (upriv);
928             if ((upriv & S_IWUSR)) {
929                 setfilunixmode(vol, path, upriv);
930             }
931             else {
932                 /* do it later */
933                 upriv_bit = 1;
934             }
935             break;
936         case FILPBIT_PDINFO :
937             if (afp_version < 30) { /* else it's UTF8 name */
938                 achar = *buf;
939                 buf += 2;
940                 /* Keep special case to support crlf translations */
941                 if ((unsigned int) achar == 0x04) {
942                     fdType = (u_char *)"TEXT";
943                     buf += 2;
944                 } else {
945                     xyy[0] = ( u_char ) 'p';
946                     xyy[1] = achar;
947                     xyy[3] = *buf++;
948                     xyy[2] = *buf++;
949                     fdType = xyy;
950                 }
951                 break;
952             }
953             /* fallthrough */
954         default :
955             err = AFPERR_BITMAP;
956             /* break while loop */
957             bitmap = 0;
958             break;
959         }
960
961         bitmap = bitmap>>1;
962         bit++;
963     }
964
965     /* second try with adouble open 
966     */
967     if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
968         LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
969         /*
970          * For some things, we don't need an adouble header:
971          * - change of modification date
972          * - UNIX privs (Bug-ID #2863424)
973          */
974         if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
975             LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
976             return AFPERR_ACCESS;
977         }
978         LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
979         isad = 0;
980     } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
981         ad_setname(adp, path->m_name);
982     }
983     
984     bit = 0;
985     bitmap = f_bitmap;
986     while ( bitmap != 0 ) {
987         while (( bitmap & 1 ) == 0 ) {
988             bitmap = bitmap>>1;
989             bit++;
990         }
991
992         switch(  bit ) {
993         case FILPBIT_ATTR :
994             ad_getattr(adp, &bshort);
995             oshort = bshort;
996             if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
997                 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
998             } else {
999                 bshort &= ~ashort;
1000             }
1001             if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1002                 change_parent_mdate = 1;
1003             ad_setattr(adp, bshort);
1004             break;
1005         case FILPBIT_CDATE :
1006             ad_setdate(adp, AD_DATE_CREATE, cdate);
1007             break;
1008         case FILPBIT_MDATE :
1009             break;
1010         case FILPBIT_BDATE :
1011             ad_setdate(adp, AD_DATE_BACKUP, bdate);
1012             break;
1013         case FILPBIT_FINFO :
1014             if (default_type( ad_entry( adp, ADEID_FINDERI ))
1015                     && ( 
1016                      ((em = getextmap( path->m_name )) &&
1017                       !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1018                       !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1019                      || ((em = getdefextmap()) &&
1020                       !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1021                       !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1022             )) {
1023                 memcpy(finder_buf, ufinderi, 8 );
1024             }
1025             memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1026             break;
1027         case FILPBIT_UNIXPR :
1028             if (upriv_bit) {
1029                 setfilunixmode(vol, path, upriv);
1030             }
1031             break;
1032         case FILPBIT_PDINFO :
1033             if (afp_version < 30) { /* else it's UTF8 name */
1034                 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1035                 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1036                 break;
1037             }
1038             /* fallthrough */
1039         default :
1040             err = AFPERR_BITMAP;
1041             goto setfilparam_done;
1042         }
1043         bitmap = bitmap>>1;
1044         bit++;
1045     }
1046
1047 setfilparam_done:
1048     if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1049        newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1050     }
1051     if (newdate) {
1052        if (isad)
1053           ad_setdate(adp, AD_DATE_MODIFY, newdate);
1054        ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1055        utime(upath, &ut);
1056     }
1057
1058     if (isad) {
1059         ad_flush(adp);
1060         ad_close(adp, ADFLAGS_HF);
1061     }
1062
1063     if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1064         newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1065         bitmap = 1<<FILPBIT_MDATE;
1066         setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1067     }
1068
1069 #ifdef DEBUG
1070     LOG(log_debug9, logtype_afpd, "end setfilparams:");
1071 #endif /* DEBUG */
1072     return err;
1073 }
1074
1075 /*
1076  * renamefile and copyfile take the old and new unix pathnames
1077  * and the new mac name.
1078  *
1079  * sdir_fd     source dir fd to which src path is relative (for openat et al semantics)
1080  *             passing -1 means this is not used, src path is a full path
1081  * src         the source path 
1082  * dst         the dest filename in current dir
1083  * newname     the dest mac name
1084  * adp         adouble struct of src file, if open, or & zeroed one
1085  *
1086  */
1087 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1088 {
1089     int         rc;
1090
1091     LOG(log_debug, logtype_afpd,
1092         "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1093
1094     if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1095         switch ( errno ) {
1096         case ENOENT :
1097             return( AFPERR_NOOBJ );
1098         case EPERM:
1099         case EACCES :
1100             return( AFPERR_ACCESS );
1101         case EROFS:
1102             return AFPERR_VLOCK;
1103         case EXDEV :                    /* Cross device move -- try copy */
1104            /* NOTE: with open file it's an error because after the copy we will 
1105             * get two files, it's fixable for our process (eg reopen the new file, get the
1106             * locks, and so on. But it doesn't solve the case with a second process
1107             */
1108             if (adp->ad_open_forks) {
1109                 /* FIXME  warning in syslog so admin'd know there's a conflict ?*/
1110                 return AFPERR_OLOCK; /* little lie */
1111             }
1112             if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1113                 /* on error copyfile delete dest */
1114                 return( rc );
1115             }
1116             return deletefile(vol, sdir_fd, src, 0);
1117         default :
1118             return( AFPERR_PARAM );
1119         }
1120     }
1121
1122     if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1123         int err;
1124         
1125         err = errno;        
1126         /* try to undo the data fork rename,
1127          * we know we are on the same device 
1128         */
1129         if (err) {
1130         unix_rename(-1, dst, sdir_fd, src ); 
1131             /* return the first error */
1132             switch ( err) {
1133             case ENOENT :
1134                 return AFPERR_NOOBJ;
1135             case EPERM:
1136             case EACCES :
1137                 return AFPERR_ACCESS ;
1138             case EROFS:
1139                 return AFPERR_VLOCK;
1140             default :
1141                 return AFPERR_PARAM ;
1142             }
1143         }
1144     }
1145
1146     /* don't care if we can't open the newly renamed ressource fork */
1147     if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1148         ad_setname(adp, newname);
1149         ad_flush( adp );
1150         ad_close( adp, ADFLAGS_HF );
1151     }
1152
1153     return( AFP_OK );
1154 }
1155
1156 /* ---------------- 
1157    convert a Mac long name to an utf8 name,
1158 */
1159 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1160 {
1161 size_t    outlen;
1162
1163     if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1164         return -1;
1165     }
1166     return outlen;
1167 }
1168
1169 /* ---------------- */
1170 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1171 {
1172 char        type = *ibuf;
1173 size_t      plen = 0;
1174 uint16_t   len16;
1175 uint32_t   hint;
1176
1177     if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1178         return -1;
1179     }
1180     ibuf++;
1181     switch (type) {
1182     case 2:
1183         if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1184             if (afp_version >= 30) {
1185                 /* convert it to UTF8 
1186                 */
1187                 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1188                    return -1;
1189             }
1190             else {
1191                 strncpy( newname, ibuf, plen );
1192                 newname[ plen ] = '\0';
1193             }
1194             if (strlen(newname) != plen) {
1195                 /* there's \0 in newname, e.g. it's a pathname not
1196                  * only a filename. 
1197                 */
1198                 return -1;
1199             }
1200         }
1201         break;
1202     case 3:
1203         memcpy(&hint, ibuf, sizeof(hint));
1204         ibuf += sizeof(hint);
1205            
1206         memcpy(&len16, ibuf, sizeof(len16));
1207         ibuf += sizeof(len16);
1208         plen = ntohs(len16);
1209         
1210         if (plen) {
1211             if (plen > AFPOBJ_TMPSIZ) {
1212                 return -1;
1213             }
1214             strncpy( newname, ibuf, plen );
1215             newname[ plen ] = '\0';
1216             if (strlen(newname) != plen) {
1217                 return -1;
1218             }
1219         }
1220         break;
1221     }
1222     return plen;
1223 }
1224
1225 /* -----------------------------------
1226 */
1227 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1228 {
1229     struct vol  *s_vol, *d_vol;
1230     struct dir  *dir;
1231     char        *newname, *p, *upath;
1232     struct path *s_path;
1233     uint32_t    sdid, ddid;
1234     int         err, retvalue = AFP_OK;
1235     uint16_t    svid, dvid;
1236
1237     struct adouble ad, *adp;
1238     int denyreadset;
1239     
1240     *rbuflen = 0;
1241     ibuf += 2;
1242
1243     memcpy(&svid, ibuf, sizeof( svid ));
1244     ibuf += sizeof( svid );
1245     if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1246         return( AFPERR_PARAM );
1247     }
1248
1249     memcpy(&sdid, ibuf, sizeof( sdid ));
1250     ibuf += sizeof( sdid );
1251     if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1252         return afp_errno;
1253     }
1254
1255     memcpy(&dvid, ibuf, sizeof( dvid ));
1256     ibuf += sizeof( dvid );
1257     memcpy(&ddid, ibuf, sizeof( ddid ));
1258     ibuf += sizeof( ddid );
1259
1260     if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1261         return get_afp_errno(AFPERR_PARAM);
1262     }
1263     if ( path_isadir(s_path) ) {
1264         return( AFPERR_BADTYPE );
1265     }
1266
1267     /* don't allow copies when the file is open.
1268      * XXX: the spec only calls for read/deny write access.
1269      *      however, copyfile doesn't have any of that info,
1270      *      and locks need to stay coherent. as a result,
1271      *      we just balk if the file is opened already. */
1272
1273     adp = of_ad(s_vol, s_path, &ad);
1274
1275     if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY) < 0) {
1276         return AFPERR_DENYCONF;
1277     }
1278     denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 || 
1279                   ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1280
1281     if (denyreadset) {
1282         retvalue = AFPERR_DENYCONF;
1283         goto copy_exit;
1284     }
1285
1286     newname = obj->newtmp;
1287     strcpy( newname, s_path->m_name );
1288
1289     p = ctoupath( s_vol, curdir, newname );
1290     if (!p) {
1291         retvalue = AFPERR_PARAM;
1292         goto copy_exit;
1293     }
1294
1295     if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1296         retvalue = AFPERR_PARAM;
1297         goto copy_exit;
1298     }
1299
1300     if (d_vol->v_flags & AFPVOL_RO) {
1301         retvalue = AFPERR_VLOCK;
1302         goto copy_exit;
1303     }
1304
1305     if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1306         retvalue = afp_errno;
1307         goto copy_exit;
1308     }
1309
1310     if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1311         retvalue = get_afp_errno(AFPERR_NOOBJ);
1312         goto copy_exit;
1313     }
1314     
1315     if ( *s_path->m_name != '\0' ) {
1316         retvalue =path_error(s_path, AFPERR_NOOBJ);
1317         goto copy_exit;
1318     }
1319
1320     /* one of the handful of places that knows about the path type */
1321     if (copy_path_name(d_vol, newname, ibuf) < 0) {
1322         retvalue = AFPERR_PARAM;
1323         goto copy_exit;
1324     }
1325     /* newname is always only a filename so curdir *is* its
1326      * parent folder
1327     */
1328     if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1329         retvalue =AFPERR_PARAM;
1330         goto copy_exit;
1331     }
1332
1333     if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1334         retvalue = err;
1335         goto copy_exit;
1336     }
1337     curdir->d_offcnt++;
1338
1339     setvoltime(obj, d_vol );
1340
1341 copy_exit:
1342     ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1343     return( retvalue );
1344 }
1345
1346 /* ----------------------- */
1347 static int copy_all(const int dfd, const void *buf,
1348                                size_t buflen)
1349 {
1350     ssize_t cc;
1351
1352 #ifdef DEBUG
1353     LOG(log_debug9, logtype_afpd, "begin copy_all:");
1354 #endif /* DEBUG */
1355
1356     while (buflen > 0) {
1357         if ((cc = write(dfd, buf, buflen)) < 0) {
1358             switch (errno) {
1359             case EINTR:
1360                 continue;
1361             default:
1362                 return -1;
1363             }
1364         }
1365         buflen -= cc;
1366     }
1367
1368 #ifdef DEBUG
1369     LOG(log_debug9, logtype_afpd, "end copy_all:");
1370 #endif /* DEBUG */
1371
1372     return 0;
1373 }
1374
1375 /* -------------------------- 
1376  * copy only the fork data stream
1377 */
1378 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1379 {
1380     ssize_t cc;
1381     int     err = 0;
1382     char    filebuf[8192];
1383     int     sfd, dfd;
1384     off_t   soff, doff;
1385
1386     if (eid == ADEID_DFORK) {
1387         sfd = ad_data_fileno(ads);
1388         dfd = ad_data_fileno(add);
1389     }
1390     else {
1391         sfd = ad_reso_fileno(ads);
1392         dfd = ad_reso_fileno(add);
1393     }        
1394
1395     if (add->ad_version == AD_VERSION2)
1396         soff = doff = ad_getentryoff(ads, eid);
1397     else {
1398         if (eid == ADEID_DFORK)
1399             soff = doff = ad_getentryoff(ads, eid);
1400         else {
1401 #ifdef HAVE_EAFD
1402             soff = doff = 0;
1403 #else
1404             soff = doff = ADEDOFF_RFORK_OSX;
1405 #endif
1406         }
1407     }
1408
1409     if ((off_t)-1 == lseek(sfd, soff, SEEK_SET))
1410         return -1;
1411
1412     if ((off_t)-1 == lseek(dfd, doff, SEEK_SET))
1413         return -1;
1414         
1415 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1416     /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1417     off_t   offset = 0;
1418     size_t  size;
1419     struct stat         st;
1420     #define BUF 128*1024*1024
1421
1422     if (fstat(sfd, &st) == 0) {
1423         
1424         while (1) {
1425             if ( offset >= st.st_size) {
1426                return 0;
1427             }
1428             size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1429             if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1430                 switch (errno) {
1431                 case ENOSYS:
1432                 case EINVAL:  /* there's no guarantee that all fs support sendfile */
1433                     goto no_sendfile;
1434                 default:
1435                     return -1;
1436                 }
1437             }
1438         }
1439     }
1440     no_sendfile:
1441     lseek(sfd, offset, SEEK_SET);
1442 #endif 
1443
1444     while (1) {
1445         if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1446             if (errno == EINTR)
1447                 continue;
1448             err = -1;
1449             break;
1450         }
1451
1452         if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1453             break;
1454         }
1455     }
1456     return err;
1457 }
1458
1459 /* ----------------------------------
1460  * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1461  * because we are doing it elsewhere.
1462  * currently if newname is NULL then adp is NULL. 
1463  */
1464 int copyfile(const struct vol *s_vol,
1465              const struct vol *d_vol, 
1466              int sfd,
1467              char *src,
1468              char *dst,
1469              char *newname,
1470              struct adouble *adp)
1471 {
1472     struct adouble      ads, add;
1473     int                 err = 0;
1474     int                 ret_err = 0;
1475     int                 adflags;
1476     int                 stat_result;
1477     struct stat         st;
1478     
1479     LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1480         sfd, src, dst, newname);
1481
1482     if (adp == NULL) {
1483         ad_init(&ads, s_vol);
1484         adp = &ads;
1485     }
1486
1487     adflags = ADFLAGS_DF | ADFLAGS_RF | ADFLAGS_NORF;
1488     if (newname) {
1489         adflags |= ADFLAGS_HF;
1490     }
1491
1492     if (ad_openat(adp, sfd, src, adflags | ADFLAGS_NOHF | ADFLAGS_RDONLY) < 0) {
1493         ret_err = errno;
1494         goto done;
1495     }
1496
1497     if (!AD_RSRC_OPEN(adp))
1498         /* no resource fork, don't create one for dst file */
1499         adflags &= ~ADFLAGS_RF;
1500
1501     stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1502
1503     if (stat_result < 0) {           
1504       /* unlikely but if fstat fails, the default file mode will be 0666. */
1505       st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1506     }
1507
1508     ad_init(&add, d_vol);
1509     if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode) < 0) {
1510         ret_err = errno;
1511         ad_close( adp, adflags );
1512         if (EEXIST != ret_err) {
1513             deletefile(d_vol, -1, dst, 0);
1514             goto done;
1515         }
1516         return AFPERR_EXIST;
1517     }
1518
1519     if (AD_RSRC_OPEN(adp))
1520         err = copy_fork(ADEID_RFORK, &add, adp);
1521     
1522     if (err == 0)
1523         err = copy_fork(ADEID_DFORK, &add, adp);
1524
1525     if (err < 0)
1526        ret_err = errno;
1527
1528     if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1529         /* set the new name in the resource fork */
1530         ad_copy_header(&add, adp);
1531         ad_setname(&add, newname);
1532         ad_flush( &add );
1533     }
1534     ad_close( adp, adflags );
1535
1536     if (ad_close( &add, adflags ) <0) {
1537        ret_err = errno;
1538     } 
1539
1540     if (ret_err) {
1541         deletefile(d_vol, -1, dst, 0);
1542     }
1543     else if (stat_result == 0) {
1544         /* set dest modification date to src date */
1545         struct utimbuf  ut;
1546
1547         ut.actime = ut.modtime = st.st_mtime;
1548         utime(dst, &ut);
1549         /* FIXME netatalk doesn't use resource fork file date
1550          * but maybe we should set its modtime too.
1551         */
1552     }
1553
1554 done:
1555     switch ( ret_err ) {
1556     case 0:
1557         return AFP_OK;
1558     case EDQUOT:
1559     case EFBIG:
1560     case ENOSPC:
1561         return AFPERR_DFULL;
1562     case ENOENT:
1563         return AFPERR_NOOBJ;
1564     case EACCES:
1565         return AFPERR_ACCESS;
1566     case EROFS:
1567         return AFPERR_VLOCK;
1568     }
1569     return AFPERR_PARAM;
1570 }
1571
1572
1573 /* -----------------------------------
1574    vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1575    checkAttrib:   1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1576
1577    when deletefile is called we don't have lock on it, file is closed (for us)
1578    untrue if called by renamefile
1579    
1580    ad_open always try to open file RDWR first and ad_lock takes care of
1581    WRITE lock on read only file.
1582 */
1583
1584 static int check_attrib(struct adouble *adp)
1585 {
1586 uint16_t   bshort = 0;
1587
1588         ad_getattr(adp, &bshort);
1589     /*
1590      * Does kFPDeleteInhibitBit (bit 8) set?
1591      */
1592         if ((bshort & htons(ATTRBIT_NODELETE))) {
1593                 return AFPERR_OLOCK;
1594         }
1595     if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1596         return AFPERR_BUSY;
1597         }
1598         return 0;
1599 }
1600 /* 
1601  * dirfd can be used for unlinkat semantics
1602  */
1603 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1604 {
1605     struct adouble      ad;
1606     struct adouble      *adp = NULL;
1607     int                 adflags, err = AFP_OK;
1608     int                 meta = 0;
1609
1610     LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1611
1612     ad_init(&ad, vol);
1613     if (checkAttrib) {
1614         /* was EACCESS error try to get only metadata */
1615         /* we never want to create a resource fork here, we are going to delete it 
1616          * moreover sometimes deletefile is called with a no existent file and 
1617          * ad_open would create a 0 byte resource fork
1618         */
1619         if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1620             if ((err = check_attrib(&ad))) {
1621                 ad_close(&ad, ADFLAGS_HF);
1622                return err;
1623             }
1624             meta = 1;
1625         }
1626     }
1627  
1628     /* try to open both forks at once */
1629     adflags = ADFLAGS_DF;
1630     if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1631         switch (errno) {
1632         case ENOENT:
1633             err = AFPERR_NOOBJ;
1634             goto end;
1635         case EACCES: /* maybe it's a file with no write mode for us */
1636             break;   /* was return AFPERR_ACCESS;*/
1637         case EROFS:
1638             err = AFPERR_VLOCK;
1639             goto end;
1640         default:
1641             err = AFPERR_PARAM;
1642             goto end;
1643         }
1644     }
1645     else {
1646         adp = &ad;
1647     }
1648
1649     if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1650         adflags |= ADFLAGS_RF;
1651         /* FIXME we have a pb here because we want to know if a file is open 
1652          * there's a 'priority inversion' if you can't open the ressource fork RW
1653          * you can delete it if it's open because you can't get a write lock.
1654          * 
1655          * ADLOCK_FILELOCK means the whole ressource fork, not only after the 
1656          * metadatas
1657          *
1658          * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1659          */
1660         if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1661             err = AFPERR_BUSY;
1662             goto end;
1663         }
1664     }
1665
1666     if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1667         LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1668         err = AFPERR_BUSY;
1669     } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1670         cnid_t id;
1671         if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1672             cnid_delete(vol->v_cdb, id);
1673         }
1674     }
1675
1676 end:
1677     if (meta)
1678         ad_close(&ad, ADFLAGS_HF);
1679
1680     if (adp)
1681         ad_close( &ad, adflags );  /* ad_close removes locks if any */
1682
1683     return err;
1684 }
1685
1686 /* ------------------------------------ */
1687 /* return a file id */
1688 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1689 {
1690     struct stat         *st;
1691     struct vol          *vol;
1692     struct dir          *dir;
1693     char                *upath;
1694     int                 len;
1695     cnid_t              did, id;
1696     u_short             vid;
1697     struct path         *s_path;
1698
1699     *rbuflen = 0;
1700
1701     ibuf += 2;
1702
1703     memcpy(&vid, ibuf, sizeof(vid));
1704     ibuf += sizeof(vid);
1705
1706     if (NULL == ( vol = getvolbyvid( vid )) ) {
1707         return( AFPERR_PARAM);
1708     }
1709
1710     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1711         return AFPERR_NOOP;
1712     }
1713
1714     if (vol->v_flags & AFPVOL_RO)
1715         return AFPERR_VLOCK;
1716
1717     memcpy(&did, ibuf, sizeof( did ));
1718     ibuf += sizeof(did);
1719
1720     if (NULL == ( dir = dirlookup( vol, did )) ) {
1721         return afp_errno; /* was AFPERR_PARAM */
1722     }
1723
1724     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1725         return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1726     }
1727
1728     if ( path_isadir(s_path) ) {
1729         return( AFPERR_BADTYPE );
1730     }
1731
1732     upath = s_path->u_name;
1733     switch (s_path->st_errno) {
1734         case 0:
1735              break; /* success */
1736         case EPERM:
1737         case EACCES:
1738             return AFPERR_ACCESS;
1739         case ENOENT:
1740             return AFPERR_NOOBJ;
1741         default:
1742             return AFPERR_PARAM;
1743     }
1744     st = &s_path->st;
1745     if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1746         memcpy(rbuf, &id, sizeof(id));
1747         *rbuflen = sizeof(id);
1748         return AFPERR_EXISTID;
1749     }
1750
1751     if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1752         memcpy(rbuf, &id, sizeof(id));
1753         *rbuflen = sizeof(id);
1754         return AFP_OK;
1755     }
1756
1757     return afp_errno;
1758 }
1759
1760 /* ------------------------------- */
1761 struct reenum {
1762     struct vol *vol;
1763     cnid_t     did;
1764 };
1765
1766 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1767 {
1768     struct path   path;
1769     struct reenum *param = data;
1770     struct vol    *vol = param->vol;  
1771     cnid_t        did  = param->did;
1772     cnid_t        aint;
1773     
1774     if ( lstat(de->d_name, &path.st) < 0 )
1775         return 0;
1776     
1777     /* update or add to cnid */
1778     aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1779
1780     return 0;
1781 }
1782
1783 /* --------------------
1784  * Ok the db is out of synch with the dir.
1785  * but if it's a deleted file we don't want to do it again and again.
1786 */
1787 static int
1788 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1789 {
1790     int             ret;
1791     struct reenum   data;
1792     struct stat     st;
1793     
1794     if (vol->v_cdb == NULL) {
1795         return -1;
1796     }
1797     
1798     /* FIXME use of_statdir ? */
1799     if (lstat(name, &st)) {
1800         return -1;
1801     }
1802
1803     if (dirreenumerate(dir, &st)) {
1804         /* we already did it once and the dir haven't been modified */
1805         return dir->d_offcnt;
1806     }
1807     
1808     data.vol = vol;
1809     data.did = dir->d_did;
1810     if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1811         setdiroffcnt(curdir, &st,  ret);
1812         dir->d_flags |= DIRF_CNID;
1813     }
1814
1815     return ret;
1816 }
1817
1818 /* ------------------------------
1819    resolve a file id */
1820 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1821 {
1822     struct vol          *vol;
1823     struct dir          *dir;
1824     char                *upath;
1825     struct path         path;
1826     int                 err, retry=0;
1827     size_t              buflen;
1828     cnid_t              id, cnid;
1829     uint16_t            vid, bitmap;
1830
1831     static char buffer[12 + MAXPATHLEN + 1];
1832     int len = 12 + MAXPATHLEN + 1;
1833
1834     *rbuflen = 0;
1835     ibuf += 2;
1836
1837     memcpy(&vid, ibuf, sizeof(vid));
1838     ibuf += sizeof(vid);
1839
1840     if (NULL == ( vol = getvolbyvid( vid )) ) {
1841         return( AFPERR_PARAM);
1842     }
1843
1844     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1845         return AFPERR_NOOP;
1846     }
1847
1848     memcpy(&id, ibuf, sizeof( id ));
1849     ibuf += sizeof(id);
1850     cnid = id;
1851     
1852     if (!id) {
1853         /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1854         return AFPERR_NOID;
1855     }
1856 retry:
1857     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1858         return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1859     }
1860
1861     if (NULL == ( dir = dirlookup( vol, id )) ) {
1862         return AFPERR_NOID; /* idem AFPERR_PARAM */
1863     }
1864     if (movecwd(vol, dir) < 0) {
1865         switch (errno) {
1866         case EACCES:
1867         case EPERM:
1868             return AFPERR_ACCESS;
1869         case ENOENT:
1870             return AFPERR_NOID;
1871         default:
1872             return AFPERR_PARAM;
1873         }
1874     }
1875
1876     memset(&path, 0, sizeof(path));
1877     path.u_name = upath;
1878     if ( of_stat(&path) < 0 ) {
1879 #ifdef ESTALE
1880         /* with nfs and our working directory is deleted */
1881         if (errno == ESTALE) {
1882             errno = ENOENT;
1883         }
1884 #endif  
1885         if ( errno == ENOENT && !retry) {
1886             /* cnid db is out of sync, reenumerate the directory and update ids */
1887             reenumerate_id(vol, ".", dir);
1888             id = cnid;
1889             retry = 1;
1890             goto retry;
1891         }
1892         switch (errno) {
1893         case EACCES:
1894         case EPERM:
1895             return AFPERR_ACCESS;
1896         case ENOENT:
1897             return AFPERR_NOID;
1898         default:
1899             return AFPERR_PARAM;
1900         }
1901     }
1902
1903     /* directories are bad */
1904     if (S_ISDIR(path.st.st_mode)) {
1905         /* OS9 and OSX don't return the same error code  */
1906         return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1907     }
1908
1909     memcpy(&bitmap, ibuf, sizeof(bitmap));
1910     bitmap = ntohs( bitmap );
1911     if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1912         return AFPERR_NOID;
1913     }
1914     path.id = cnid;
1915     if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir, 
1916                             rbuf + sizeof(bitmap), &buflen))) {
1917         return err;
1918     }
1919     *rbuflen = buflen + sizeof(bitmap);
1920     memcpy(rbuf, ibuf, sizeof(bitmap));
1921
1922     return AFP_OK;
1923 }
1924
1925 /* ------------------------------ */
1926 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1927 {
1928     struct stat         st;
1929     struct vol          *vol;
1930     struct dir          *dir;
1931     char                *upath;
1932     int                 err;
1933     cnid_t              id;
1934     cnid_t              fileid;
1935     u_short             vid;
1936     static char buffer[12 + MAXPATHLEN + 1];
1937     int len = 12 + MAXPATHLEN + 1;
1938
1939     *rbuflen = 0;
1940     ibuf += 2;
1941
1942     memcpy(&vid, ibuf, sizeof(vid));
1943     ibuf += sizeof(vid);
1944
1945     if (NULL == ( vol = getvolbyvid( vid )) ) {
1946         return( AFPERR_PARAM);
1947     }
1948
1949     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1950         return AFPERR_NOOP;
1951     }
1952
1953     if (vol->v_flags & AFPVOL_RO)
1954         return AFPERR_VLOCK;
1955
1956     memcpy(&id, ibuf, sizeof( id ));
1957     ibuf += sizeof(id);
1958     fileid = id;
1959
1960     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1961         return AFPERR_NOID;
1962     }
1963
1964     if (NULL == ( dir = dirlookup( vol, id )) ) {
1965         if (afp_errno == AFPERR_NOOBJ) {
1966             err = AFPERR_NOOBJ;
1967             goto delete;
1968         }
1969         return( AFPERR_PARAM );
1970     }
1971
1972     err = AFP_OK;
1973     if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1974         switch (errno) {
1975         case EACCES:
1976         case EPERM:
1977             return AFPERR_ACCESS;
1978 #ifdef ESTALE
1979         case ESTALE:
1980 #endif  
1981         case ENOENT:
1982             /* still try to delete the id */
1983             err = AFPERR_NOOBJ;
1984             break;
1985         default:
1986             return AFPERR_PARAM;
1987         }
1988     }
1989     else if (S_ISDIR(st.st_mode)) /* directories are bad */
1990         return AFPERR_BADTYPE;
1991
1992 delete:
1993     if (cnid_delete(vol->v_cdb, fileid)) {
1994         switch (errno) {
1995         case EROFS:
1996             return AFPERR_VLOCK;
1997         case EPERM:
1998         case EACCES:
1999             return AFPERR_ACCESS;
2000         default:
2001             return AFPERR_PARAM;
2002         }
2003     }
2004
2005     return err;
2006 }
2007
2008 /* ------------------------------ */
2009 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
2010 {
2011     int             ret;
2012
2013     if (path->st_errno) {
2014         switch (path->st_errno) {
2015         case ENOENT:
2016             afp_errno = AFPERR_NOID;
2017             break;
2018         case EPERM:
2019         case EACCES:
2020             afp_errno = AFPERR_ACCESS;
2021             break;
2022         default:
2023             afp_errno = AFPERR_PARAM;
2024             break;
2025         }
2026         return NULL;
2027     }
2028     /* we use file_access both for legacy Mac perm and
2029      * for unix privilege, rename will take care of folder perms
2030     */
2031     if (file_access(path, OPENACC_WR ) < 0) {
2032         afp_errno = AFPERR_ACCESS;
2033         return NULL;
2034     }
2035     
2036     if ((*of = of_findname(path))) {
2037         /* reuse struct adouble so it won't break locks */
2038         adp = (*of)->of_ad;
2039     }
2040     else {
2041         ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
2042         /* META and HF */
2043         if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2044             /* from AFP spec.
2045              * The user must have the Read & Write privilege for both files in order to use this command.
2046              */
2047             ad_close(adp, ADFLAGS_HF);
2048             afp_errno = AFPERR_ACCESS;
2049             return NULL;
2050         }
2051     }
2052     return adp;
2053 }
2054
2055 #define APPLETEMP ".AppleTempXXXXXX"
2056
2057 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2058 {
2059     struct stat         srcst, destst;
2060     struct vol          *vol;
2061     struct dir          *dir, *sdir;
2062     char                *spath, temp[17], *p;
2063     char                *supath, *upath;
2064     struct path         *path;
2065     int                 err;
2066     struct adouble      ads;
2067     struct adouble      add;
2068     struct adouble      *adsp = NULL;
2069     struct adouble      *addp = NULL;
2070     struct ofork        *s_of = NULL;
2071     struct ofork        *d_of = NULL;
2072     int                 crossdev;
2073     
2074     int                 slen, dlen;
2075     uint32_t            sid, did;
2076     uint16_t            vid;
2077
2078     uid_t              uid;
2079     gid_t              gid;
2080
2081     *rbuflen = 0;
2082     ibuf += 2;
2083
2084     memcpy(&vid, ibuf, sizeof(vid));
2085     ibuf += sizeof(vid);
2086
2087     if (NULL == ( vol = getvolbyvid( vid )) ) {
2088         return( AFPERR_PARAM);
2089     }
2090
2091     if ((vol->v_flags & AFPVOL_RO))
2092         return AFPERR_VLOCK;
2093
2094     /* source and destination dids */
2095     memcpy(&sid, ibuf, sizeof(sid));
2096     ibuf += sizeof(sid);
2097     memcpy(&did, ibuf, sizeof(did));
2098     ibuf += sizeof(did);
2099
2100     /* source file */
2101     if (NULL == (dir = dirlookup( vol, sid )) ) {
2102         return afp_errno; /* was AFPERR_PARAM */
2103     }
2104
2105     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2106         return get_afp_errno(AFPERR_NOOBJ); 
2107     }
2108
2109     if ( path_isadir(path) ) {
2110         return AFPERR_BADTYPE;   /* it's a dir */
2111     }
2112
2113     /* save some stuff */
2114     srcst = path->st;
2115     sdir = curdir;
2116     spath = obj->oldtmp;
2117     supath = obj->newtmp;
2118     strcpy(spath, path->m_name);
2119     strcpy(supath, path->u_name); /* this is for the cnid changing */
2120     p = absupath( vol, sdir, supath);
2121     if (!p) {
2122         /* pathname too long */
2123         return AFPERR_PARAM ;
2124     }
2125     
2126     ad_init(&ads, vol);
2127     if (!(adsp = find_adouble( path, &s_of, &ads))) {
2128         return afp_errno;
2129     }
2130
2131     /* ***** from here we may have resource fork open **** */
2132     
2133     /* look for the source cnid. if it doesn't exist, don't worry about
2134      * it. */
2135     sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2136
2137     if (NULL == ( dir = dirlookup( vol, did )) ) {
2138         err = afp_errno; /* was AFPERR_PARAM */
2139         goto err_exchangefile;
2140     }
2141
2142     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2143         err = get_afp_errno(AFPERR_NOOBJ); 
2144         goto err_exchangefile;
2145     }
2146
2147     if ( path_isadir(path) ) {
2148         err = AFPERR_BADTYPE;
2149         goto err_exchangefile;
2150     }
2151
2152     /* FPExchangeFiles is the only call that can return the SameObj
2153      * error */
2154     if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2155         err = AFPERR_SAMEOBJ;
2156         goto err_exchangefile;
2157     }
2158
2159     ad_init(&add, vol);
2160     if (!(addp = find_adouble( path, &d_of, &add))) {
2161         err = afp_errno;
2162         goto err_exchangefile;
2163     }
2164     destst = path->st;
2165
2166     /* they are not on the same device and at least one is open
2167      * FIXME broken for for crossdev and adouble v2
2168      * return an error 
2169     */
2170     crossdev = (srcst.st_dev != destst.st_dev);
2171     if (/* (d_of || s_of)  && */ crossdev) {
2172         err = AFPERR_MISC;
2173         goto err_exchangefile;
2174     }
2175
2176     /* look for destination id. */
2177     upath = path->u_name;
2178     did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2179
2180     /* construct a temp name.
2181      * NOTE: the temp file will be in the dest file's directory. it
2182      * will also be inaccessible from AFP. */
2183     memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2184     if (!mktemp(temp)) {
2185         err = AFPERR_MISC;
2186         goto err_exchangefile;
2187     }
2188     
2189     if (crossdev) {
2190         /* FIXME we need to close fork for copy, both s_of and d_of are null */
2191        ad_close(adsp, ADFLAGS_HF);
2192        ad_close(addp, ADFLAGS_HF);
2193     }
2194
2195     /* now, quickly rename the file. we error if we can't. */
2196     if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2197         goto err_exchangefile;
2198     of_rename(vol, s_of, sdir, spath, curdir, temp);
2199
2200     /* rename destination to source */
2201     if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2202         goto err_src_to_tmp;
2203     of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2204
2205     /* rename temp to destination */
2206     if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2207         goto err_dest_to_src;
2208     of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2209
2210     /* id's need switching. src -> dest and dest -> src. 
2211      * we need to re-stat() if it was a cross device copy.
2212     */
2213     if (sid)
2214         cnid_delete(vol->v_cdb, sid);
2215     if (did)
2216         cnid_delete(vol->v_cdb, did);
2217
2218     if ((did && ( (crossdev && lstat( upath, &srcst) < 0) || 
2219                 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2220        ||
2221        (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2222                 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2223     ) {
2224         switch (errno) {
2225         case EPERM:
2226         case EACCES:
2227             err = AFPERR_ACCESS;
2228             break;
2229         default:
2230             err = AFPERR_PARAM;
2231         }
2232         goto err_temp_to_dest;
2233     }
2234     
2235     /* here we need to reopen if crossdev */
2236     if (sid && ad_setid(addp, destst.st_dev, destst.st_ino,  sid, sdir->d_did, vol->v_stamp)) 
2237     {
2238        ad_flush( addp );
2239     }
2240         
2241     if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino,  did, curdir->d_did, vol->v_stamp)) 
2242     {
2243        ad_flush( adsp );
2244     }
2245
2246     /* change perms, src gets dest perm and vice versa */
2247
2248     uid = geteuid();
2249     gid = getegid();
2250     if (seteuid(0)) {
2251         LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2252         err = AFP_OK; /* ignore error */
2253         goto err_temp_to_dest;
2254     }
2255
2256     /*
2257      * we need to exchange ACL entries as well
2258      */
2259     /* exchange_acls(vol, p, upath); */
2260
2261     path->st = srcst;
2262     path->st_valid = 1;
2263     path->st_errno = 0;
2264     path->m_name = NULL;
2265     path->u_name = upath;
2266
2267     setfilunixmode(vol, path, destst.st_mode);
2268     setfilowner(vol, destst.st_uid, destst.st_gid, path);
2269
2270     path->st = destst;
2271     path->st_valid = 1;
2272     path->st_errno = 0;
2273     path->u_name = p;
2274
2275     setfilunixmode(vol, path, srcst.st_mode);
2276     setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2277
2278     if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2279         LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2280         exit(EXITERR_SYS);
2281     }
2282
2283     err = AFP_OK;
2284     goto err_exchangefile;
2285
2286     /* all this stuff is so that we can unwind a failed operation
2287      * properly. */
2288 err_temp_to_dest:
2289     /* rename dest to temp */
2290     renamefile(vol, -1, upath, temp, temp, adsp);
2291     of_rename(vol, s_of, curdir, upath, curdir, temp);
2292
2293 err_dest_to_src:
2294     /* rename source back to dest */
2295     renamefile(vol, -1, p, upath, path->m_name, addp);
2296     of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2297
2298 err_src_to_tmp:
2299     /* rename temp back to source */
2300     renamefile(vol, -1, temp, p, spath, adsp);
2301     of_rename(vol, s_of, curdir, temp, sdir, spath);
2302
2303 err_exchangefile:
2304     if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2305        ad_close(adsp, ADFLAGS_HF);
2306     }
2307     if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2308        ad_close(addp, ADFLAGS_HF);
2309     }
2310
2311     struct dir *cached;
2312     if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2313         (void)dir_remove(vol, cached);
2314     if ((cached = dircache_search_by_did(vol, did)) != NULL)
2315         (void)dir_remove(vol, cached);
2316
2317     return err;
2318 }