]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/file.c
Solaris share reservation cross platform locking
[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 #ifdef HAVE_FSHARE_T
1262     fshare_t shmd;
1263     shmd.f_access = F_RDACC;
1264     shmd.f_deny = F_NODNY;
1265     if (fcntl(ad_data_fileno(adp), F_SHARE, &shmd) != 0) {
1266         retvalue = AFPERR_DENYCONF;
1267         goto copy_exit;
1268     }
1269     if (AD_RSRC_OPEN(adp) && fcntl(ad_reso_fileno(adp), F_SHARE, &shmd) != 0) {
1270         retvalue = AFPERR_DENYCONF;
1271         goto copy_exit;
1272     }
1273 #endif
1274     denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 || 
1275                   ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1276
1277     if (denyreadset) {
1278         retvalue = AFPERR_DENYCONF;
1279         goto copy_exit;
1280     }
1281
1282     newname = obj->newtmp;
1283     strcpy( newname, s_path->m_name );
1284
1285     p = ctoupath( s_vol, curdir, newname );
1286     if (!p) {
1287         retvalue = AFPERR_PARAM;
1288         goto copy_exit;
1289     }
1290
1291     if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1292         retvalue = AFPERR_PARAM;
1293         goto copy_exit;
1294     }
1295
1296     if (d_vol->v_flags & AFPVOL_RO) {
1297         retvalue = AFPERR_VLOCK;
1298         goto copy_exit;
1299     }
1300
1301     if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1302         retvalue = afp_errno;
1303         goto copy_exit;
1304     }
1305
1306     if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1307         retvalue = get_afp_errno(AFPERR_NOOBJ);
1308         goto copy_exit;
1309     }
1310     
1311     if ( *s_path->m_name != '\0' ) {
1312         retvalue =path_error(s_path, AFPERR_NOOBJ);
1313         goto copy_exit;
1314     }
1315
1316     /* one of the handful of places that knows about the path type */
1317     if (copy_path_name(d_vol, newname, ibuf) < 0) {
1318         retvalue = AFPERR_PARAM;
1319         goto copy_exit;
1320     }
1321     /* newname is always only a filename so curdir *is* its
1322      * parent folder
1323     */
1324     if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding(d_vol->v_obj)))) {
1325         retvalue =AFPERR_PARAM;
1326         goto copy_exit;
1327     }
1328
1329     if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1330         retvalue = err;
1331         goto copy_exit;
1332     }
1333     curdir->d_offcnt++;
1334
1335     setvoltime(obj, d_vol );
1336
1337 copy_exit:
1338     ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1339     return( retvalue );
1340 }
1341
1342 /* ----------------------------------
1343  * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1344  * because we are doing it elsewhere.
1345  * currently if newname is NULL then adp is NULL. 
1346  */
1347 int copyfile(const struct vol *s_vol,
1348              const struct vol *d_vol, 
1349              int sfd,
1350              char *src,
1351              char *dst,
1352              char *newname,
1353              struct adouble *adp)
1354 {
1355     struct adouble      ads, add;
1356     int                 err = 0;
1357     int                 ret_err = 0;
1358     int                 adflags;
1359     int                 stat_result;
1360     struct stat         st;
1361     
1362     LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1363         sfd, src, dst, newname);
1364
1365     if (adp == NULL) {
1366         ad_init(&ads, s_vol);
1367         adp = &ads;
1368     }
1369
1370     adflags = ADFLAGS_DF | ADFLAGS_RF | ADFLAGS_NORF;
1371
1372     if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1373         ret_err = errno;
1374         goto done;
1375     }
1376
1377     if (!AD_RSRC_OPEN(adp))
1378         /* no resource fork, don't create one for dst file */
1379         adflags &= ~ADFLAGS_RF;
1380
1381     stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1382
1383     if (stat_result < 0) {           
1384       /* unlikely but if fstat fails, the default file mode will be 0666. */
1385       st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1386     }
1387
1388     ad_init(&add, d_vol);
1389     if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1390         ret_err = errno;
1391         ad_close( adp, adflags );
1392         if (EEXIST != ret_err) {
1393             deletefile(d_vol, -1, dst, 0);
1394             goto done;
1395         }
1396         return AFPERR_EXIST;
1397     }
1398
1399     if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1400         LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1401
1402     if (err == 0)
1403         if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1404             LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1405
1406     if (err < 0)
1407        ret_err = errno;
1408
1409     if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1410         /* set the new name in the resource fork */
1411         ad_copy_header(&add, adp);
1412         ad_setname(&add, newname);
1413         ad_flush( &add );
1414     }
1415     ad_close( adp, adflags );
1416
1417     if (ad_close( &add, adflags ) <0) {
1418        ret_err = errno;
1419     } 
1420
1421     if (ret_err) {
1422         deletefile(d_vol, -1, dst, 0);
1423     }
1424     else if (stat_result == 0) {
1425         /* set dest modification date to src date */
1426         struct utimbuf  ut;
1427
1428         ut.actime = ut.modtime = st.st_mtime;
1429         utime(dst, &ut);
1430         /* FIXME netatalk doesn't use resource fork file date
1431          * but maybe we should set its modtime too.
1432         */
1433     }
1434
1435 done:
1436     switch ( ret_err ) {
1437     case 0:
1438         return AFP_OK;
1439     case EDQUOT:
1440     case EFBIG:
1441     case ENOSPC:
1442         return AFPERR_DFULL;
1443     case ENOENT:
1444         return AFPERR_NOOBJ;
1445     case EACCES:
1446         return AFPERR_ACCESS;
1447     case EROFS:
1448         return AFPERR_VLOCK;
1449     }
1450     return AFPERR_PARAM;
1451 }
1452
1453
1454 /* -----------------------------------
1455    vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1456    checkAttrib:   1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1457
1458    when deletefile is called we don't have lock on it, file is closed (for us)
1459    untrue if called by renamefile
1460    
1461    ad_open always try to open file RDWR first and ad_lock takes care of
1462    WRITE lock on read only file.
1463 */
1464
1465 static int check_attrib(struct adouble *adp)
1466 {
1467 uint16_t   bshort = 0;
1468
1469         ad_getattr(adp, &bshort);
1470     /*
1471      * Does kFPDeleteInhibitBit (bit 8) set?
1472      */
1473         if ((bshort & htons(ATTRBIT_NODELETE))) {
1474                 return AFPERR_OLOCK;
1475         }
1476     if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1477         return AFPERR_BUSY;
1478         }
1479         return 0;
1480 }
1481 /* 
1482  * dirfd can be used for unlinkat semantics
1483  */
1484 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1485 {
1486     struct adouble      ad;
1487     struct adouble      *adp = NULL;
1488     int                 adflags, err = AFP_OK;
1489     int                 meta = 0;
1490
1491     LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1492
1493     ad_init(&ad, vol);
1494     if (checkAttrib) {
1495         /* was EACCESS error try to get only metadata */
1496         /* we never want to create a resource fork here, we are going to delete it 
1497          * moreover sometimes deletefile is called with a no existent file and 
1498          * ad_open would create a 0 byte resource fork
1499         */
1500         if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1501             if ((err = check_attrib(&ad))) {
1502                 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1503                return err;
1504             }
1505             meta = 1;
1506         }
1507     }
1508  
1509     /* try to open both forks at once */
1510     adflags = ADFLAGS_DF;
1511     if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1512         switch (errno) {
1513         case ENOENT:
1514             err = AFPERR_NOOBJ;
1515             goto end;
1516         case EACCES: /* maybe it's a file with no write mode for us */
1517             break;   /* was return AFPERR_ACCESS;*/
1518         case EROFS:
1519             err = AFPERR_VLOCK;
1520             goto end;
1521         default:
1522             err = AFPERR_PARAM;
1523             goto end;
1524         }
1525     }
1526     else {
1527         adp = &ad;
1528     }
1529
1530     if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1531         adflags |= ADFLAGS_RF;
1532         /* FIXME we have a pb here because we want to know if a file is open 
1533          * there's a 'priority inversion' if you can't open the ressource fork RW
1534          * you can delete it if it's open because you can't get a write lock.
1535          * 
1536          * ADLOCK_FILELOCK means the whole ressource fork, not only after the 
1537          * metadatas
1538          *
1539          * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1540          */
1541         if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1542             err = AFPERR_BUSY;
1543             goto end;
1544         }
1545     }
1546
1547     if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1548         LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1549         err = AFPERR_BUSY;
1550     } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1551         cnid_t id;
1552         if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1553             cnid_delete(vol->v_cdb, id);
1554         }
1555     }
1556
1557 end:
1558     if (meta)
1559         ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1560
1561     if (adp)
1562         ad_close( &ad, adflags );  /* ad_close removes locks if any */
1563
1564     return err;
1565 }
1566
1567 /* ------------------------------------ */
1568 /* return a file id */
1569 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1570 {
1571     struct stat         *st;
1572     struct vol          *vol;
1573     struct dir          *dir;
1574     char                *upath;
1575     int                 len;
1576     cnid_t              did, id;
1577     u_short             vid;
1578     struct path         *s_path;
1579
1580     *rbuflen = 0;
1581
1582     ibuf += 2;
1583
1584     memcpy(&vid, ibuf, sizeof(vid));
1585     ibuf += sizeof(vid);
1586
1587     if (NULL == ( vol = getvolbyvid( vid )) ) {
1588         return( AFPERR_PARAM);
1589     }
1590
1591     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1592         return AFPERR_NOOP;
1593     }
1594
1595     if (vol->v_flags & AFPVOL_RO)
1596         return AFPERR_VLOCK;
1597
1598     memcpy(&did, ibuf, sizeof( did ));
1599     ibuf += sizeof(did);
1600
1601     if (NULL == ( dir = dirlookup( vol, did )) ) {
1602         return afp_errno; /* was AFPERR_PARAM */
1603     }
1604
1605     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1606         return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1607     }
1608
1609     if ( path_isadir(s_path) ) {
1610         return( AFPERR_BADTYPE );
1611     }
1612
1613     upath = s_path->u_name;
1614     switch (s_path->st_errno) {
1615         case 0:
1616              break; /* success */
1617         case EPERM:
1618         case EACCES:
1619             return AFPERR_ACCESS;
1620         case ENOENT:
1621             return AFPERR_NOOBJ;
1622         default:
1623             return AFPERR_PARAM;
1624     }
1625     st = &s_path->st;
1626     if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1627         memcpy(rbuf, &id, sizeof(id));
1628         *rbuflen = sizeof(id);
1629         return AFPERR_EXISTID;
1630     }
1631
1632     if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1633         memcpy(rbuf, &id, sizeof(id));
1634         *rbuflen = sizeof(id);
1635         return AFP_OK;
1636     }
1637
1638     return afp_errno;
1639 }
1640
1641 /* ------------------------------- */
1642 struct reenum {
1643     struct vol *vol;
1644     cnid_t     did;
1645 };
1646
1647 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1648 {
1649     struct path   path;
1650     struct reenum *param = data;
1651     struct vol    *vol = param->vol;  
1652     cnid_t        did  = param->did;
1653     cnid_t        aint;
1654     
1655     if ( lstat(de->d_name, &path.st) < 0 )
1656         return 0;
1657     
1658     /* update or add to cnid */
1659     aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1660
1661     return 0;
1662 }
1663
1664 /* --------------------
1665  * Ok the db is out of synch with the dir.
1666  * but if it's a deleted file we don't want to do it again and again.
1667 */
1668 static int
1669 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1670 {
1671     int             ret;
1672     struct reenum   data;
1673     struct stat     st;
1674     
1675     if (vol->v_cdb == NULL) {
1676         return -1;
1677     }
1678     
1679     /* FIXME use of_statdir ? */
1680     if (lstat(name, &st)) {
1681         return -1;
1682     }
1683
1684     if (dirreenumerate(dir, &st)) {
1685         /* we already did it once and the dir haven't been modified */
1686         return dir->d_offcnt;
1687     }
1688     
1689     data.vol = vol;
1690     data.did = dir->d_did;
1691     if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1692         setdiroffcnt(curdir, &st,  ret);
1693         dir->d_flags |= DIRF_CNID;
1694     }
1695
1696     return ret;
1697 }
1698
1699 /* ------------------------------
1700    resolve a file id */
1701 int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1702 {
1703     struct vol          *vol;
1704     struct dir          *dir;
1705     char                *upath;
1706     struct path         path;
1707     int                 err, retry=0;
1708     size_t              buflen;
1709     cnid_t              id, cnid;
1710     uint16_t            vid, bitmap;
1711
1712     static char buffer[12 + MAXPATHLEN + 1];
1713     int len = 12 + MAXPATHLEN + 1;
1714
1715     *rbuflen = 0;
1716     ibuf += 2;
1717
1718     memcpy(&vid, ibuf, sizeof(vid));
1719     ibuf += sizeof(vid);
1720
1721     if (NULL == ( vol = getvolbyvid( vid )) ) {
1722         return( AFPERR_PARAM);
1723     }
1724
1725     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1726         return AFPERR_NOOP;
1727     }
1728
1729     memcpy(&id, ibuf, sizeof( id ));
1730     ibuf += sizeof(id);
1731     cnid = id;
1732     
1733     if (!id) {
1734         /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1735         return AFPERR_NOID;
1736     }
1737 retry:
1738     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1739         return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1740     }
1741
1742     if (NULL == ( dir = dirlookup( vol, id )) ) {
1743         return AFPERR_NOID; /* idem AFPERR_PARAM */
1744     }
1745     if (movecwd(vol, dir) < 0) {
1746         switch (errno) {
1747         case EACCES:
1748         case EPERM:
1749             return AFPERR_ACCESS;
1750         case ENOENT:
1751             return AFPERR_NOID;
1752         default:
1753             return AFPERR_PARAM;
1754         }
1755     }
1756
1757     memset(&path, 0, sizeof(path));
1758     path.u_name = upath;
1759     if ( of_stat(&path) < 0 ) {
1760 #ifdef ESTALE
1761         /* with nfs and our working directory is deleted */
1762         if (errno == ESTALE) {
1763             errno = ENOENT;
1764         }
1765 #endif  
1766         if ( errno == ENOENT && !retry) {
1767             /* cnid db is out of sync, reenumerate the directory and update ids */
1768             reenumerate_id(vol, ".", dir);
1769             id = cnid;
1770             retry = 1;
1771             goto retry;
1772         }
1773         switch (errno) {
1774         case EACCES:
1775         case EPERM:
1776             return AFPERR_ACCESS;
1777         case ENOENT:
1778             return AFPERR_NOID;
1779         default:
1780             return AFPERR_PARAM;
1781         }
1782     }
1783
1784     /* directories are bad */
1785     if (S_ISDIR(path.st.st_mode)) {
1786         /* OS9 and OSX don't return the same error code  */
1787         return (obj->afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1788     }
1789
1790     memcpy(&bitmap, ibuf, sizeof(bitmap));
1791     bitmap = ntohs( bitmap );
1792     if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding(obj)))) {
1793         return AFPERR_NOID;
1794     }
1795     path.id = cnid;
1796     if (AFP_OK != (err = getfilparams(obj, vol, bitmap, &path , curdir, 
1797                                       rbuf + sizeof(bitmap), &buflen, 0))) {
1798         return err;
1799     }
1800     *rbuflen = buflen + sizeof(bitmap);
1801     memcpy(rbuf, ibuf, sizeof(bitmap));
1802
1803     return AFP_OK;
1804 }
1805
1806 /* ------------------------------ */
1807 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1808 {
1809     struct stat         st;
1810     struct vol          *vol;
1811     struct dir          *dir;
1812     char                *upath;
1813     int                 err;
1814     cnid_t              id;
1815     cnid_t              fileid;
1816     u_short             vid;
1817     static char buffer[12 + MAXPATHLEN + 1];
1818     int len = 12 + MAXPATHLEN + 1;
1819
1820     *rbuflen = 0;
1821     ibuf += 2;
1822
1823     memcpy(&vid, ibuf, sizeof(vid));
1824     ibuf += sizeof(vid);
1825
1826     if (NULL == ( vol = getvolbyvid( vid )) ) {
1827         return( AFPERR_PARAM);
1828     }
1829
1830     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1831         return AFPERR_NOOP;
1832     }
1833
1834     if (vol->v_flags & AFPVOL_RO)
1835         return AFPERR_VLOCK;
1836
1837     memcpy(&id, ibuf, sizeof( id ));
1838     ibuf += sizeof(id);
1839     fileid = id;
1840
1841     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1842         return AFPERR_NOID;
1843     }
1844
1845     if (NULL == ( dir = dirlookup( vol, id )) ) {
1846         if (afp_errno == AFPERR_NOOBJ) {
1847             err = AFPERR_NOOBJ;
1848             goto delete;
1849         }
1850         return( AFPERR_PARAM );
1851     }
1852
1853     err = AFP_OK;
1854     if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1855         switch (errno) {
1856         case EACCES:
1857         case EPERM:
1858             return AFPERR_ACCESS;
1859 #ifdef ESTALE
1860         case ESTALE:
1861 #endif  
1862         case ENOENT:
1863             /* still try to delete the id */
1864             err = AFPERR_NOOBJ;
1865             break;
1866         default:
1867             return AFPERR_PARAM;
1868         }
1869     }
1870     else if (S_ISDIR(st.st_mode)) /* directories are bad */
1871         return AFPERR_BADTYPE;
1872
1873 delete:
1874     if (cnid_delete(vol->v_cdb, fileid)) {
1875         switch (errno) {
1876         case EROFS:
1877             return AFPERR_VLOCK;
1878         case EPERM:
1879         case EACCES:
1880             return AFPERR_ACCESS;
1881         default:
1882             return AFPERR_PARAM;
1883         }
1884     }
1885
1886     return err;
1887 }
1888
1889 /* ------------------------------ */
1890 static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
1891 {
1892     int             ret;
1893
1894     if (path->st_errno) {
1895         switch (path->st_errno) {
1896         case ENOENT:
1897             afp_errno = AFPERR_NOID;
1898             break;
1899         case EPERM:
1900         case EACCES:
1901             afp_errno = AFPERR_ACCESS;
1902             break;
1903         default:
1904             afp_errno = AFPERR_PARAM;
1905             break;
1906         }
1907         return NULL;
1908     }
1909     /* we use file_access both for legacy Mac perm and
1910      * for unix privilege, rename will take care of folder perms
1911     */
1912     if (file_access(obj, vol, path, OPENACC_WR ) < 0) {
1913         afp_errno = AFPERR_ACCESS;
1914         return NULL;
1915     }
1916     
1917     if ((*of = of_findname(path))) {
1918         /* reuse struct adouble so it won't break locks */
1919         adp = (*of)->of_ad;
1920     }
1921     else {
1922         ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
1923         /* META and HF */
1924         if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1925             /* from AFP spec.
1926              * The user must have the Read & Write privilege for both files in order to use this command.
1927              */
1928             ad_close(adp, ADFLAGS_HF);
1929             afp_errno = AFPERR_ACCESS;
1930             return NULL;
1931         }
1932     }
1933     return adp;
1934 }
1935
1936 #define APPLETEMP ".AppleTempXXXXXX"
1937
1938 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1939 {
1940     struct stat         srcst, destst;
1941     struct vol          *vol;
1942     struct dir          *dir, *sdir;
1943     char                *spath, temp[17], *p;
1944     char                *supath, *upath;
1945     struct path         *path;
1946     int                 err;
1947     struct adouble      ads;
1948     struct adouble      add;
1949     struct adouble      *adsp = NULL;
1950     struct adouble      *addp = NULL;
1951     struct ofork        *s_of = NULL;
1952     struct ofork        *d_of = NULL;
1953     int                 crossdev;
1954     
1955     int                 slen, dlen;
1956     uint32_t            sid, did;
1957     uint16_t            vid;
1958
1959     uid_t              uid;
1960     gid_t              gid;
1961
1962     *rbuflen = 0;
1963     ibuf += 2;
1964
1965     memcpy(&vid, ibuf, sizeof(vid));
1966     ibuf += sizeof(vid);
1967
1968     if (NULL == ( vol = getvolbyvid( vid )) ) {
1969         return( AFPERR_PARAM);
1970     }
1971
1972     if ((vol->v_flags & AFPVOL_RO))
1973         return AFPERR_VLOCK;
1974
1975     /* source and destination dids */
1976     memcpy(&sid, ibuf, sizeof(sid));
1977     ibuf += sizeof(sid);
1978     memcpy(&did, ibuf, sizeof(did));
1979     ibuf += sizeof(did);
1980
1981     /* source file */
1982     if (NULL == (dir = dirlookup( vol, sid )) ) {
1983         return afp_errno; /* was AFPERR_PARAM */
1984     }
1985
1986     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1987         return get_afp_errno(AFPERR_NOOBJ); 
1988     }
1989
1990     if ( path_isadir(path) ) {
1991         return AFPERR_BADTYPE;   /* it's a dir */
1992     }
1993
1994     /* save some stuff */
1995     srcst = path->st;
1996     sdir = curdir;
1997     spath = obj->oldtmp;
1998     supath = obj->newtmp;
1999     strcpy(spath, path->m_name);
2000     strcpy(supath, path->u_name); /* this is for the cnid changing */
2001     p = absupath( vol, sdir, supath);
2002     if (!p) {
2003         /* pathname too long */
2004         return AFPERR_PARAM ;
2005     }
2006     
2007     ad_init(&ads, vol);
2008     if (!(adsp = find_adouble(obj, vol, path, &s_of, &ads))) {
2009         return afp_errno;
2010     }
2011
2012     /* ***** from here we may have resource fork open **** */
2013     
2014     /* look for the source cnid. if it doesn't exist, don't worry about
2015      * it. */
2016     sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2017
2018     if (NULL == ( dir = dirlookup( vol, did )) ) {
2019         err = afp_errno; /* was AFPERR_PARAM */
2020         goto err_exchangefile;
2021     }
2022
2023     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2024         err = get_afp_errno(AFPERR_NOOBJ); 
2025         goto err_exchangefile;
2026     }
2027
2028     if ( path_isadir(path) ) {
2029         err = AFPERR_BADTYPE;
2030         goto err_exchangefile;
2031     }
2032
2033     /* FPExchangeFiles is the only call that can return the SameObj
2034      * error */
2035     if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2036         err = AFPERR_SAMEOBJ;
2037         goto err_exchangefile;
2038     }
2039
2040     ad_init(&add, vol);
2041     if (!(addp = find_adouble(obj, vol, path, &d_of, &add))) {
2042         err = afp_errno;
2043         goto err_exchangefile;
2044     }
2045     destst = path->st;
2046
2047     /* they are not on the same device and at least one is open
2048      * FIXME broken for for crossdev and adouble v2
2049      * return an error 
2050     */
2051     crossdev = (srcst.st_dev != destst.st_dev);
2052     if (/* (d_of || s_of)  && */ crossdev) {
2053         err = AFPERR_MISC;
2054         goto err_exchangefile;
2055     }
2056
2057     /* look for destination id. */
2058     upath = path->u_name;
2059     did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2060
2061     /* construct a temp name.
2062      * NOTE: the temp file will be in the dest file's directory. it
2063      * will also be inaccessible from AFP. */
2064     memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2065     if (!mktemp(temp)) {
2066         err = AFPERR_MISC;
2067         goto err_exchangefile;
2068     }
2069     
2070     if (crossdev) {
2071         /* FIXME we need to close fork for copy, both s_of and d_of are null */
2072        ad_close(adsp, ADFLAGS_HF);
2073        ad_close(addp, ADFLAGS_HF);
2074     }
2075
2076     /* now, quickly rename the file. we error if we can't. */
2077     if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2078         goto err_exchangefile;
2079     of_rename(vol, s_of, sdir, spath, curdir, temp);
2080
2081     /* rename destination to source */
2082     if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2083         goto err_src_to_tmp;
2084     of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2085
2086     /* rename temp to destination */
2087     if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2088         goto err_dest_to_src;
2089     of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2090
2091     /* id's need switching. src -> dest and dest -> src. 
2092      * we need to re-stat() if it was a cross device copy.
2093     */
2094     if (sid)
2095         cnid_delete(vol->v_cdb, sid);
2096     if (did)
2097         cnid_delete(vol->v_cdb, did);
2098
2099     if ((did && ( (crossdev && lstat( upath, &srcst) < 0) || 
2100                 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2101        ||
2102        (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2103                 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2104     ) {
2105         switch (errno) {
2106         case EPERM:
2107         case EACCES:
2108             err = AFPERR_ACCESS;
2109             break;
2110         default:
2111             err = AFPERR_PARAM;
2112         }
2113         goto err_temp_to_dest;
2114     }
2115     
2116     /* here we need to reopen if crossdev */
2117     if (sid && ad_setid(addp, destst.st_dev, destst.st_ino,  sid, sdir->d_did, vol->v_stamp)) 
2118     {
2119        ad_flush( addp );
2120     }
2121         
2122     if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino,  did, curdir->d_did, vol->v_stamp)) 
2123     {
2124        ad_flush( adsp );
2125     }
2126
2127     /* change perms, src gets dest perm and vice versa */
2128
2129     uid = geteuid();
2130     gid = getegid();
2131     if (seteuid(0)) {
2132         LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2133         err = AFP_OK; /* ignore error */
2134         goto err_temp_to_dest;
2135     }
2136
2137     /*
2138      * we need to exchange ACL entries as well
2139      */
2140     /* exchange_acls(vol, p, upath); */
2141
2142     path->st = srcst;
2143     path->st_valid = 1;
2144     path->st_errno = 0;
2145     path->m_name = NULL;
2146     path->u_name = upath;
2147
2148     setfilunixmode(vol, path, destst.st_mode);
2149     setfilowner(vol, destst.st_uid, destst.st_gid, path);
2150
2151     path->st = destst;
2152     path->st_valid = 1;
2153     path->st_errno = 0;
2154     path->u_name = p;
2155
2156     setfilunixmode(vol, path, srcst.st_mode);
2157     setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2158
2159     if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2160         LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2161         exit(EXITERR_SYS);
2162     }
2163
2164     err = AFP_OK;
2165     goto err_exchangefile;
2166
2167     /* all this stuff is so that we can unwind a failed operation
2168      * properly. */
2169 err_temp_to_dest:
2170     /* rename dest to temp */
2171     renamefile(vol, -1, upath, temp, temp, adsp);
2172     of_rename(vol, s_of, curdir, upath, curdir, temp);
2173
2174 err_dest_to_src:
2175     /* rename source back to dest */
2176     renamefile(vol, -1, p, upath, path->m_name, addp);
2177     of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2178
2179 err_src_to_tmp:
2180     /* rename temp back to source */
2181     renamefile(vol, -1, temp, p, spath, adsp);
2182     of_rename(vol, s_of, curdir, temp, sdir, spath);
2183
2184 err_exchangefile:
2185     if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2186        ad_close(adsp, ADFLAGS_HF);
2187     }
2188     if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2189        ad_close(addp, ADFLAGS_HF);
2190     }
2191
2192     struct dir *cached;
2193     if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2194         (void)dir_remove(vol, cached);
2195     if ((cached = dircache_search_by_did(vol, did)) != NULL)
2196         (void)dir_remove(vol, cached);
2197
2198     return err;
2199 }