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