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