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