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