]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/file.c
Fixed handling of Unix-prohibited characters in file names for dropkludge.
[netatalk.git] / etc / afpd / file.c
1 /*
2  * Copyright (c) 1990,1993 Regents of The University of Michigan.
3  * All Rights Reserved.  See COPYRIGHT.
4  */
5
6 #ifdef HAVE_CONFIG_H
7 #include "config.h"
8 #endif
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <unistd.h>
13 #include <string.h>
14 #include <utime.h>
15 #include <fcntl.h>
16 #include <dirent.h>
17 #include <sys/mman.h>
18 #include <errno.h>
19
20 #include <sys/syslog.h>
21 #include <sys/types.h>
22 #include <sys/time.h>
23 #include <sys/param.h>
24 #include <sys/stat.h>
25
26 #include <netatalk/endian.h>
27 #include <atalk/adouble.h>
28 #include <atalk/afp.h>
29 #include <atalk/util.h>
30 #include <atalk/cnid.h>
31
32 #include "directory.h"
33 #include "desktop.h"
34 #include "volume.h"
35 #include "fork.h"
36 #include "file.h"
37 #include "filedir.h"
38 #include "globals.h"
39
40 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
41  * field         bytes        subfield    bytes
42  * 
43  * files:
44  * ioFlFndrInfo  16      ->       type    4  type field
45  *                             creator    4  creator field
46  *                               flags    2  finder flags: 
47  *                                           alias, bundle, etc.
48  *                            location    4  location in window
49  *                              folder    2  window that contains file
50  * 
51  * ioFlXFndrInfo 16      ->     iconID    2  icon id
52  *                              unused    6  reserved 
53  *                              script    1  script system
54  *                              xflags    1  reserved
55  *                           commentID    2  comment id
56  *                           putawayID    4  home directory id
57  */
58
59 const u_char ufinderi[] = {
60     'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X',
61     0, 0, 0, 0, 0, 0, 0, 0, 
62     0, 0, 0, 0, 0, 0, 0, 0,
63     0, 0, 0, 0, 0, 0, 0, 0
64 };
65
66 int getfilparams(vol, bitmap, path, dir, st, buf, buflen )
67     struct vol  *vol;
68     u_int16_t   bitmap;
69     char        *path;
70     struct dir  *dir;
71     struct stat *st;
72     char        *buf;
73     int         *buflen;
74 {
75     struct stat         hst, lst, *lstp;
76     struct adouble      ad, *adp;
77     struct ofork        *of;
78     struct extmap       *em;
79     char                *data, *nameoff = NULL, *upath;
80     int                 bit = 0, isad = 1, aint;
81     u_int16_t           ashort;
82     u_char              achar, fdType[4];
83
84     upath = mtoupath(vol, path);
85     if ((of = of_findname(vol, curdir, path))) {
86       adp = of->of_ad;
87     } else {
88       memset(&ad, 0, sizeof(ad));
89       adp = &ad;
90     }
91
92     if ( ad_open( upath, ADFLAGS_HF, O_RDONLY, 0, adp) < 0 ) {
93         isad = 0;
94     } else if ( fstat( ad_hfileno( adp ), &hst ) < 0 ) {
95             syslog( LOG_ERR, "getfilparams fstat: %m" );
96     }
97
98     data = buf;
99     while ( bitmap != 0 ) {
100         while (( bitmap & 1 ) == 0 ) {
101             bitmap = bitmap>>1;
102             bit++;
103         }
104
105         switch ( bit ) {
106         case FILPBIT_ATTR :
107             if ( isad ) {
108                 ad_getattr(adp, &ashort);
109             } else if (*upath == '.') {
110                 ashort = htons(ATTRBIT_INVISIBLE);
111             } else
112                 ashort = 0;
113             memcpy(data, &ashort, sizeof( ashort ));
114             data += sizeof( u_short );
115             break;
116
117         case FILPBIT_PDID :
118             memcpy(data, &dir->d_did, sizeof( int ));
119             data += sizeof( int );
120             break;
121
122         case FILPBIT_CDATE :
123             if (!isad || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
124                 aint = AD_DATE_FROM_UNIX(st->st_mtime);
125             memcpy(data, &aint, sizeof( aint ));
126             data += sizeof( aint );
127             break;
128
129         case FILPBIT_MDATE :
130             if ( isad && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
131                 if ((st->st_mtime > AD_DATE_TO_UNIX(aint)) && 
132                     (hst.st_mtime < st->st_mtime)) {
133                     aint = AD_DATE_FROM_UNIX(st->st_mtime);
134                 }
135             } else {
136                 aint = AD_DATE_FROM_UNIX(st->st_mtime);
137             }
138             memcpy(data, &aint, sizeof( int ));
139             data += sizeof( int );
140             break;
141
142         case FILPBIT_BDATE :
143             if (!isad || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
144                 aint = AD_DATE_START;
145             memcpy(data, &aint, sizeof( int ));
146             data += sizeof( int );
147             break;
148
149         case FILPBIT_FINFO :
150             if (isad) 
151               memcpy(data, ad_entry(adp, ADEID_FINDERI), 32);
152             else {
153               memcpy(data, ufinderi, 32);
154               if (*upath == '.') { /* make it invisible */
155                 ashort = htons(FINDERINFO_INVISIBLE);
156                 memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
157               }
158             } 
159
160             if ((!isad || (memcmp(ad_entry(adp, ADEID_FINDERI),
161                                   ufinderi, 8 ) == 0)) &&
162                 (em = getextmap( path ))) {
163               memcpy(data, em->em_type, sizeof( em->em_type ));
164               memcpy(data + 4, em->em_creator, sizeof(em->em_creator));
165             }
166             data += 32;
167             break;
168
169         case FILPBIT_LNAME :
170             nameoff = data;
171             data += sizeof( u_int16_t );
172             break;
173
174         case FILPBIT_SNAME :
175             memset(data, 0, sizeof(u_int16_t));
176             data += sizeof( u_int16_t );
177             break;
178
179         case FILPBIT_FNUM :
180 #if AD_VERSION > AD_VERSION1
181           /* use the CNID database if we're using AD v2 */
182             if (isad)
183               memcpy(&aint, ad_entry(adp, ADEID_DID), sizeof(aint));
184             else
185               aint = 0;
186
187             if (!(aint = cnid_add(vol->v_db, st, dir->d_did, upath, 
188                                   strlen(upath), aint))) {
189 #endif
190             /*
191              * What a fucking mess.  First thing:  DID and FNUMs are
192              * in the same space for purposes of enumerate (and several
193              * other wierd places).  While we consider this Apple's bug,
194              * this is the work-around:  In order to maintain constant and
195              * unique DIDs and FNUMs, we monotonically generate the DIDs
196              * during the session, and derive the FNUMs from the filesystem.
197              * Since the DIDs are small, we insure that the FNUMs are fairly
198              * large by setting thier high bits to the device number.
199              *
200              * AFS already does something very similar to this for the
201              * inode number, so we don't repeat the procedure.
202              *
203              * new algorithm:
204              * due to complaints over did's being non-persistent,
205              * here's the current hack to provide semi-persistent
206              * did's: 
207              *      1) we reserve the first bit for file ids.
208              *      2) the next 7 bits are for the device.
209              *      3) the remaining 24 bits are for the inode.
210              *
211              * both the inode and device information are actually hashes
212              * that are then truncated to the requisite bit length.
213              *
214              * it should be okay to use lstat to deal with symlinks.
215              */
216               lstp = (lstat(upath, &lst) < 0) ? st : &lst;
217               aint = htonl(CNID(lstp, 1));
218 #if AD_VERSION > AD_VERSION1
219             }
220 #endif
221             memcpy(data, &aint, sizeof( aint ));
222             data += sizeof( aint );
223             break;
224
225         case FILPBIT_DFLEN :
226             aint = htonl( st->st_size );
227             memcpy(data, &aint, sizeof( aint ));
228             data += sizeof( aint );
229             break;
230
231         case FILPBIT_RFLEN :
232             if ( isad ) {
233                 aint = htonl( ad_getentrylen( adp, ADEID_RFORK ));
234             } else {
235                 aint = 0;
236             }
237             memcpy(data, &aint, sizeof( aint ));
238             data += sizeof( aint );
239             break;
240
241             /* Current client needs ProDOS info block for this file.
242                Use simple heuristic and let the Mac "type" string tell
243                us what the PD file code should be.  Everything gets a
244                subtype of 0x0000 unless the original value was hashed
245                to "pXYZ" when we created it.  See IA, Ver 2.
246                <shirsch@ibm.net> */
247         case FILPBIT_PDINFO :
248             if ( isad ) {
249               memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
250               
251               if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
252                 achar = '\x04';
253                 ashort = 0x0000;
254               }
255               else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
256                 achar = '\xff';
257                 ashort = 0x0000;
258               }
259               else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
260                 achar = '\xb3';
261                 ashort = 0x0000;
262               }
263               else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
264                 achar = '\x00';
265                 ashort = 0x0000;
266               }
267               else if ( fdType[0] == 'p' ) {
268                 achar = fdType[1];
269                 ashort = (fdType[2] * 256) + fdType[3];
270               } 
271               else {
272                 achar = '\x00';
273                 ashort = 0x0000;
274               }
275             } 
276             else {
277               achar = '\x00';
278               ashort = 0x0000;
279             }
280             
281             *data++ = achar;
282             *data++ = 0;
283             memcpy(data, &ashort, sizeof( ashort ));
284             data += sizeof( ashort );
285             memset(data, 0, sizeof( ashort ));
286             data += sizeof( ashort );
287             break;
288
289         default :
290             if ( isad ) {
291                 ad_close( adp, ADFLAGS_HF );
292             }
293             return( AFPERR_BITMAP );
294         }
295         bitmap = bitmap>>1;
296         bit++;
297     }
298     if ( nameoff ) {
299         ashort = htons( data - buf );
300         memcpy(nameoff, &ashort, sizeof( ashort ));
301         if ((aint = strlen( path )) > MACFILELEN)
302           aint = MACFILELEN;
303         *data++ = aint;
304         memcpy(data, path, aint );
305         data += aint;
306     }
307     if ( isad ) {
308         ad_close( adp, ADFLAGS_HF );
309     }
310     *buflen = data - buf;
311     return( AFP_OK );
312 }
313
314 int afp_createfile(obj, ibuf, ibuflen, rbuf, rbuflen )
315     AFPObj      *obj;
316     char        *ibuf, *rbuf;
317     int         ibuflen, *rbuflen;
318 {
319     struct stat         st;
320 #ifdef DROPKLUDGE
321     struct stat         sb;
322     char                adpath[50];
323     int                 uid;
324 #endif DROPKLUDGE
325     struct adouble      ad, *adp;
326     struct vol          *vol;
327     struct dir          *dir;
328     struct ofork        *of;
329     char                *path, *upath;
330     int                 creatf, did, openf;
331     u_int16_t           vid;
332
333     *rbuflen = 0;
334     ibuf++;
335     creatf = (unsigned char) *ibuf++;
336
337     memcpy(&vid, ibuf, sizeof( vid ));
338     ibuf += sizeof( vid );
339
340     if (( vol = getvolbyvid( vid )) == NULL ) {
341         return( AFPERR_PARAM );
342     }
343
344     if (vol->v_flags & AFPVOL_RO)
345         return AFPERR_VLOCK;
346
347     memcpy(&did, ibuf, sizeof( did));
348     ibuf += sizeof( did );
349
350     if (( dir = dirsearch( vol, did )) == NULL ) {
351         return( AFPERR_NOOBJ );
352     }
353
354     if (( path = cname( vol, dir, &ibuf )) == NULL ) {
355         return( AFPERR_NOOBJ );
356     }
357
358     if ((vol->v_flags & AFPVOL_MSWINDOWS) && 
359         strpbrk(path, MSWINDOWS_BADCHARS))
360         return AFPERR_PARAM;
361
362     upath = mtoupath(vol, path);
363
364     if ((vol->v_flags & AFPVOL_NOHEX) && strchr(upath, '/'))
365       return AFPERR_PARAM;
366
367     if (!validupath(vol, upath))
368       return AFPERR_EXIST;
369
370     if ((of = of_findname(vol, curdir, path))) {
371       adp = of->of_ad;
372     } else {
373       memset(&ad, 0, sizeof(ad));
374       adp = &ad;
375     }
376     if ( creatf) {
377         /* on a hard create, fail if file exists and is open */
378         if ((stat(upath, &st) == 0) && of)
379           return AFPERR_BUSY;
380         openf = O_RDWR|O_CREAT|O_TRUNC;
381     } else {
382         openf = O_RDWR|O_CREAT|O_EXCL;
383     }
384
385     if ( ad_open( upath, vol_noadouble(vol)|ADFLAGS_DF|ADFLAGS_HF,
386                   openf, 0666, adp) < 0 ) {
387       switch ( errno ) {
388         case EEXIST :
389             return( AFPERR_EXIST );
390         case EACCES :
391             return( AFPERR_ACCESS );
392         case ENOENT:
393             /* on noadouble volumes, just creating the data fork is ok */
394             if (vol_noadouble(vol) && (stat(upath, &st) == 0))
395               goto createfile_done;
396             /* fallthrough */
397         default :
398             return( AFPERR_PARAM );
399         }
400     }
401
402     ad_setentrylen( adp, ADEID_NAME, strlen( path ));
403     memcpy(ad_entry( adp, ADEID_NAME ), path, 
404            ad_getentrylen( adp, ADEID_NAME ));
405     ad_flush( adp, ADFLAGS_DF|ADFLAGS_HF );
406     ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
407
408 createfile_done:
409
410 #ifdef DROPKLUDGE
411
412 /* The below code changes the way file ownership is determined in the name of
413 fixing dropboxes.  It has known security problem.  See the netatalk FAQ for
414 more information */
415     if (stat(".", &sb) == -1) 
416       syslog (LOG_ERR, "Error checking directory %s: %m", dir->d_name);
417     else {
418       uid=geteuid();
419       if ( uid != sb.st_uid )
420       {
421         strcpy (adpath, "./.AppleDouble/");
422         strcat (adpath, mtoupath(vol, path));
423         seteuid(0); /* Become root to change the owner of the file */
424         if (chown(mtoupath(vol, path), sb.st_uid, sb.st_gid) < 0) 
425         {
426           syslog (LOG_ERR, "Error changing owner/gid: %m");
427           return (-1);
428         }
429         /* In order to write information to the file, the Mac client needs
430         to be able to read from it too, so read bits have to be turned on.
431         Directory permissions remain unchanged */
432         stat(upath, &st);
433         if (chmod(mtoupath(vol,path),(st.st_mode&0x0FFFF)| S_IRGRP| S_IROTH) < 0)
434         {
435           syslog (LOG_ERR, "Error adding file read permissions: %m");
436           return (-1);
437         }
438         else syslog (LOG_DEBUG, "Added S_IRGRP and S_IROTH: %m");
439         if (chown(adpath, sb.st_uid, sb.st_gid) < 0)
440         {
441           syslog (LOG_ERR, "Error changing AppleDouble owner/gid: %m");
442           return (-1);
443         }
444         if (chmod(adpath, (st.st_mode&0x0FFFF)| S_IRGRP| S_IROTH) < 0)
445         {
446           syslog (LOG_ERR, "Error adding AD file read permissions: %m");
447           return (-1);
448         }
449         else syslog (LOG_DEBUG, "Added S_IRGRP and S_IROTH to AD: %m");
450         syslog (LOG_DEBUG, "Changing afpd owner back to %d", uid);
451         seteuid(uid); /* Restore process ownership to normal */
452       }
453     }
454
455 #endif DROPKLUDGE
456
457     setvoltime(obj, vol );
458     return AFP_OK;
459 }
460
461 int afp_setfilparams(obj, ibuf, ibuflen, rbuf, rbuflen )
462     AFPObj      *obj;
463     char        *ibuf, *rbuf;
464     int         ibuflen, *rbuflen;
465 {
466     struct vol  *vol;
467     struct dir  *dir;
468     char        *path;
469     int         did, rc;
470     u_int16_t   vid, bitmap;
471
472     *rbuflen = 0;
473     ibuf += 2;
474
475     memcpy(&vid, ibuf, sizeof( vid ));
476     ibuf += sizeof( vid );
477     if (( vol = getvolbyvid( vid )) == NULL ) {
478         return( AFPERR_PARAM );
479     }
480
481     if (vol->v_flags & AFPVOL_RO)
482         return AFPERR_VLOCK;
483
484     memcpy(&did, ibuf, sizeof( did ));
485     ibuf += sizeof( did );
486     if (( dir = dirsearch( vol, did )) == NULL ) {
487         return( AFPERR_NOOBJ );
488     }
489
490     memcpy(&bitmap, ibuf, sizeof( bitmap ));
491     bitmap = ntohs( bitmap );
492     ibuf += sizeof( bitmap );
493
494     if (( path = cname( vol, dir, &ibuf )) == NULL ) {
495         return( AFPERR_NOOBJ );
496     }
497
498     if ((u_long)ibuf & 1 ) {
499         ibuf++;
500     }
501
502     if (( rc = setfilparams(vol, path, bitmap, ibuf )) == AFP_OK ) {
503         setvoltime(obj, vol );
504     }
505
506     return( rc );
507 }
508
509
510 int setfilparams(vol, path, bitmap, buf )
511     struct vol  *vol;
512     char        *path, *buf;
513     u_int16_t   bitmap;
514 {
515     struct adouble      ad, *adp;
516     struct ofork        *of;
517     struct extmap       *em;
518     int                 bit = 0, isad = 1, err = AFP_OK;
519     char                *upath;
520     u_char              achar, *fdType, xyy[4];
521     u_int16_t           ashort, bshort;
522     u_int32_t           aint;
523     struct utimbuf      ut;
524
525     upath = mtoupath(vol, path);
526     if ((of = of_findname(vol, curdir, path))) {
527       adp = of->of_ad;
528     } else {
529       memset(&ad, 0, sizeof(ad));
530       adp = &ad;
531     }
532     if (ad_open( upath, vol_noadouble(vol) | ADFLAGS_HF, 
533                  O_RDWR|O_CREAT, 0666, adp) < 0) {
534       /* for some things, we don't need an adouble header */
535       if (bitmap & ~(1<<FILPBIT_MDATE)) {
536         return vol_noadouble(vol) ? AFP_OK : AFPERR_ACCESS;
537       }
538       isad = 0;
539     } else if ((ad_getoflags( adp, ADFLAGS_HF ) & O_CREAT) ) {
540         ad_setentrylen( adp, ADEID_NAME, strlen( path ));
541         memcpy(ad_entry( adp, ADEID_NAME ), path, 
542                ad_getentrylen( adp, ADEID_NAME ));
543     }
544
545     while ( bitmap != 0 ) {
546         while (( bitmap & 1 ) == 0 ) {
547             bitmap = bitmap>>1;
548             bit++;
549         }
550
551         switch(  bit ) {
552         case FILPBIT_ATTR :
553             memcpy(&ashort, buf, sizeof( ashort ));
554             ad_getattr(adp, &bshort);
555             if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
556               bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
557             } else {
558               bshort &= ~ashort;
559             }
560             ad_setattr(adp, bshort);
561             buf += sizeof( ashort );
562             break;
563
564         case FILPBIT_CDATE :
565             memcpy(&aint, buf, sizeof(aint));
566             ad_setdate(adp, AD_DATE_CREATE, aint);
567             buf += sizeof( aint );
568             break;
569
570         case FILPBIT_MDATE :
571             memcpy(&aint, buf, sizeof( aint ));
572             if (isad)
573               ad_setdate(adp, AD_DATE_MODIFY, aint);
574             ut.actime = ut.modtime = AD_DATE_TO_UNIX(aint);
575             utime(upath, &ut);
576             buf += sizeof( aint );
577             break;
578
579         case FILPBIT_BDATE :
580             memcpy(&aint, buf, sizeof(aint));
581             ad_setdate(adp, AD_DATE_BACKUP, aint);
582             buf += sizeof( aint );
583             break;
584
585         case FILPBIT_FINFO :
586             if ((memcmp( ad_entry( adp, ADEID_FINDERI ), ufinderi, 8 ) == 0)
587                 && (em = getextmap( path )) && 
588                 (memcmp(buf, em->em_type, sizeof( em->em_type )) == 0) &&
589                 (memcmp(buf + 4, em->em_creator,
590                         sizeof( em->em_creator )) == 0)) {
591               memcpy(buf, ufinderi, 8 );
592             }
593             memcpy(ad_entry( adp, ADEID_FINDERI ), buf, 32 );
594             buf += 32;
595             break;
596
597             /* Client needs to set the ProDOS file info for this file.
598                Use defined strings for the simple cases, and convert
599                all else into pXYY per Inside Appletalk.  Always set
600                the creator as "pdos". <shirsch@ibm.net> */
601         case FILPBIT_PDINFO :
602             achar = *buf;
603             buf += 2;
604             memcpy(&ashort, buf, sizeof( ashort ));
605             ashort = ntohs( ashort );
606             buf += 2;
607  
608             switch ( (unsigned int) achar )
609               {
610               case 0x04 :
611                 fdType = ( u_char *) "TEXT";
612                 break;
613                 
614               case 0xff :
615                 fdType = ( u_char *) "PSYS";
616                 break;
617  
618               case 0xb3 :
619                 fdType = ( u_char *) "PS16";
620                 break;
621  
622               case 0x00 :
623                 fdType = ( u_char *) "BINA";
624                 break;
625  
626               default :
627                 xyy[0] = ( u_char ) 'p';
628                 xyy[1] = achar;
629                 xyy[2] = ( u_char ) ( ashort >> 8 ) & 0xff;
630                 xyy[3] = ( u_char ) ashort & 0xff;
631                 fdType = xyy;
632                 break;
633               }
634             
635             memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
636             memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
637             break;
638  
639
640         default :
641             err = AFPERR_BITMAP;
642             goto setfilparam_done;
643         }
644
645         bitmap = bitmap>>1;
646         bit++;
647     }
648
649 setfilparam_done:
650     if (isad) {
651       ad_flush( adp, ADFLAGS_HF );
652       ad_close( adp, ADFLAGS_HF );
653     }
654     return err;
655 }
656
657 /*
658  * renamefile and copyfile take the old and new unix pathnames
659  * and the new mac name.
660  * NOTE: if we have to copy a file instead of renaming it, locks
661  *       will break.
662  */
663 int renamefile(src, dst, newname, noadouble )
664     char        *src, *dst, *newname;
665     const int         noadouble;
666 {
667     struct adouble      ad;
668     char                adsrc[ MAXPATHLEN + 1];
669     int                 len, rc;
670
671     /*
672      * Note that this is only checking the existance of the data file,
673      * not the header file.  The thinking is that if the data file doesn't
674      * exist, but the header file does, the right thing to do is remove
675      * the data file silently.
676      */
677
678     /* existence check moved to afp_moveandrename */
679
680     if ( rename( src, dst ) < 0 ) {
681         switch ( errno ) {
682         case ENOENT :
683             return( AFPERR_NOOBJ );
684         case EPERM:
685         case EACCES :
686             return( AFPERR_ACCESS );
687         case EROFS:
688             return AFPERR_VLOCK;
689         case EXDEV :                    /* Cross device move -- try copy */
690             if (( rc = copyfile(src, dst, newname, noadouble )) != AFP_OK ) {
691                 deletefile( dst );
692                 return( rc );
693             }
694             return deletefile( src );
695         default :
696             return( AFPERR_PARAM );
697         }
698     }
699
700     strcpy( adsrc, ad_path( src, 0 ));
701     rc = 0;
702 rename_retry:
703     if (rename( adsrc, ad_path( dst, 0 )) < 0 ) {
704         struct stat st;
705
706         switch ( errno ) {
707         case ENOENT :
708           /* check for a source appledouble header. if it exists, make
709            * a dest appledouble directory and do the rename again. */
710           memset(&ad, 0, sizeof(ad));
711           if (rc || stat(adsrc, &st) ||
712               (ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad) < 0))
713             return AFP_OK;
714           rc++;
715           ad_close(&ad, ADFLAGS_HF);
716           goto rename_retry;
717         case EPERM:
718         case EACCES :
719             return( AFPERR_ACCESS );
720         case EROFS:
721             return AFPERR_VLOCK;
722         default :
723             return( AFPERR_PARAM );
724         }
725     }
726
727     memset(&ad, 0, sizeof(ad));
728     if ( ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, &ad) < 0 ) {
729         switch ( errno ) {
730         case ENOENT :
731             return( AFPERR_NOOBJ );
732         case EACCES :
733             return( AFPERR_ACCESS );
734         case EROFS:
735             return AFPERR_VLOCK;
736         default :
737             return( AFPERR_PARAM );
738         }
739     }
740
741     len = strlen( newname );
742     ad_setentrylen( &ad, ADEID_NAME, len );
743     memcpy(ad_entry( &ad, ADEID_NAME ), newname, len );
744     ad_flush( &ad, ADFLAGS_HF );
745     ad_close( &ad, ADFLAGS_HF );
746
747     return( AFP_OK );
748 }
749
750 int afp_copyfile(obj, ibuf, ibuflen, rbuf, rbuflen )
751     AFPObj      *obj;
752     char        *ibuf, *rbuf;
753     int         ibuflen, *rbuflen;
754 {
755     struct vol  *vol;
756     struct dir  *dir;
757     char        *newname, *path, *p;
758     u_int32_t   sdid, ddid;
759     int         plen, err;
760     u_int16_t   svid, dvid;
761
762     *rbuflen = 0;
763     ibuf += 2;
764
765     memcpy(&svid, ibuf, sizeof( svid ));
766     ibuf += sizeof( svid );
767     if (( vol = getvolbyvid( svid )) == NULL ) {
768         return( AFPERR_PARAM );
769     }
770
771     memcpy(&sdid, ibuf, sizeof( sdid ));
772     ibuf += sizeof( sdid );
773     if (( dir = dirsearch( vol, sdid )) == NULL ) {
774         return( AFPERR_PARAM );
775     }
776
777     memcpy(&dvid, ibuf, sizeof( dvid ));
778     ibuf += sizeof( dvid );
779     memcpy(&ddid, ibuf, sizeof( ddid ));
780     ibuf += sizeof( ddid );
781
782     if (( path = cname( vol, dir, &ibuf )) == NULL ) {
783         return( AFPERR_NOOBJ );
784     }
785     if ( *path == '\0' ) {
786         return( AFPERR_BADTYPE );
787     }
788
789     /* don't allow copies when the file is open.
790      * XXX: the spec only calls for read/deny write access.
791      *      however, copyfile doesn't have any of that info,
792      *      and locks need to stay coherent. as a result,
793      *      we just balk if the file is opened already. */
794     if (of_findname(vol, curdir, path))
795       return AFPERR_DENYCONF;
796     
797     newname = obj->newtmp;
798     strcpy( newname, path );
799
800     p = ctoupath( vol, curdir, newname );
801
802     if (( vol = getvolbyvid( dvid )) == NULL ) {
803         return( AFPERR_PARAM );
804     }
805
806     if (vol->v_flags & AFPVOL_RO)
807         return AFPERR_VLOCK;
808
809     if (( dir = dirsearch( vol, ddid )) == NULL ) {
810         return( AFPERR_PARAM );
811     }
812
813     if (( path = cname( vol, dir, &ibuf )) == NULL ) {
814         return( AFPERR_NOOBJ );
815     }
816     if ( *path != '\0' ) {
817         return( AFPERR_BADTYPE );
818     }
819
820     /* one of the handful of places that knows about the path type */
821     if ( *ibuf++ != 2 ) {
822         return( AFPERR_PARAM );
823     }
824     if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
825         strncpy( newname, ibuf, plen );
826         newname[ plen ] = '\0';
827     }
828
829     if ( (err = copyfile(p, mtoupath(vol, newname ), newname, 
830                   vol_noadouble(vol))) < 0 ) {
831         return err;
832     }
833
834     setvoltime(obj, vol );
835     return( AFP_OK );
836 }
837
838
839 static __inline__ int copy_all(const int dfd, const void *buf,
840                                size_t buflen)
841 {
842   ssize_t cc;
843
844   while (buflen > 0) {
845     if ((cc = write(dfd, buf, buflen)) < 0) {
846       switch (errno) {
847       case EINTR:
848         continue;
849       case EDQUOT:
850       case EFBIG:
851       case ENOSPC:
852         return AFPERR_DFULL;
853       case EROFS:
854         return AFPERR_VLOCK;
855       default:
856         return AFPERR_PARAM;
857       }
858     }
859     buflen -= cc;
860   }
861
862   return 0;
863 }
864
865 /* XXX: this needs to use ad_open and ad_lock. so, we need to
866  * pass in vol and path */
867 int copyfile(src, dst, newname, noadouble )
868     char        *src, *dst, *newname;
869     const int   noadouble;
870 {
871     struct adouble      ad;
872     struct stat         st;
873     char                filebuf[8192];
874     int                 sfd, dfd, len, err = AFP_OK;
875     ssize_t             cc;
876
877
878     if (newname) { 
879       if ((sfd = open( ad_path( src, ADFLAGS_HF ), O_RDONLY, 0 )) < 0 ) {
880         switch ( errno ) {
881         case ENOENT :
882             break; /* just copy the data fork */
883         case EACCES :
884             return( AFPERR_ACCESS );
885         default :
886             return( AFPERR_PARAM );
887         }
888       } else {
889         if (( dfd = open( ad_path( dst, ADFLAGS_HF ), O_WRONLY|O_CREAT,
890                 ad_mode( ad_path( dst, ADFLAGS_HF ), 0666 ))) < 0 ) {
891             close( sfd );
892             switch ( errno ) {
893             case ENOENT :
894                 return( AFPERR_NOOBJ );
895             case EACCES :
896                 return( AFPERR_ACCESS );
897             case EROFS:
898                 return AFPERR_VLOCK;
899             default :
900                 return( AFPERR_PARAM );
901             }
902         }
903
904         /* copy the file */
905 #ifdef SENDFILE_FLAVOR_LINUX
906         if (fstat(sfd, &st) == 0) {
907           if ((cc = sendfile(dfd, sfd, NULL, st.st_size)) < 0) {
908             switch (errno) {
909             case EDQUOT:
910             case EFBIG:
911             case ENOSPC:
912               err = AFPERR_DFULL;
913               break;
914             case EROFS:
915               err = AFPERR_VLOCK;
916               break;
917             default:
918               err = AFPERR_PARAM;
919             }
920           }
921           goto copyheader_done;
922         }
923 #endif
924         while (1) {
925           if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
926             if (errno == EINTR) 
927               continue;
928             err = AFPERR_PARAM;
929             break;
930           }
931
932           if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0))
933             break;
934         }
935
936 copyheader_done:
937         close(sfd);
938         close(dfd);
939         if (err < 0) {
940           unlink(ad_path(dst, ADFLAGS_HF));
941           return err;
942         }
943       }
944     }
945
946     /* data fork copying */
947     if (( sfd = open( src, O_RDONLY, 0 )) < 0 ) {
948         switch ( errno ) {
949         case ENOENT :
950             return( AFPERR_NOOBJ );
951         case EACCES :
952             return( AFPERR_ACCESS );
953         default :
954             return( AFPERR_PARAM );
955         }
956     }
957
958     if (( dfd = open( dst, O_WRONLY|O_CREAT, ad_mode( dst, 0666 ))) < 0 ) {
959         close( sfd );
960         switch ( errno ) {
961         case ENOENT :
962             return( AFPERR_NOOBJ );
963         case EACCES :
964             return( AFPERR_ACCESS );
965         case EROFS:
966             return AFPERR_VLOCK;
967         default :
968             return( AFPERR_PARAM );
969         }
970     }
971
972 #ifdef SENDFILE_FLAVOR_LINUX
973     if (fstat(sfd, &st) == 0) {
974       if ((cc = sendfile(dfd, sfd, NULL, st.st_size)) < 0) {
975         switch (errno) {
976         case EDQUOT:
977         case EFBIG:
978         case ENOSPC:
979           err = AFPERR_DFULL;
980           break;
981         default:
982           err = AFPERR_PARAM;
983         }
984       }
985       goto copydata_done;
986     }
987 #endif
988
989     while (1) {
990       if ((cc = read( sfd, filebuf, sizeof( filebuf ))) < 0) {
991         if (errno == EINTR)
992           continue;
993         
994         err = AFPERR_PARAM;
995         break;
996       }
997       
998       if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
999         break;
1000       }
1001     }
1002     
1003 copydata_done:
1004     close(sfd);
1005     close(dfd);
1006     if (err < 0) {
1007       unlink(ad_path(dst, ADFLAGS_HF));
1008       unlink(dst);
1009       return err;
1010     }
1011     
1012     if (newname) {
1013       memset(&ad, 0, sizeof(ad));
1014       if ( ad_open( dst, noadouble | ADFLAGS_HF, O_RDWR|O_CREAT,
1015                     0666, &ad) < 0 ) {
1016         switch ( errno ) {
1017         case ENOENT :
1018           return noadouble ? AFP_OK : AFPERR_NOOBJ;
1019         case EACCES :
1020           return( AFPERR_ACCESS );
1021         case EROFS:
1022           return AFPERR_VLOCK;
1023         default :
1024           return( AFPERR_PARAM );
1025         }
1026       }
1027
1028       len = strlen( newname );
1029       ad_setentrylen( &ad, ADEID_NAME, len );
1030       memcpy(ad_entry( &ad, ADEID_NAME ), newname, len );
1031       ad_flush( &ad, ADFLAGS_HF );
1032       ad_close( &ad, ADFLAGS_HF );
1033     }
1034     
1035     return( AFP_OK );
1036 }
1037
1038
1039 int deletefile( file )
1040     char                *file;
1041 {
1042     struct adouble      ad;
1043     int                 adflags, err = AFP_OK;
1044
1045     /* try to open both at once */
1046     adflags = ADFLAGS_DF|ADFLAGS_HF;
1047     memset(&ad, 0, sizeof(ad));
1048     if ( ad_open( file, adflags, O_RDWR, 0, &ad ) < 0 ) {
1049           switch (errno) {
1050           case ENOENT:
1051             adflags = ADFLAGS_DF;
1052             /* that failed. now try to open just the data fork */
1053             memset(&ad, 0, sizeof(ad));
1054             if ( ad_open( file, adflags, O_RDWR, 0, &ad ) < 0 ) {
1055               switch (errno) {
1056               case ENOENT:
1057                 return AFPERR_NOOBJ;
1058               case EACCES:
1059                 return AFPERR_ACCESS;
1060               default:
1061                 return AFPERR_PARAM;
1062               }
1063             }
1064             break;
1065
1066           case EACCES:
1067             return( AFPERR_ACCESS );
1068           case EROFS:
1069             return AFPERR_VLOCK;
1070           default:
1071             return( AFPERR_PARAM );
1072           }
1073     }
1074
1075     if ((adflags & ADFLAGS_HF) &&
1076         (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR, 0, 0) < 0 )) {
1077       ad_close( &ad, adflags );
1078       return( AFPERR_BUSY );
1079     }
1080
1081     if (ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0 ) < 0) {
1082       err = AFPERR_BUSY;
1083       goto delete_unlock;
1084     }
1085
1086     if ( unlink( ad_path( file, ADFLAGS_HF )) < 0 ) {
1087         switch ( errno ) {
1088         case EPERM:
1089         case EACCES :
1090             err = AFPERR_ACCESS;
1091             goto delete_unlock;
1092         case EROFS:
1093             err = AFPERR_VLOCK;
1094             goto delete_unlock;
1095         case ENOENT :
1096             break;
1097         default :
1098             err = AFPERR_PARAM;
1099             goto delete_unlock;
1100         }
1101     }
1102
1103     if ( unlink( file ) < 0 ) {
1104         switch ( errno ) {
1105         case EPERM:
1106         case EACCES :
1107             err = AFPERR_ACCESS;
1108             break;
1109         case EROFS:
1110             err = AFPERR_VLOCK;
1111             break;
1112         case ENOENT :
1113             break;
1114         default :
1115             err = AFPERR_PARAM;
1116             break;
1117         }
1118     }
1119
1120 delete_unlock:
1121     if (adflags & ADFLAGS_HF)
1122       ad_tmplock(&ad, ADEID_RFORK, ADLOCK_CLR, 0, 0);
1123     ad_tmplock(&ad, ADEID_DFORK, ADLOCK_CLR, 0, 0);
1124     ad_close( &ad, adflags );
1125     return err;
1126 }
1127
1128
1129 #if AD_VERSION > AD_VERSION1
1130 /* return a file id */
1131 int afp_createid(obj, ibuf, ibuflen, rbuf, rbuflen )
1132     AFPObj      *obj;
1133     char        *ibuf, *rbuf;
1134     int         ibuflen, *rbuflen;
1135 {
1136     struct stat         st;
1137     struct adouble      ad;
1138     struct vol          *vol;
1139     struct dir          *dir;
1140     char                *path, *upath;
1141     int                 len;
1142     cnid_t              did, id;
1143     u_short             vid;
1144     
1145     *rbuflen = 0;
1146     ibuf += 2;
1147
1148     memcpy(&vid, ibuf, sizeof(vid));
1149     ibuf += sizeof(vid);
1150
1151     if (( vol = getvolbyvid( vid )) == NULL ) {
1152         return( AFPERR_PARAM);
1153     }
1154
1155     if (vol->v_flags & AFPVOL_RO)
1156         return AFPERR_VLOCK;
1157
1158     memcpy(&did, ibuf, sizeof( did ));
1159     ibuf += sizeof(did);
1160
1161     if (( dir = dirsearch( vol, did )) == NULL ) {
1162         return( AFPERR_PARAM );
1163     }
1164
1165     if (( path = cname( vol, dir, &ibuf )) == NULL ) {
1166         return( AFPERR_PARAM );
1167     }
1168
1169     if ( *path == '\0' ) {
1170         return( AFPERR_BADTYPE );
1171     }
1172
1173     upath = mtoupath(vol, path);
1174     if (stat(upath, &st) < 0) {
1175       switch (errno) {
1176       case EPERM:
1177       case EACCES:
1178         return AFPERR_ACCESS;
1179       case ENOENT:
1180         return AFPERR_NOOBJ;
1181       default:
1182         return AFPERR_PARAM;
1183       }
1184     }
1185
1186     if (id = cnid_lookup(vol->v_db, &st, did, upath, len = strlen(upath))) {
1187       memcpy(rbuf, &id, sizeof(id));
1188       *rbuflen = sizeof(id);
1189       return AFPERR_EXISTID;
1190     }
1191
1192     memset(&ad, 0, sizeof(ad));
1193     if (ad_open( upath, ADFLAGS_HF, O_RDONLY, 0, &ad ) < 0)
1194       id = 0;
1195     else {
1196       memcpy(&id, ad_entry(&ad, ADEID_DID), sizeof(id));
1197       ad_close(upath, ADFLAGS_HF);
1198     }
1199
1200     if (id = cnid_add(vol->v_db, &st, did, upath, len, id)) {
1201       memcpy(rbuf, &id, sizeof(id));
1202       *rbuflen = sizeof(id);
1203       return AFP_OK;
1204     }
1205
1206     switch (errno) {
1207     case EROFS:
1208       return AFPERR_VLOCK;
1209       break;
1210     case EPERM:
1211     case EACCES:
1212       return AFPERR_ACCESS;
1213       break;
1214     default:
1215       syslog(LOG_ERR, "afp_createid: cnid_add: %m");
1216       return AFPERR_PARAM;
1217     }
1218 }
1219
1220 /* resolve a file id */
1221 int afp_resolveid(obj, ibuf, ibuflen, rbuf, rbuflen )
1222     AFPObj      *obj;
1223     char        *ibuf, *rbuf;
1224     int         ibuflen, *rbuflen;
1225 {
1226     struct stat         st;
1227     struct vol          *vol;
1228     struct dir          *dir;
1229     char                *upath;
1230     int                 err, buflen;
1231     cnid_t              id;
1232     u_int16_t           vid, bitmap;
1233     
1234     *rbuflen = 0;
1235     ibuf += 2;
1236
1237     memcpy(&vid, ibuf, sizeof(vid));
1238     ibuf += sizeof(vid);
1239
1240     if (( vol = getvolbyvid( vid )) == NULL ) {
1241         return( AFPERR_PARAM);
1242     }
1243
1244     memcpy(&id, ibuf, sizeof( id ));
1245     ibuf += sizeof(id);
1246
1247     if ((upath = cnid_resolve(vol->v_db, &id)) == NULL) {
1248       return AFPERR_BADID;
1249     }
1250
1251     if (( dir = dirsearch( vol, id )) == NULL ) {
1252       return( AFPERR_PARAM );
1253     }
1254
1255     if ((movecwd(vol, dir) < 0) || (stat(upath, &st) < 0)) {
1256       switch (errno) {
1257       case EACCES:
1258       case EPERM:
1259         return AFPERR_ACCESS;
1260       case ENOENT:
1261         return AFPERR_NOID;
1262       default:
1263         return AFPERR_PARAM;
1264       }
1265     }
1266
1267     /* directories are bad */
1268     if (S_ISDIR(st.st_mode))
1269       return AFPERR_BADTYPE;
1270
1271     memcpy(&bitmap, ibuf, sizeof(bitmap));
1272     bitmap = ntohs( bitmap );
1273
1274     if ((err = getfilparams(vol, bitmap, utompath(vol, upath), curdir, &st,
1275                            rbuf + sizeof(bitmap), &buflen)) != AFP_OK)
1276       return err;
1277
1278     *rbuflen = buflen + sizeof(bitmap);
1279     memcpy(rbuf, ibuf, sizeof(bitmap));
1280     return AFP_OK;
1281 }
1282
1283 int afp_deleteid(obj, ibuf, ibuflen, rbuf, rbuflen )
1284     AFPObj      *obj;
1285     char        *ibuf, *rbuf;
1286     int         ibuflen, *rbuflen;
1287 {
1288     struct stat         st;
1289     struct vol          *vol;
1290     struct dir          *dir;
1291     char                *upath;
1292     int                 err;
1293     cnid_t              id;
1294     u_short             vid;
1295     
1296     *rbuflen = 0;
1297     ibuf += 2;
1298
1299     memcpy(&vid, ibuf, sizeof(vid));
1300     ibuf += sizeof(vid);
1301
1302     if (( vol = getvolbyvid( vid )) == NULL ) {
1303         return( AFPERR_PARAM);
1304     }
1305
1306     if (vol->v_flags & AFPVOL_RO)
1307         return AFPERR_VLOCK;
1308
1309     memcpy(&id, ibuf, sizeof( id ));
1310     ibuf += sizeof(id);
1311
1312     if ((upath = cnid_resolve(vol->v_db, &id)) == NULL) {
1313       return AFPERR_NOID;
1314     }
1315
1316     if (( dir = dirsearch( vol, id )) == NULL ) {
1317       return( AFPERR_PARAM );
1318     }
1319
1320     err = AFP_OK;
1321     if ((movecwd(vol, dir) < 0) || (stat(upath, &st) < 0)) {
1322       switch (errno) {
1323       case EACCES:
1324       case EPERM:
1325         return AFPERR_ACCESS;
1326       case ENOENT:
1327         /* still try to delete the id */
1328         err = AFPERR_NOOBJ;
1329         break;
1330       default:
1331         return AFPERR_PARAM;
1332       }
1333     }
1334
1335     /* directories are bad */
1336     if (S_ISDIR(st.st_mode))
1337       return AFPERR_BADTYPE;
1338
1339     if (cnid_delete(vol->v_db, id)) {
1340       switch (errno) {
1341       case EROFS:
1342         return AFPERR_VLOCK;
1343       case EPERM:
1344       case EACCES:
1345         return AFPERR_ACCESS;
1346       default:
1347         return AFPERR_PARAM;
1348       }
1349     }
1350
1351     return err;
1352 }
1353 #endif
1354
1355 #define APPLETEMP ".AppleTempXXXXXX"
1356 int afp_exchangefiles(obj, ibuf, ibuflen, rbuf, rbuflen )
1357     AFPObj      *obj;
1358     char        *ibuf, *rbuf;
1359     int         ibuflen, *rbuflen;
1360 {
1361     struct stat         srcst, destst;
1362     struct vol          *vol;
1363     struct dir          *dir, *sdir;
1364     char                *spath, temp[17], *path, *p;
1365     char                *supath, *upath;
1366     int                 err;
1367 #if AD_VERSION > AD_VERSION1
1368     int                 slen, dlen;
1369 #endif
1370     cnid_t              sid, did;
1371     u_int16_t           vid;
1372     
1373     *rbuflen = 0;
1374     ibuf += 2;
1375
1376     memcpy(&vid, ibuf, sizeof(vid));
1377     ibuf += sizeof(vid);
1378
1379     if (( vol = getvolbyvid( vid )) == NULL ) {
1380         return( AFPERR_PARAM);
1381     }
1382
1383     if (vol->v_flags & AFPVOL_RO)
1384         return AFPERR_VLOCK;
1385
1386     /* source and destination dids */
1387     memcpy(&sid, ibuf, sizeof(sid));
1388     ibuf += sizeof(sid);
1389     memcpy(&did, ibuf, sizeof(did));
1390     ibuf += sizeof(did);
1391
1392     /* source file */
1393     if ((dir = dirsearch( vol, sid )) == NULL ) {
1394         return( AFPERR_PARAM );
1395     }
1396
1397     if (( path = cname( vol, dir, &ibuf )) == NULL ) {
1398         return( AFPERR_PARAM );
1399     }
1400
1401     if ( *path == '\0' ) {
1402         return( AFPERR_BADTYPE );
1403     }
1404
1405     upath = mtoupath(vol, path);
1406     if (stat(upath, &srcst) < 0) {
1407       switch (errno) {
1408       case ENOENT:
1409         return AFPERR_NOID;
1410       case EPERM:
1411       case EACCES:
1412         return AFPERR_ACCESS;
1413       default:
1414         return AFPERR_PARAM;
1415       }
1416     }
1417
1418     /* save some stuff */
1419     sdir = curdir;
1420     spath = obj->oldtmp;
1421     supath = obj->newtmp;
1422     strcpy(spath, path);
1423     strcpy(supath, upath); /* this is for the cnid changing */
1424     p = ctoupath( vol, sdir, spath);
1425
1426     /* look for the source cnid. if it doesn't exist, don't worry about
1427      * it. */
1428 #if AD_VERSION > AD_VERSION1
1429     sid = cnid_lookup(vol->v_db, &srcst, sdir->d_did, supath, 
1430                       slen = strlen(supath));
1431 #endif
1432
1433     if (( dir = dirsearch( vol, did )) == NULL ) {
1434         return( AFPERR_PARAM );
1435     }
1436
1437     if (( path = cname( vol, dir, &ibuf )) == NULL ) {
1438         return( AFPERR_PARAM );
1439     }
1440
1441     if ( *path == '\0' ) {
1442         return( AFPERR_BADTYPE );
1443     }
1444
1445     /* FPExchangeFiles is the only call that can return the SameObj
1446      * error */
1447     if ((curdir == sdir) && strcmp(spath, path) == 0)
1448       return AFPERR_SAMEOBJ;
1449
1450     upath = mtoupath(vol, path);
1451     if (stat(upath, &destst) < 0) {
1452       switch (errno) {
1453       case ENOENT:
1454         return AFPERR_NOID;
1455       case EPERM:
1456       case EACCES:
1457         return AFPERR_ACCESS;
1458       default:
1459         return AFPERR_PARAM;
1460       }
1461     }
1462
1463 #if AD_VERSION > AD_VERSION1
1464     /* look for destination id. */
1465     did = cnid_lookup(vol->v_db, &destst, curdir->d_did, upath, 
1466                       dlen = strlen(upath));
1467 #endif
1468
1469     /* construct a temp name. 
1470      * NOTE: the temp file will be in the dest file's directory. it
1471      * will also be inaccessible from AFP. */
1472     memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
1473     if (!mktemp(temp))
1474       return AFPERR_MISC;
1475
1476     /* now, quickly rename the file. we error if we can't. */
1477     if ((err = renamefile(p, temp, temp, vol_noadouble(vol))) < 0)
1478       goto err_exchangefile;
1479     of_rename(vol, sdir, spath, curdir, temp);
1480
1481     /* rename destination to source */
1482     if ((err = renamefile(path, p, spath, vol_noadouble(vol))) < 0) 
1483       goto err_src_to_tmp;
1484     of_rename(vol, curdir, path, sdir, spath);
1485
1486     /* rename temp to destination */
1487     if ((err = renamefile(temp, upath, path, vol_noadouble(vol))) < 0) 
1488       goto err_dest_to_src;
1489     of_rename(vol, curdir, temp, curdir, path);
1490     
1491 #if AD_VERSION > AD_VERSION1
1492     /* id's need switching. src -> dest and dest -> src. */
1493     if (sid && (cnid_update(vol->v_db, sid, &destst, curdir->d_did, 
1494                             upath, dlen) < 0)) {
1495       switch (errno) {
1496       case EPERM:
1497       case EACCES:
1498         err = AFPERR_ACCESS;
1499       default:
1500         err = AFPERR_PARAM;
1501       }
1502       goto err_temp_to_dest;
1503     }
1504
1505     if (did && (cnid_update(vol->v_db, did, &srcst, sdir->d_did,
1506                             supath, slen) < 0)) {
1507       switch (errno) {
1508       case EPERM:
1509       case EACCES:
1510         err = AFPERR_ACCESS;
1511       default:
1512         err = AFPERR_PARAM;
1513       }
1514
1515       if (sid)
1516         cnid_update(vol->v_db, sid, &srcst, sdir->d_did, supath, slen);
1517       goto err_temp_to_dest;
1518     }
1519 #endif
1520     return AFP_OK;
1521
1522
1523     /* all this stuff is so that we can unwind a failed operation 
1524      * properly. */
1525 err_temp_to_dest:
1526     /* rename dest to temp */
1527     renamefile(upath, temp, temp, vol_noadouble(vol));
1528     of_rename(vol, curdir, upath, curdir, temp);
1529
1530 err_dest_to_src:
1531     /* rename source back to dest */
1532     renamefile(p, upath, path, vol_noadouble(vol));
1533     of_rename(vol, sdir, spath, curdir, path);
1534
1535 err_src_to_tmp:
1536     /* rename temp back to source */
1537     renamefile(temp, p, spath, vol_noadouble(vol));
1538     of_rename(vol, curdir, temp, sdir, spath);
1539
1540 err_exchangefile:
1541     return err;
1542 }