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