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