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