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