]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/file.c
Fix several warnings, remove const
[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 createfile_done:
755     curdir->d_offcnt++;
756
757     setvoltime(obj, vol );
758
759     return (retvalue);
760 }
761
762 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
763 {
764     struct vol  *vol;
765     struct dir  *dir;
766     struct path *s_path;
767     int         did, rc;
768     uint16_t    vid, bitmap;
769
770     *rbuflen = 0;
771     ibuf += 2;
772
773     memcpy(&vid, ibuf, sizeof( vid ));
774     ibuf += sizeof( vid );
775     if (NULL == ( vol = getvolbyvid( vid )) ) {
776         return( AFPERR_PARAM );
777     }
778
779     if (vol->v_flags & AFPVOL_RO)
780         return AFPERR_VLOCK;
781
782     memcpy(&did, ibuf, sizeof( did ));
783     ibuf += sizeof( did );
784     if (NULL == ( dir = dirlookup( vol, did )) ) {
785         return afp_errno; /* was AFPERR_NOOBJ */
786     }
787
788     memcpy(&bitmap, ibuf, sizeof( bitmap ));
789     bitmap = ntohs( bitmap );
790     ibuf += sizeof( bitmap );
791
792     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
793         return get_afp_errno(AFPERR_PARAM);
794     }
795
796     if (path_isadir(s_path)) {
797         return( AFPERR_BADTYPE ); /* it's a directory */
798     }
799
800     if ( s_path->st_errno != 0 ) {
801         return( AFPERR_NOOBJ );
802     }
803
804     if ((u_long)ibuf & 1 ) {
805         ibuf++;
806     }
807
808     if (AFP_OK == ( rc = setfilparams(obj, vol, s_path, bitmap, ibuf )) ) {
809         setvoltime(obj, vol );
810     }
811
812     return( rc );
813 }
814
815 /*
816  * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic  
817  * 
818 */
819 extern struct path Cur_Path;
820
821 int setfilparams(const AFPObj *obj, struct vol *vol,
822                  struct path *path, uint16_t f_bitmap, char *buf )
823 {
824     struct adouble      ad, *adp;
825     struct extmap       *em;
826     int                 bit, isad = 1, err = AFP_OK;
827     char                *upath;
828     u_char              achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
829     uint16_t            ashort, bshort, oshort;
830     uint32_t            aint;
831     uint32_t            upriv;
832     uint16_t           upriv_bit = 0;
833         struct utimbuf  ut;
834     int                 change_mdate = 0;
835     int                 change_parent_mdate = 0;
836     int                 newdate = 0;
837     struct timeval      tv;
838     uid_t               f_uid;
839     gid_t               f_gid;
840     uint16_t           bitmap = f_bitmap;
841     uint32_t           cdate,bdate;
842     u_char              finder_buf[32];
843     int symlinked = S_ISLNK(path->st.st_mode);
844     int fp;
845     ssize_t len;
846     char symbuf[MAXPATHLEN+1];
847
848 #ifdef DEBUG
849     LOG(log_debug9, logtype_afpd, "begin setfilparams:");
850 #endif /* DEBUG */
851
852     adp = of_ad(vol, path, &ad);
853     upath = path->u_name;
854
855     if (!vol_unix_priv(vol) && check_access(obj, vol, upath, OPENACC_WR ) < 0) {
856         return AFPERR_ACCESS;
857     }
858
859     /* with unix priv maybe we have to change adouble file priv first */
860     bit = 0;
861     while ( bitmap != 0 ) {
862         while (( bitmap & 1 ) == 0 ) {
863             bitmap = bitmap>>1;
864             bit++;
865         }
866         switch(  bit ) {
867         case FILPBIT_ATTR :
868             change_mdate = 1;
869             memcpy(&ashort, buf, sizeof( ashort ));
870             buf += sizeof( ashort );
871             break;
872         case FILPBIT_CDATE :
873             change_mdate = 1;
874             memcpy(&cdate, buf, sizeof(cdate));
875             buf += sizeof( cdate );
876             break;
877         case FILPBIT_MDATE :
878             memcpy(&newdate, buf, sizeof( newdate ));
879             buf += sizeof( newdate );
880             break;
881         case FILPBIT_BDATE :
882             change_mdate = 1;
883             memcpy(&bdate, buf, sizeof( bdate));
884             buf += sizeof( bdate );
885             break;
886         case FILPBIT_FINFO :
887             change_mdate = 1;
888             if (memcmp(buf,"slnkrhap",8) == 0
889                 && !(S_ISLNK(path->st.st_mode))
890                 && !(vol->v_flags & AFPVOL_FOLLOWSYM)) {
891                 /* request to turn this into a symlink */
892                 if ((fp = open(path->u_name, O_RDONLY)) == -1) {
893                     err = AFPERR_MISC;
894                     goto setfilparam_done;
895                 }
896                 len = read(fp, symbuf, MAXPATHLEN);
897                 close(fp);
898                 if (!(len > 0)) {
899                     err = AFPERR_MISC;
900                     goto setfilparam_done;
901                 }
902                 if (unlink(path->u_name) != 0) {
903                     err = AFPERR_MISC;
904                     goto setfilparam_done;
905                 }
906                 symbuf[len] = 0;
907                 if (symlink(symbuf, path->u_name) != 0) {
908                     err = AFPERR_MISC;
909                     goto setfilparam_done;
910                 }
911                 of_stat(vol, path);
912                 symlinked = 1;
913             }
914             memcpy(finder_buf, buf, 32 );
915             buf += 32;
916             break;
917         case FILPBIT_UNIXPR :
918             if (!vol_unix_priv(vol)) {
919                 /* this volume doesn't use unix priv */
920                 err = AFPERR_BITMAP;
921                 bitmap = 0;
922                 break;
923             }
924             change_mdate = 1;
925             change_parent_mdate = 1;
926
927             memcpy( &aint, buf, sizeof( aint ));
928             f_uid = ntohl (aint);
929             buf += sizeof( aint );
930             memcpy( &aint, buf, sizeof( aint ));
931             f_gid = ntohl (aint);
932             buf += sizeof( aint );
933             setfilowner(vol, f_uid, f_gid, path);
934
935             memcpy( &upriv, buf, sizeof( upriv ));
936             buf += sizeof( upriv );
937             upriv = ntohl (upriv);
938             if ((upriv & S_IWUSR)) {
939                 setfilunixmode(vol, path, upriv);
940             }
941             else {
942                 /* do it later */
943                 upriv_bit = 1;
944             }
945             break;
946         case FILPBIT_PDINFO :
947             if (obj->afp_version < 30) { /* else it's UTF8 name */
948                 achar = *buf;
949                 buf += 2;
950                 /* Keep special case to support crlf translations */
951                 if ((unsigned int) achar == 0x04) {
952                     fdType = (u_char *)"TEXT";
953                     buf += 2;
954                 } else {
955                     xyy[0] = ( u_char ) 'p';
956                     xyy[1] = achar;
957                     xyy[3] = *buf++;
958                     xyy[2] = *buf++;
959                     fdType = xyy;
960                 }
961                 break;
962             }
963             /* fallthrough */
964         default :
965             err = AFPERR_BITMAP;
966             /* break while loop */
967             bitmap = 0;
968             break;
969         }
970
971         bitmap = bitmap>>1;
972         bit++;
973     }
974
975     /* second try with adouble open 
976     */
977     if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
978         LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
979         /*
980          * For some things, we don't need an adouble header:
981          * - change of modification date
982          * - UNIX privs (Bug-ID #2863424)
983          */
984         if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
985             LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
986             return AFPERR_ACCESS;
987         }
988         LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
989         isad = 0;
990     } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
991         ad_setname(adp, path->m_name);
992         cnid_t id;
993         if ((id = get_id(vol, adp, &path->st, curdir->d_did, upath, strlen(upath))) == CNID_INVALID) {
994             LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
995             return AFPERR_MISC;
996         }
997         (void)ad_setid(adp, path->st.st_dev, path->st.st_ino, id, curdir->d_did, vol->v_stamp);
998     }
999     
1000     bit = 0;
1001     bitmap = f_bitmap;
1002     while ( bitmap != 0 ) {
1003         while (( bitmap & 1 ) == 0 ) {
1004             bitmap = bitmap>>1;
1005             bit++;
1006         }
1007
1008         switch(  bit ) {
1009         case FILPBIT_ATTR :
1010             ad_getattr(adp, &bshort);
1011             oshort = bshort;
1012             if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1013                 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1014             } else {
1015                 bshort &= ~ashort;
1016             }
1017             if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1018                 change_parent_mdate = 1;
1019             ad_setattr(adp, bshort);
1020             break;
1021         case FILPBIT_CDATE :
1022             ad_setdate(adp, AD_DATE_CREATE, cdate);
1023             break;
1024         case FILPBIT_MDATE :
1025             break;
1026         case FILPBIT_BDATE :
1027             ad_setdate(adp, AD_DATE_BACKUP, bdate);
1028             break;
1029         case FILPBIT_FINFO :
1030             if (default_type( ad_entry( adp, ADEID_FINDERI ))
1031                     && ( 
1032                      ((em = getextmap( path->m_name )) &&
1033                       !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1034                       !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1035                      || ((em = getdefextmap()) &&
1036                       !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1037                       !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1038             )) {
1039                 memcpy(finder_buf, ufinderi, 8 );
1040             }
1041             memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1042             break;
1043         case FILPBIT_UNIXPR :
1044             if (upriv_bit) {
1045                 setfilunixmode(vol, path, upriv);
1046             }
1047             break;
1048         case FILPBIT_PDINFO :
1049             if (obj->afp_version < 30) { /* else it's UTF8 name */
1050                 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1051                 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1052                 break;
1053             }
1054             /* fallthrough */
1055         default :
1056             err = AFPERR_BITMAP;
1057             goto setfilparam_done;
1058         }
1059         bitmap = bitmap>>1;
1060         bit++;
1061     }
1062
1063 setfilparam_done:
1064     if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1065        newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1066     }
1067     if (newdate) {
1068        if (isad)
1069           ad_setdate(adp, AD_DATE_MODIFY, newdate);
1070        ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1071        utime(upath, &ut);
1072     }
1073
1074     if (isad) {
1075         ad_flush(adp);
1076         ad_close(adp, ADFLAGS_HF);
1077     }
1078
1079     if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1080         newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1081         bitmap = 1<<FILPBIT_MDATE;
1082         setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1083     }
1084
1085 #ifdef DEBUG
1086     LOG(log_debug9, logtype_afpd, "end setfilparams:");
1087 #endif /* DEBUG */
1088     return err;
1089 }
1090
1091 /*
1092  * renamefile and copyfile take the old and new unix pathnames
1093  * and the new mac name.
1094  *
1095  * sdir_fd     source dir fd to which src path is relative (for openat et al semantics)
1096  *             passing -1 means this is not used, src path is a full path
1097  * src         the source path 
1098  * dst         the dest filename in current dir
1099  * newname     the dest mac name
1100  * adp         adouble struct of src file, if open, or & zeroed one
1101  *
1102  */
1103 int renamefile(struct vol *vol, struct dir *ddir, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1104 {
1105     int         rc;
1106
1107     LOG(log_debug, logtype_afpd,
1108         "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1109
1110     if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1111         switch ( errno ) {
1112         case ENOENT :
1113             return( AFPERR_NOOBJ );
1114         case EPERM:
1115         case EACCES :
1116             return( AFPERR_ACCESS );
1117         case EROFS:
1118             return AFPERR_VLOCK;
1119         case EXDEV :                    /* Cross device move -- try copy */
1120            /* NOTE: with open file it's an error because after the copy we will 
1121             * get two files, it's fixable for our process (eg reopen the new file, get the
1122             * locks, and so on. But it doesn't solve the case with a second process
1123             */
1124             if (adp->ad_open_forks) {
1125                 /* FIXME  warning in syslog so admin'd know there's a conflict ?*/
1126                 return AFPERR_OLOCK; /* little lie */
1127             }
1128             if (AFP_OK != ( rc = copyfile(vol, vol, ddir, sdir_fd, src, dst, newname, NULL )) ) {
1129                 /* on error copyfile delete dest */
1130                 return( rc );
1131             }
1132             return deletefile(vol, sdir_fd, src, 0);
1133         default :
1134             return( AFPERR_PARAM );
1135         }
1136     }
1137
1138     if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1139         int err;
1140         
1141         err = errno;        
1142         /* try to undo the data fork rename,
1143          * we know we are on the same device 
1144         */
1145         if (err) {
1146         unix_rename(-1, dst, sdir_fd, src ); 
1147             /* return the first error */
1148             switch ( err) {
1149             case ENOENT :
1150                 return AFPERR_NOOBJ;
1151             case EPERM:
1152             case EACCES :
1153                 return AFPERR_ACCESS ;
1154             case EROFS:
1155                 return AFPERR_VLOCK;
1156             default :
1157                 return AFPERR_PARAM ;
1158             }
1159         }
1160     }
1161
1162     /* don't care if we can't open the newly renamed ressource fork */
1163     if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1164         ad_setname(adp, newname);
1165         ad_flush( adp );
1166         ad_close( adp, ADFLAGS_HF );
1167     }
1168
1169     return( AFP_OK );
1170 }
1171
1172 /* ---------------- 
1173    convert a Mac long name to an utf8 name,
1174 */
1175 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1176 {
1177 size_t    outlen;
1178
1179     if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1180         return -1;
1181     }
1182     return outlen;
1183 }
1184
1185 /* ---------------- */
1186 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1187 {
1188 char        type = *ibuf;
1189 size_t      plen = 0;
1190 uint16_t   len16;
1191 uint32_t   hint;
1192
1193     if ( type != 2 && !(vol->v_obj->afp_version >= 30 && type == 3) ) {
1194         return -1;
1195     }
1196     ibuf++;
1197     switch (type) {
1198     case 2:
1199         if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1200             if (vol->v_obj->afp_version >= 30) {
1201                 /* convert it to UTF8 
1202                 */
1203                 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1204                    return -1;
1205             }
1206             else {
1207                 strncpy( newname, ibuf, plen );
1208                 newname[ plen ] = '\0';
1209             }
1210             if (strlen(newname) != plen) {
1211                 /* there's \0 in newname, e.g. it's a pathname not
1212                  * only a filename. 
1213                 */
1214                 return -1;
1215             }
1216         }
1217         break;
1218     case 3:
1219         memcpy(&hint, ibuf, sizeof(hint));
1220         ibuf += sizeof(hint);
1221            
1222         memcpy(&len16, ibuf, sizeof(len16));
1223         ibuf += sizeof(len16);
1224         plen = ntohs(len16);
1225         
1226         if (plen) {
1227             if (plen > AFPOBJ_TMPSIZ) {
1228                 return -1;
1229             }
1230             strncpy( newname, ibuf, plen );
1231             newname[ plen ] = '\0';
1232             if (strlen(newname) != plen) {
1233                 return -1;
1234             }
1235         }
1236         break;
1237     }
1238     return plen;
1239 }
1240
1241 /* -----------------------------------
1242 */
1243 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1244 {
1245     struct vol  *s_vol, *d_vol;
1246     struct dir  *dir;
1247     char        *newname, *p, *upath;
1248     struct path *s_path;
1249     uint32_t    sdid, ddid;
1250     int         err, retvalue = AFP_OK;
1251     uint16_t    svid, dvid;
1252
1253     struct adouble ad, *adp;
1254     int denyreadset;
1255     
1256     *rbuflen = 0;
1257     ibuf += 2;
1258
1259     memcpy(&svid, ibuf, sizeof( svid ));
1260     ibuf += sizeof( svid );
1261     if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1262         return( AFPERR_PARAM );
1263     }
1264
1265     memcpy(&sdid, ibuf, sizeof( sdid ));
1266     ibuf += sizeof( sdid );
1267     if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1268         return afp_errno;
1269     }
1270
1271     memcpy(&dvid, ibuf, sizeof( dvid ));
1272     ibuf += sizeof( dvid );
1273     memcpy(&ddid, ibuf, sizeof( ddid ));
1274     ibuf += sizeof( ddid );
1275
1276     if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1277         return get_afp_errno(AFPERR_PARAM);
1278     }
1279     if ( path_isadir(s_path) ) {
1280         return( AFPERR_BADTYPE );
1281     }
1282
1283     /* don't allow copies when the file is open.
1284      * XXX: the spec only calls for read/deny write access.
1285      *      however, copyfile doesn't have any of that info,
1286      *      and locks need to stay coherent. as a result,
1287      *      we just balk if the file is opened already. */
1288
1289     adp = of_ad(s_vol, s_path, &ad);
1290
1291     if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1292         return AFPERR_DENYCONF;
1293     }
1294 #ifdef HAVE_FSHARE_T
1295     fshare_t shmd;
1296     shmd.f_access = F_RDACC;
1297     shmd.f_deny = F_NODNY;
1298     if (fcntl(ad_data_fileno(adp), F_SHARE, &shmd) != 0) {
1299         retvalue = AFPERR_DENYCONF;
1300         goto copy_exit;
1301     }
1302     if (AD_RSRC_OPEN(adp) && fcntl(ad_reso_fileno(adp), F_SHARE, &shmd) != 0) {
1303         retvalue = AFPERR_DENYCONF;
1304         goto copy_exit;
1305     }
1306 #endif
1307     denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 || 
1308                   ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1309
1310     if (denyreadset) {
1311         retvalue = AFPERR_DENYCONF;
1312         goto copy_exit;
1313     }
1314
1315     newname = obj->newtmp;
1316     strcpy( newname, s_path->m_name );
1317
1318     p = ctoupath( s_vol, curdir, newname );
1319     if (!p) {
1320         retvalue = AFPERR_PARAM;
1321         goto copy_exit;
1322     }
1323
1324     if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1325         retvalue = AFPERR_PARAM;
1326         goto copy_exit;
1327     }
1328
1329     if (d_vol->v_flags & AFPVOL_RO) {
1330         retvalue = AFPERR_VLOCK;
1331         goto copy_exit;
1332     }
1333
1334     if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1335         retvalue = afp_errno;
1336         goto copy_exit;
1337     }
1338
1339     if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1340         retvalue = get_afp_errno(AFPERR_NOOBJ);
1341         goto copy_exit;
1342     }
1343     
1344     if ( *s_path->m_name != '\0' ) {
1345         retvalue =path_error(s_path, AFPERR_NOOBJ);
1346         goto copy_exit;
1347     }
1348
1349     /* one of the handful of places that knows about the path type */
1350     if (copy_path_name(d_vol, newname, ibuf) < 0) {
1351         retvalue = AFPERR_PARAM;
1352         goto copy_exit;
1353     }
1354     /* newname is always only a filename so curdir *is* its
1355      * parent folder
1356     */
1357     if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding(d_vol->v_obj)))) {
1358         retvalue =AFPERR_PARAM;
1359         goto copy_exit;
1360     }
1361
1362     if ( (err = copyfile(s_vol, d_vol, curdir, -1, p, upath , newname, adp)) < 0 ) {
1363         retvalue = err;
1364         goto copy_exit;
1365     }
1366     curdir->d_offcnt++;
1367
1368     setvoltime(obj, d_vol );
1369
1370 copy_exit:
1371     ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1372     return( retvalue );
1373 }
1374
1375 /* ----------------------------------
1376  * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1377  * because we are doing it elsewhere.
1378  * currently if newname is NULL then adp is NULL. 
1379  */
1380 int copyfile(struct vol *s_vol,
1381              struct vol *d_vol, 
1382              struct dir *d_dir, 
1383              int sfd,
1384              char *src,
1385              char *dst,
1386              char *newname,
1387              struct adouble *adp)
1388 {
1389     struct adouble      ads, add;
1390     int                 err = 0;
1391     int                 ret_err = 0;
1392     int                 adflags;
1393     int                 stat_result;
1394     struct stat         st;
1395     
1396     LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1397         sfd, src, dst, newname);
1398
1399     if (adp == NULL) {
1400         ad_init(&ads, s_vol);
1401         adp = &ads;
1402     }
1403
1404     adflags = ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RF | ADFLAGS_NORF;
1405
1406     if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1407         ret_err = errno;
1408         goto done;
1409     }
1410
1411     if (!AD_META_OPEN(adp))
1412         /* no resource fork, don't create one for dst file */
1413         adflags &= ~ADFLAGS_HF;
1414
1415     if (!AD_RSRC_OPEN(adp))
1416         /* no resource fork, don't create one for dst file */
1417         adflags &= ~ADFLAGS_RF;
1418
1419     stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1420
1421     if (stat_result < 0) {           
1422       /* unlikely but if fstat fails, the default file mode will be 0666. */
1423       st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1424     }
1425
1426     ad_init(&add, d_vol);
1427     if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1428         ret_err = errno;
1429         ad_close( adp, adflags );
1430         if (EEXIST != ret_err) {
1431             deletefile(d_vol, -1, dst, 0);
1432             goto done;
1433         }
1434         return AFPERR_EXIST;
1435     }
1436
1437     if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1438         LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1439
1440     if (err == 0)
1441         if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1442             LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1443
1444     if (err < 0)
1445        ret_err = errno;
1446
1447     if (AD_META_OPEN(&add)) {
1448         if (AD_META_OPEN(adp))
1449             ad_copy_header(&add, adp);
1450         ad_setname(&add, dst);
1451         cnid_t id;
1452         struct stat stdest;
1453         if (fstat(ad_meta_fileno(&add), &stdest) != 0) {
1454             ret_err = errno;
1455             goto error;
1456         }
1457         if ((id = get_id(d_vol, &add, &stdest, d_dir->d_did, dst, strlen(dst))) == CNID_INVALID) {
1458             ret_err = EINVAL;
1459             goto error;
1460         }
1461         (void)ad_setid(&add, stdest.st_dev, stdest.st_ino, id, d_dir->d_did, d_vol->v_stamp);
1462         ad_flush(&add);
1463     }
1464
1465 error:
1466     ad_close( adp, adflags );
1467
1468     if (ad_close( &add, adflags ) <0) {
1469        ret_err = errno;
1470     } 
1471
1472     if (ret_err) {
1473         deletefile(d_vol, -1, dst, 0);
1474     }
1475     else if (stat_result == 0) {
1476         /* set dest modification date to src date */
1477         struct utimbuf  ut;
1478
1479         ut.actime = ut.modtime = st.st_mtime;
1480         utime(dst, &ut);
1481         /* FIXME netatalk doesn't use resource fork file date
1482          * but maybe we should set its modtime too.
1483         */
1484     }
1485
1486 done:
1487     switch ( ret_err ) {
1488     case 0:
1489         return AFP_OK;
1490     case EDQUOT:
1491     case EFBIG:
1492     case ENOSPC:
1493         LOG(log_info, logtype_afpd, "copyfile: DISK FULL");
1494         return AFPERR_DFULL;
1495     case ENOENT:
1496         return AFPERR_NOOBJ;
1497     case EACCES:
1498         return AFPERR_ACCESS;
1499     case EROFS:
1500         return AFPERR_VLOCK;
1501     }
1502     return AFPERR_PARAM;
1503 }
1504
1505
1506 /* -----------------------------------
1507    vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1508    checkAttrib:   1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1509
1510    when deletefile is called we don't have lock on it, file is closed (for us)
1511    untrue if called by renamefile
1512    
1513    ad_open always try to open file RDWR first and ad_lock takes care of
1514    WRITE lock on read only file.
1515 */
1516
1517 static int check_attrib(struct adouble *adp)
1518 {
1519 uint16_t   bshort = 0;
1520
1521         ad_getattr(adp, &bshort);
1522     /*
1523      * Does kFPDeleteInhibitBit (bit 8) set?
1524      */
1525         if ((bshort & htons(ATTRBIT_NODELETE))) {
1526                 return AFPERR_OLOCK;
1527         }
1528     if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1529         return AFPERR_BUSY;
1530         }
1531         return 0;
1532 }
1533 /* 
1534  * dirfd can be used for unlinkat semantics
1535  */
1536 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1537 {
1538     struct adouble      ad;
1539     struct adouble      *adp = NULL;
1540     int                 adflags, err = AFP_OK;
1541     int                 meta = 0;
1542
1543     LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1544
1545     ad_init(&ad, vol);
1546     if (checkAttrib) {
1547         /* was EACCESS error try to get only metadata */
1548         /* we never want to create a resource fork here, we are going to delete it 
1549          * moreover sometimes deletefile is called with a no existent file and 
1550          * ad_open would create a 0 byte resource fork
1551         */
1552         if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1553             if ((err = check_attrib(&ad))) {
1554                 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1555                return err;
1556             }
1557             meta = 1;
1558         }
1559     }
1560  
1561     /* try to open both forks at once */
1562     adflags = ADFLAGS_DF;
1563     if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1564         switch (errno) {
1565         case ENOENT:
1566             err = AFPERR_NOOBJ;
1567             goto end;
1568         case EACCES: /* maybe it's a file with no write mode for us */
1569             break;   /* was return AFPERR_ACCESS;*/
1570         case EROFS:
1571             err = AFPERR_VLOCK;
1572             goto end;
1573         default:
1574             err = AFPERR_PARAM;
1575             goto end;
1576         }
1577     }
1578     else {
1579         adp = &ad;
1580     }
1581
1582     if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1583         adflags |= ADFLAGS_RF;
1584         /* FIXME we have a pb here because we want to know if a file is open 
1585          * there's a 'priority inversion' if you can't open the ressource fork RW
1586          * you can delete it if it's open because you can't get a write lock.
1587          * 
1588          * ADLOCK_FILELOCK means the whole ressource fork, not only after the 
1589          * metadatas
1590          *
1591          * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1592          */
1593         if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1594             err = AFPERR_BUSY;
1595             goto end;
1596         }
1597     }
1598
1599     if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1600         LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1601         err = AFPERR_BUSY;
1602     } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1603         cnid_t id;
1604         if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1605             cnid_delete(vol->v_cdb, id);
1606         }
1607     }
1608
1609 end:
1610     if (meta)
1611         ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1612
1613     if (adp)
1614         ad_close( &ad, adflags );  /* ad_close removes locks if any */
1615
1616     return err;
1617 }
1618
1619 /* ------------------------------------ */
1620 /* return a file id */
1621 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1622 {
1623     struct stat         *st;
1624     struct vol          *vol;
1625     struct dir          *dir;
1626     char                *upath;
1627     int                 len;
1628     cnid_t              did, id;
1629     u_short             vid;
1630     struct path         *s_path;
1631
1632     *rbuflen = 0;
1633
1634     ibuf += 2;
1635
1636     memcpy(&vid, ibuf, sizeof(vid));
1637     ibuf += sizeof(vid);
1638
1639     if (NULL == ( vol = getvolbyvid( vid )) ) {
1640         return( AFPERR_PARAM);
1641     }
1642
1643     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1644         return AFPERR_NOOP;
1645     }
1646
1647     if (vol->v_flags & AFPVOL_RO)
1648         return AFPERR_VLOCK;
1649
1650     memcpy(&did, ibuf, sizeof( did ));
1651     ibuf += sizeof(did);
1652
1653     if (NULL == ( dir = dirlookup( vol, did )) ) {
1654         return afp_errno; /* was AFPERR_PARAM */
1655     }
1656
1657     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1658         return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1659     }
1660
1661     if ( path_isadir(s_path) ) {
1662         return( AFPERR_BADTYPE );
1663     }
1664
1665     upath = s_path->u_name;
1666     switch (s_path->st_errno) {
1667         case 0:
1668              break; /* success */
1669         case EPERM:
1670         case EACCES:
1671             return AFPERR_ACCESS;
1672         case ENOENT:
1673             return AFPERR_NOOBJ;
1674         default:
1675             return AFPERR_PARAM;
1676     }
1677     st = &s_path->st;
1678     if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1679         memcpy(rbuf, &id, sizeof(id));
1680         *rbuflen = sizeof(id);
1681         return AFPERR_EXISTID;
1682     }
1683
1684     if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1685         memcpy(rbuf, &id, sizeof(id));
1686         *rbuflen = sizeof(id);
1687         return AFP_OK;
1688     }
1689
1690     return afp_errno;
1691 }
1692
1693 /* ------------------------------- */
1694 struct reenum {
1695     struct vol *vol;
1696     cnid_t     did;
1697 };
1698
1699 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1700 {
1701     struct path   path;
1702     struct reenum *param = data;
1703     struct vol    *vol = param->vol;  
1704     cnid_t        did  = param->did;
1705     cnid_t        aint;
1706     
1707     if (ostat(de->d_name, &path.st, vol_syml_opt(vol)) < 0)
1708         return 0;
1709     
1710     /* update or add to cnid */
1711     aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1712
1713     return 0;
1714 }
1715
1716 /* --------------------
1717  * Ok the db is out of synch with the dir.
1718  * but if it's a deleted file we don't want to do it again and again.
1719 */
1720 static int
1721 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1722 {
1723     int             ret;
1724     struct reenum   data;
1725     struct stat     st;
1726     
1727     if (vol->v_cdb == NULL) {
1728         return -1;
1729     }
1730     
1731     /* FIXME use of_statdir ? */
1732     if (ostat(name, &st, vol_syml_opt(vol))) {
1733         return -1;
1734     }
1735
1736     if (dirreenumerate(dir, &st)) {
1737         /* we already did it once and the dir haven't been modified */
1738         return dir->d_offcnt;
1739     }
1740     
1741     data.vol = vol;
1742     data.did = dir->d_did;
1743     if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1744         setdiroffcnt(curdir, &st,  ret);
1745         dir->d_flags |= DIRF_CNID;
1746     }
1747
1748     return ret;
1749 }
1750
1751 /* ------------------------------
1752    resolve a file id */
1753 int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1754 {
1755     struct vol          *vol;
1756     struct dir          *dir;
1757     char                *upath;
1758     struct path         path;
1759     int                 err, retry=0;
1760     size_t              buflen;
1761     cnid_t              id, cnid;
1762     uint16_t            vid, bitmap;
1763
1764     static char buffer[12 + MAXPATHLEN + 1];
1765     int len = 12 + MAXPATHLEN + 1;
1766
1767     *rbuflen = 0;
1768     ibuf += 2;
1769
1770     memcpy(&vid, ibuf, sizeof(vid));
1771     ibuf += sizeof(vid);
1772
1773     if (NULL == ( vol = getvolbyvid( vid )) ) {
1774         return( AFPERR_PARAM);
1775     }
1776
1777     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1778         return AFPERR_NOOP;
1779     }
1780
1781     memcpy(&id, ibuf, sizeof( id ));
1782     ibuf += sizeof(id);
1783     cnid = id;
1784     
1785     if (!id) {
1786         /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1787         return AFPERR_NOID;
1788     }
1789 retry:
1790     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1791         return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1792     }
1793
1794     if (NULL == ( dir = dirlookup( vol, id )) ) {
1795         return AFPERR_NOID; /* idem AFPERR_PARAM */
1796     }
1797     if (movecwd(vol, dir) < 0) {
1798         switch (errno) {
1799         case EACCES:
1800         case EPERM:
1801             return AFPERR_ACCESS;
1802         case ENOENT:
1803             return AFPERR_NOID;
1804         default:
1805             return AFPERR_PARAM;
1806         }
1807     }
1808
1809     memset(&path, 0, sizeof(path));
1810     path.u_name = upath;
1811     if (of_stat(vol, &path) < 0 ) {
1812 #ifdef ESTALE
1813         /* with nfs and our working directory is deleted */
1814         if (errno == ESTALE) {
1815             errno = ENOENT;
1816         }
1817 #endif  
1818         if ( errno == ENOENT && !retry) {
1819             /* cnid db is out of sync, reenumerate the directory and update ids */
1820             reenumerate_id(vol, ".", dir);
1821             id = cnid;
1822             retry = 1;
1823             goto retry;
1824         }
1825         switch (errno) {
1826         case EACCES:
1827         case EPERM:
1828             return AFPERR_ACCESS;
1829         case ENOENT:
1830             return AFPERR_NOID;
1831         default:
1832             return AFPERR_PARAM;
1833         }
1834     }
1835
1836     /* directories are bad */
1837     if (S_ISDIR(path.st.st_mode)) {
1838         /* OS9 and OSX don't return the same error code  */
1839         return (obj->afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1840     }
1841
1842     memcpy(&bitmap, ibuf, sizeof(bitmap));
1843     bitmap = ntohs( bitmap );
1844     if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding(obj)))) {
1845         return AFPERR_NOID;
1846     }
1847     path.id = cnid;
1848     if (AFP_OK != (err = getfilparams(obj, vol, bitmap, &path , curdir, 
1849                                       rbuf + sizeof(bitmap), &buflen, 0))) {
1850         return err;
1851     }
1852     *rbuflen = buflen + sizeof(bitmap);
1853     memcpy(rbuf, ibuf, sizeof(bitmap));
1854
1855     return AFP_OK;
1856 }
1857
1858 /* ------------------------------ */
1859 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1860 {
1861     struct stat         st;
1862     struct vol          *vol;
1863     struct dir          *dir;
1864     char                *upath;
1865     int                 err;
1866     cnid_t              id;
1867     cnid_t              fileid;
1868     u_short             vid;
1869     static char buffer[12 + MAXPATHLEN + 1];
1870     int len = 12 + MAXPATHLEN + 1;
1871
1872     *rbuflen = 0;
1873     ibuf += 2;
1874
1875     memcpy(&vid, ibuf, sizeof(vid));
1876     ibuf += sizeof(vid);
1877
1878     if (NULL == ( vol = getvolbyvid( vid )) ) {
1879         return( AFPERR_PARAM);
1880     }
1881
1882     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1883         return AFPERR_NOOP;
1884     }
1885
1886     if (vol->v_flags & AFPVOL_RO)
1887         return AFPERR_VLOCK;
1888
1889     memcpy(&id, ibuf, sizeof( id ));
1890     ibuf += sizeof(id);
1891     fileid = id;
1892
1893     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1894         return AFPERR_NOID;
1895     }
1896
1897     if (NULL == ( dir = dirlookup( vol, id )) ) {
1898         if (afp_errno == AFPERR_NOOBJ) {
1899             err = AFPERR_NOOBJ;
1900             goto delete;
1901         }
1902         return( AFPERR_PARAM );
1903     }
1904
1905     err = AFP_OK;
1906     if ((movecwd(vol, dir) < 0) || (ostat(upath, &st, vol_syml_opt(vol)) < 0)) {
1907         switch (errno) {
1908         case EACCES:
1909         case EPERM:
1910             return AFPERR_ACCESS;
1911 #ifdef ESTALE
1912         case ESTALE:
1913 #endif  
1914         case ENOENT:
1915             /* still try to delete the id */
1916             err = AFPERR_NOOBJ;
1917             break;
1918         default:
1919             return AFPERR_PARAM;
1920         }
1921     }
1922     else if (S_ISDIR(st.st_mode)) /* directories are bad */
1923         return AFPERR_BADTYPE;
1924
1925 delete:
1926     if (cnid_delete(vol->v_cdb, fileid)) {
1927         switch (errno) {
1928         case EROFS:
1929             return AFPERR_VLOCK;
1930         case EPERM:
1931         case EACCES:
1932             return AFPERR_ACCESS;
1933         default:
1934             return AFPERR_PARAM;
1935         }
1936     }
1937
1938     return err;
1939 }
1940
1941 /* ------------------------------ */
1942 static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
1943 {
1944     int             ret;
1945
1946     if (path->st_errno) {
1947         switch (path->st_errno) {
1948         case ENOENT:
1949             afp_errno = AFPERR_NOID;
1950             break;
1951         case EPERM:
1952         case EACCES:
1953             afp_errno = AFPERR_ACCESS;
1954             break;
1955         default:
1956             afp_errno = AFPERR_PARAM;
1957             break;
1958         }
1959         return NULL;
1960     }
1961     /* we use file_access both for legacy Mac perm and
1962      * for unix privilege, rename will take care of folder perms
1963     */
1964     if (file_access(obj, vol, path, OPENACC_WR ) < 0) {
1965         afp_errno = AFPERR_ACCESS;
1966         return NULL;
1967     }
1968     
1969     if ((*of = of_findname(vol, path))) {
1970         /* reuse struct adouble so it won't break locks */
1971         adp = (*of)->of_ad;
1972     }
1973     else {
1974         ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
1975         /* META and HF */
1976         if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1977             /* from AFP spec.
1978              * The user must have the Read & Write privilege for both files in order to use this command.
1979              */
1980             ad_close(adp, ADFLAGS_HF);
1981             afp_errno = AFPERR_ACCESS;
1982             return NULL;
1983         }
1984     }
1985     return adp;
1986 }
1987
1988 #define APPLETEMP ".AppleTempXXXXXX"
1989
1990 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1991 {
1992     struct stat         srcst, destst;
1993     struct vol          *vol;
1994     struct dir          *dir, *sdir;
1995     char                *spath, temp[17], *p;
1996     char                *supath, *upath;
1997     struct path         *path;
1998     int                 err;
1999     struct adouble      ads;
2000     struct adouble      add;
2001     struct adouble      *adsp = NULL;
2002     struct adouble      *addp = NULL;
2003     struct ofork        *s_of = NULL;
2004     struct ofork        *d_of = NULL;
2005     int                 crossdev;
2006     
2007     int                 slen, dlen;
2008     uint32_t            sid, did;
2009     uint16_t            vid;
2010
2011     *rbuflen = 0;
2012     ibuf += 2;
2013
2014     memcpy(&vid, ibuf, sizeof(vid));
2015     ibuf += sizeof(vid);
2016
2017     if (NULL == ( vol = getvolbyvid( vid )) ) {
2018         return( AFPERR_PARAM);
2019     }
2020
2021     if ((vol->v_flags & AFPVOL_RO))
2022         return AFPERR_VLOCK;
2023
2024     /* source and destination dids */
2025     memcpy(&sid, ibuf, sizeof(sid));
2026     ibuf += sizeof(sid);
2027     memcpy(&did, ibuf, sizeof(did));
2028     ibuf += sizeof(did);
2029
2030     /* source file */
2031     if (NULL == (dir = dirlookup( vol, sid )) ) {
2032         return afp_errno; /* was AFPERR_PARAM */
2033     }
2034
2035     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2036         return get_afp_errno(AFPERR_NOOBJ); 
2037     }
2038
2039     if ( path_isadir(path) ) {
2040         return AFPERR_BADTYPE;   /* it's a dir */
2041     }
2042
2043     /* save some stuff */
2044     srcst = path->st;
2045     sdir = curdir;
2046     spath = obj->oldtmp;
2047     supath = obj->newtmp;
2048     strcpy(spath, path->m_name);
2049     strcpy(supath, path->u_name); /* this is for the cnid changing */
2050     p = absupath( vol, sdir, supath);
2051     if (!p) {
2052         /* pathname too long */
2053         return AFPERR_PARAM ;
2054     }
2055     
2056     ad_init(&ads, vol);
2057     if (!(adsp = find_adouble(obj, vol, path, &s_of, &ads))) {
2058         return afp_errno;
2059     }
2060
2061     /* ***** from here we may have resource fork open **** */
2062     
2063     /* look for the source cnid. if it doesn't exist, don't worry about
2064      * it. */
2065     sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2066
2067     if (NULL == ( dir = dirlookup( vol, did )) ) {
2068         err = afp_errno; /* was AFPERR_PARAM */
2069         goto err_exchangefile;
2070     }
2071
2072     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2073         err = get_afp_errno(AFPERR_NOOBJ); 
2074         goto err_exchangefile;
2075     }
2076
2077     if ( path_isadir(path) ) {
2078         err = AFPERR_BADTYPE;
2079         goto err_exchangefile;
2080     }
2081
2082     /* FPExchangeFiles is the only call that can return the SameObj
2083      * error */
2084     if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2085         err = AFPERR_SAMEOBJ;
2086         goto err_exchangefile;
2087     }
2088
2089     ad_init(&add, vol);
2090     if (!(addp = find_adouble(obj, vol, path, &d_of, &add))) {
2091         err = afp_errno;
2092         goto err_exchangefile;
2093     }
2094     destst = path->st;
2095
2096     /* they are not on the same device and at least one is open
2097      * FIXME broken for for crossdev and adouble v2
2098      * return an error 
2099     */
2100     crossdev = (srcst.st_dev != destst.st_dev);
2101     if (/* (d_of || s_of)  && */ crossdev) {
2102         err = AFPERR_MISC;
2103         goto err_exchangefile;
2104     }
2105
2106     /* look for destination id. */
2107     upath = path->u_name;
2108     did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2109
2110     /* construct a temp name.
2111      * NOTE: the temp file will be in the dest file's directory. it
2112      * will also be inaccessible from AFP. */
2113     memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2114     int fd;
2115     if ((fd = mkstemp(temp)) == -1) {
2116         err = AFPERR_MISC;
2117         goto err_exchangefile;
2118     }
2119     close(fd);
2120
2121     if (crossdev) {
2122         /* FIXME we need to close fork for copy, both s_of and d_of are null */
2123        ad_close(adsp, ADFLAGS_HF);
2124        ad_close(addp, ADFLAGS_HF);
2125     }
2126
2127     /* now, quickly rename the file. we error if we can't. */
2128     if ((err = renamefile(vol, curdir, -1, p, temp, temp, adsp)) != AFP_OK)
2129         goto err_exchangefile;
2130     of_rename(vol, s_of, sdir, spath, curdir, temp);
2131
2132     /* rename destination to source */
2133     if ((err = renamefile(vol, curdir, -1, upath, p, spath, addp)) != AFP_OK)
2134         goto err_src_to_tmp;
2135     of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2136
2137     /* rename temp to destination */
2138     if ((err = renamefile(vol, curdir, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2139         goto err_dest_to_src;
2140     of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2141
2142     /* id's need switching. src -> dest and dest -> src. 
2143      * we need to re-stat() if it was a cross device copy.
2144     */
2145     if (sid)
2146         cnid_delete(vol->v_cdb, sid);
2147     if (did)
2148         cnid_delete(vol->v_cdb, did);
2149
2150     if ((did && ( (crossdev && ostat(upath, &srcst, vol_syml_opt(vol)) < 0) || 
2151                 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2152        ||
2153         (sid && ( (crossdev && ostat(p, &destst, vol_syml_opt(vol)) < 0) ||
2154                 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2155     ) {
2156         switch (errno) {
2157         case EPERM:
2158         case EACCES:
2159             err = AFPERR_ACCESS;
2160             break;
2161         default:
2162             err = AFPERR_PARAM;
2163         }
2164         goto err_temp_to_dest;
2165     }
2166     
2167     /* here we need to reopen if crossdev */
2168     if (sid && ad_setid(addp, destst.st_dev, destst.st_ino,  sid, sdir->d_did, vol->v_stamp)) 
2169     {
2170        ad_flush( addp );
2171     }
2172         
2173     if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino,  did, curdir->d_did, vol->v_stamp)) 
2174     {
2175        ad_flush( adsp );
2176     }
2177
2178     /* change perms, src gets dest perm and vice versa */
2179
2180     become_root();
2181
2182     /*
2183      * we need to exchange ACL entries as well
2184      */
2185     /* exchange_acls(vol, p, upath); */
2186
2187     path->st = srcst;
2188     path->st_valid = 1;
2189     path->st_errno = 0;
2190     path->m_name = NULL;
2191     path->u_name = upath;
2192
2193     setfilunixmode(vol, path, destst.st_mode);
2194     setfilowner(vol, destst.st_uid, destst.st_gid, path);
2195
2196     path->st = destst;
2197     path->st_valid = 1;
2198     path->st_errno = 0;
2199     path->u_name = p;
2200
2201     setfilunixmode(vol, path, srcst.st_mode);
2202     setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2203
2204     unbecome_root();
2205
2206     err = AFP_OK;
2207     goto err_exchangefile;
2208
2209     /* all this stuff is so that we can unwind a failed operation
2210      * properly. */
2211 err_temp_to_dest:
2212     /* rename dest to temp */
2213     renamefile(vol, curdir, -1, upath, temp, temp, adsp);
2214     of_rename(vol, s_of, curdir, upath, curdir, temp);
2215
2216 err_dest_to_src:
2217     /* rename source back to dest */
2218     renamefile(vol, curdir, -1, p, upath, path->m_name, addp);
2219     of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2220
2221 err_src_to_tmp:
2222     /* rename temp back to source */
2223     renamefile(vol, curdir, -1, temp, p, spath, adsp);
2224     of_rename(vol, s_of, curdir, temp, sdir, spath);
2225
2226 err_exchangefile:
2227     if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2228        ad_close(adsp, ADFLAGS_HF);
2229     }
2230     if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2231        ad_close(addp, ADFLAGS_HF);
2232     }
2233
2234     struct dir *cached;
2235     if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2236         (void)dir_remove(vol, cached);
2237     if ((cached = dircache_search_by_did(vol, did)) != NULL)
2238         (void)dir_remove(vol, cached);
2239
2240     return err;
2241 }