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