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