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