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