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