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