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