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