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