]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/file.c
Permission kludge; 95% bug-free!
[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     syslog(LOG_INFO, "afp_createfile");
334     *rbuflen = 0;
335     ibuf++;
336     creatf = (unsigned char) *ibuf++;
337
338     memcpy(&vid, ibuf, sizeof( vid ));
339     ibuf += sizeof( vid );
340
341     if (( vol = getvolbyvid( vid )) == NULL ) {
342         return( AFPERR_PARAM );
343     }
344
345     if (vol->v_flags & AFPVOL_RO)
346         return AFPERR_VLOCK;
347
348     memcpy(&did, ibuf, sizeof( did));
349     ibuf += sizeof( did );
350
351     if (( dir = dirsearch( vol, did )) == NULL ) {
352         return( AFPERR_NOOBJ );
353     }
354
355     if (( path = cname( vol, dir, &ibuf )) == NULL ) {
356         return( AFPERR_NOOBJ );
357     }
358
359     if ((vol->v_flags & AFPVOL_MSWINDOWS) && 
360         strpbrk(path, MSWINDOWS_BADCHARS))
361         return AFPERR_PARAM;
362
363     upath = mtoupath(vol, path);
364
365     if ((vol->v_flags & AFPVOL_NOHEX) && strchr(upath, '/'))
366       return AFPERR_PARAM;
367
368     if (!validupath(vol, upath))
369       return AFPERR_EXIST;
370
371     if ((of = of_findname(vol, curdir, path))) {
372       adp = of->of_ad;
373     } else {
374       memset(&ad, 0, sizeof(ad));
375       adp = &ad;
376     }
377     if ( creatf) {
378         /* on a hard create, fail if file exists and is open */
379         if ((stat(upath, &st) == 0) && of)
380           return AFPERR_BUSY;
381         openf = O_RDWR|O_CREAT|O_TRUNC;
382     } else {
383         openf = O_RDWR|O_CREAT|O_EXCL;
384     }
385
386     if ( ad_open( upath, vol_noadouble(vol)|ADFLAGS_DF|ADFLAGS_HF,
387                   openf, 0666, adp) < 0 ) {
388       switch ( errno ) {
389         case EEXIST :
390             return( AFPERR_EXIST );
391         case EACCES :
392             return( AFPERR_ACCESS );
393         case ENOENT:
394             /* on noadouble volumes, just creating the data fork is ok */
395             if (vol_noadouble(vol) && (stat(upath, &st) == 0))
396               goto createfile_done;
397             /* fallthrough */
398         default :
399             return( AFPERR_PARAM );
400         }
401     }
402
403     ad_setentrylen( adp, ADEID_NAME, strlen( path ));
404     memcpy(ad_entry( adp, ADEID_NAME ), path, 
405            ad_getentrylen( adp, ADEID_NAME ));
406     ad_flush( adp, ADFLAGS_DF|ADFLAGS_HF );
407     ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
408
409 createfile_done:
410
411 #ifdef DROPKLUDGE
412
413 /* The below code is an experimental, untested, incomplete kludge which 
414 provides better dropbox support.  It should NOT be turned on yet unless
415 you are a developer who wants to try it out and fix it. */
416     if (stat(".", &sb) == -1) 
417       syslog (LOG_ERR, "Error checking directory %s: %m", dir->d_name);
418     else {
419       uid=geteuid();
420       if ( uid != sb.st_uid )
421       {
422         strcpy (adpath, "./.AppleDouble/");
423         strcat (adpath, path);
424         seteuid(0); /* Become root to change the owner of the file */
425         syslog (LOG_INFO, "Changing %s to uid=%d gid=%d", path, sb.st_uid, sb.st_gid);
426         if (chown(path, sb.st_uid, sb.st_gid)==-1)
427           syslog (LOG_ERR, "Error changing permissions: %m");
428         /* In order to write information to the file, the Mac client needs
429         to be able to read from it too, so read bits have to be turned on.
430         Directory permissions remain unchanged */
431         chmod(path,(sb.st_mode&0x0FFFF)| S_IRGRP| S_IROTH);
432         if (chown(adpath, sb.st_uid, sb.st_gid)==-1)
433           syslog (LOG_ERR, "Error changing AppleDouble permissions: %m");
434         syslog (LOG_INFO, "Changing afpd owner back to %d", uid);
435         seteuid(uid); /* Restore process ownership to normal */
436       }
437     }
438
439 #endif DROPKLUDGE
440
441     setvoltime(obj, vol );
442     return AFP_OK;
443 }
444
445 int afp_setfilparams(obj, ibuf, ibuflen, rbuf, rbuflen )
446     AFPObj      *obj;
447     char        *ibuf, *rbuf;
448     int         ibuflen, *rbuflen;
449 {
450     struct vol  *vol;
451     struct dir  *dir;
452     char        *path;
453     int         did, rc;
454     u_int16_t   vid, bitmap;
455
456     syslog (LOG_INFO, "afp_setfilparams");
457     *rbuflen = 0;
458     ibuf += 2;
459
460     memcpy(&vid, ibuf, sizeof( vid ));
461     ibuf += sizeof( vid );
462     if (( vol = getvolbyvid( vid )) == NULL ) {
463         return( AFPERR_PARAM );
464     }
465
466     if (vol->v_flags & AFPVOL_RO)
467         return AFPERR_VLOCK;
468
469     memcpy(&did, ibuf, sizeof( did ));
470     ibuf += sizeof( did );
471     if (( dir = dirsearch( vol, did )) == NULL ) {
472         return( AFPERR_NOOBJ );
473     }
474
475     memcpy(&bitmap, ibuf, sizeof( bitmap ));
476     bitmap = ntohs( bitmap );
477     ibuf += sizeof( bitmap );
478
479     if (( path = cname( vol, dir, &ibuf )) == NULL ) {
480         return( AFPERR_NOOBJ );
481     }
482
483     if ((u_long)ibuf & 1 ) {
484         ibuf++;
485     }
486
487     if (( rc = setfilparams(vol, path, bitmap, ibuf )) == AFP_OK ) {
488         setvoltime(obj, vol );
489     }
490
491     return( rc );
492 }
493
494
495 int setfilparams(vol, path, bitmap, buf )
496     struct vol  *vol;
497     char        *path, *buf;
498     u_int16_t   bitmap;
499 {
500     struct adouble      ad, *adp;
501     struct ofork        *of;
502     struct extmap       *em;
503     int                 bit = 0, isad = 1, err = AFP_OK;
504     char                *upath;
505     u_char              achar, *fdType, xyy[4];
506     u_int16_t           ashort, bshort;
507     u_int32_t           aint;
508     struct utimbuf      ut;
509
510     upath = mtoupath(vol, path);
511     if ((of = of_findname(vol, curdir, path))) {
512       adp = of->of_ad;
513     } else {
514       memset(&ad, 0, sizeof(ad));
515       adp = &ad;
516     }
517     if (ad_open( upath, vol_noadouble(vol) | ADFLAGS_HF, 
518                  O_RDWR|O_CREAT, 0666, adp) < 0) {
519       /* for some things, we don't need an adouble header */
520       if (bitmap & ~(1<<FILPBIT_MDATE)) {
521         return vol_noadouble(vol) ? AFP_OK : AFPERR_ACCESS;
522       }
523       isad = 0;
524     } else if ((ad_getoflags( adp, ADFLAGS_HF ) & O_CREAT) ) {
525         ad_setentrylen( adp, ADEID_NAME, strlen( path ));
526         memcpy(ad_entry( adp, ADEID_NAME ), path, 
527                ad_getentrylen( adp, ADEID_NAME ));
528     }
529
530     while ( bitmap != 0 ) {
531         while (( bitmap & 1 ) == 0 ) {
532             bitmap = bitmap>>1;
533             bit++;
534         }
535
536         switch(  bit ) {
537         case FILPBIT_ATTR :
538             memcpy(&ashort, buf, sizeof( ashort ));
539             ad_getattr(adp, &bshort);
540             if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
541               bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
542             } else {
543               bshort &= ~ashort;
544             }
545             ad_setattr(adp, bshort);
546             buf += sizeof( ashort );
547             break;
548
549         case FILPBIT_CDATE :
550             memcpy(&aint, buf, sizeof(aint));
551             ad_setdate(adp, AD_DATE_CREATE, aint);
552             buf += sizeof( aint );
553             break;
554
555         case FILPBIT_MDATE :
556             memcpy(&aint, buf, sizeof( aint ));
557             if (isad)
558               ad_setdate(adp, AD_DATE_MODIFY, aint);
559             ut.actime = ut.modtime = AD_DATE_TO_UNIX(aint);
560             utime(upath, &ut);
561             buf += sizeof( aint );
562             break;
563
564         case FILPBIT_BDATE :
565             memcpy(&aint, buf, sizeof(aint));
566             ad_setdate(adp, AD_DATE_BACKUP, aint);
567             buf += sizeof( aint );
568             break;
569
570         case FILPBIT_FINFO :
571             if ((memcmp( ad_entry( adp, ADEID_FINDERI ), ufinderi, 8 ) == 0)
572                 && (em = getextmap( path )) && 
573                 (memcmp(buf, em->em_type, sizeof( em->em_type )) == 0) &&
574                 (memcmp(buf + 4, em->em_creator,
575                         sizeof( em->em_creator )) == 0)) {
576               memcpy(buf, ufinderi, 8 );
577             }
578             memcpy(ad_entry( adp, ADEID_FINDERI ), buf, 32 );
579             buf += 32;
580             break;
581
582             /* Client needs to set the ProDOS file info for this file.
583                Use defined strings for the simple cases, and convert
584                all else into pXYY per Inside Appletalk.  Always set
585                the creator as "pdos". <shirsch@ibm.net> */
586         case FILPBIT_PDINFO :
587             achar = *buf;
588             buf += 2;
589             memcpy(&ashort, buf, sizeof( ashort ));
590             ashort = ntohs( ashort );
591             buf += 2;
592  
593             switch ( (unsigned int) achar )
594               {
595               case 0x04 :
596                 fdType = ( u_char *) "TEXT";
597                 break;
598                 
599               case 0xff :
600                 fdType = ( u_char *) "PSYS";
601                 break;
602  
603               case 0xb3 :
604                 fdType = ( u_char *) "PS16";
605                 break;
606  
607               case 0x00 :
608                 fdType = ( u_char *) "BINA";
609                 break;
610  
611               default :
612                 xyy[0] = ( u_char ) 'p';
613                 xyy[1] = achar;
614                 xyy[2] = ( u_char ) ( ashort >> 8 ) & 0xff;
615                 xyy[3] = ( u_char ) ashort & 0xff;
616                 fdType = xyy;
617                 break;
618               }
619             
620             memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
621             memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
622             break;
623  
624
625         default :
626             err = AFPERR_BITMAP;
627             goto setfilparam_done;
628         }
629
630         bitmap = bitmap>>1;
631         bit++;
632     }
633
634 setfilparam_done:
635     if (isad) {
636       ad_flush( adp, ADFLAGS_HF );
637       ad_close( adp, ADFLAGS_HF );
638     }
639     return err;
640 }
641
642 /*
643  * renamefile and copyfile take the old and new unix pathnames
644  * and the new mac name.
645  * NOTE: if we have to copy a file instead of renaming it, locks
646  *       will break.
647  */
648 int renamefile(src, dst, newname, noadouble )
649     char        *src, *dst, *newname;
650     const int         noadouble;
651 {
652     struct adouble      ad;
653     char                adsrc[ MAXPATHLEN + 1];
654     int                 len, rc;
655
656     /*
657      * Note that this is only checking the existance of the data file,
658      * not the header file.  The thinking is that if the data file doesn't
659      * exist, but the header file does, the right thing to do is remove
660      * the data file silently.
661      */
662
663     /* existence check moved to afp_moveandrename */
664
665     if ( rename( src, dst ) < 0 ) {
666         switch ( errno ) {
667         case ENOENT :
668             return( AFPERR_NOOBJ );
669         case EPERM:
670         case EACCES :
671             return( AFPERR_ACCESS );
672         case EROFS:
673             return AFPERR_VLOCK;
674         case EXDEV :                    /* Cross device move -- try copy */
675             if (( rc = copyfile(src, dst, newname, noadouble )) != AFP_OK ) {
676                 deletefile( dst );
677                 return( rc );
678             }
679             return deletefile( src );
680         default :
681             return( AFPERR_PARAM );
682         }
683     }
684
685     strcpy( adsrc, ad_path( src, 0 ));
686     rc = 0;
687 rename_retry:
688     if (rename( adsrc, ad_path( dst, 0 )) < 0 ) {
689         struct stat st;
690
691         switch ( errno ) {
692         case ENOENT :
693           /* check for a source appledouble header. if it exists, make
694            * a dest appledouble directory and do the rename again. */
695           memset(&ad, 0, sizeof(ad));
696           if (rc || stat(adsrc, &st) ||
697               (ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad) < 0))
698             return AFP_OK;
699           rc++;
700           ad_close(&ad, ADFLAGS_HF);
701           goto rename_retry;
702         case EPERM:
703         case EACCES :
704             return( AFPERR_ACCESS );
705         case EROFS:
706             return AFPERR_VLOCK;
707         default :
708             return( AFPERR_PARAM );
709         }
710     }
711
712     memset(&ad, 0, sizeof(ad));
713     if ( ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, &ad) < 0 ) {
714         switch ( errno ) {
715         case ENOENT :
716             return( AFPERR_NOOBJ );
717         case EACCES :
718             return( AFPERR_ACCESS );
719         case EROFS:
720             return AFPERR_VLOCK;
721         default :
722             return( AFPERR_PARAM );
723         }
724     }
725
726     len = strlen( newname );
727     ad_setentrylen( &ad, ADEID_NAME, len );
728     memcpy(ad_entry( &ad, ADEID_NAME ), newname, len );
729     ad_flush( &ad, ADFLAGS_HF );
730     ad_close( &ad, ADFLAGS_HF );
731
732     return( AFP_OK );
733 }
734
735 int afp_copyfile(obj, ibuf, ibuflen, rbuf, rbuflen )
736     AFPObj      *obj;
737     char        *ibuf, *rbuf;
738     int         ibuflen, *rbuflen;
739 {
740     struct vol  *vol;
741     struct dir  *dir;
742     char        *newname, *path, *p;
743     u_int32_t   sdid, ddid;
744     int         plen, err;
745     u_int16_t   svid, dvid;
746
747     syslog(LOG_INFO, "afp_copyfile");
748     *rbuflen = 0;
749     ibuf += 2;
750
751     memcpy(&svid, ibuf, sizeof( svid ));
752     ibuf += sizeof( svid );
753     if (( vol = getvolbyvid( svid )) == NULL ) {
754         return( AFPERR_PARAM );
755     }
756
757     memcpy(&sdid, ibuf, sizeof( sdid ));
758     ibuf += sizeof( sdid );
759     if (( dir = dirsearch( vol, sdid )) == NULL ) {
760         return( AFPERR_PARAM );
761     }
762
763     memcpy(&dvid, ibuf, sizeof( dvid ));
764     ibuf += sizeof( dvid );
765     memcpy(&ddid, ibuf, sizeof( ddid ));
766     ibuf += sizeof( ddid );
767
768     if (( path = cname( vol, dir, &ibuf )) == NULL ) {
769         return( AFPERR_NOOBJ );
770     }
771     if ( *path == '\0' ) {
772         return( AFPERR_BADTYPE );
773     }
774
775     /* don't allow copies when the file is open.
776      * XXX: the spec only calls for read/deny write access.
777      *      however, copyfile doesn't have any of that info,
778      *      and locks need to stay coherent. as a result,
779      *      we just balk if the file is opened already. */
780     if (of_findname(vol, curdir, path))
781       return AFPERR_DENYCONF;
782     
783     newname = obj->newtmp;
784     strcpy( newname, path );
785
786     p = ctoupath( vol, curdir, newname );
787
788     if (( vol = getvolbyvid( dvid )) == NULL ) {
789         return( AFPERR_PARAM );
790     }
791
792     if (vol->v_flags & AFPVOL_RO)
793         return AFPERR_VLOCK;
794
795     if (( dir = dirsearch( vol, ddid )) == NULL ) {
796         return( AFPERR_PARAM );
797     }
798
799     if (( path = cname( vol, dir, &ibuf )) == NULL ) {
800         return( AFPERR_NOOBJ );
801     }
802     if ( *path != '\0' ) {
803         return( AFPERR_BADTYPE );
804     }
805
806     /* one of the handful of places that knows about the path type */
807     if ( *ibuf++ != 2 ) {
808         return( AFPERR_PARAM );
809     }
810     if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
811         strncpy( newname, ibuf, plen );
812         newname[ plen ] = '\0';
813     }
814
815     if ( (err = copyfile(p, mtoupath(vol, newname ), newname, 
816                   vol_noadouble(vol))) < 0 ) {
817         return err;
818     }
819
820     setvoltime(obj, vol );
821     return( AFP_OK );
822 }
823
824
825 static __inline__ int copy_all(const int dfd, const void *buf,
826                                size_t buflen)
827 {
828   ssize_t cc;
829
830   while (buflen > 0) {
831     if ((cc = write(dfd, buf, buflen)) < 0) {
832       switch (errno) {
833       case EINTR:
834         continue;
835       case EDQUOT:
836       case EFBIG:
837       case ENOSPC:
838         return AFPERR_DFULL;
839       case EROFS:
840         return AFPERR_VLOCK;
841       default:
842         return AFPERR_PARAM;
843       }
844     }
845     buflen -= cc;
846   }
847
848   return 0;
849 }
850
851 /* XXX: this needs to use ad_open and ad_lock. so, we need to
852  * pass in vol and path */
853 int copyfile(src, dst, newname, noadouble )
854     char        *src, *dst, *newname;
855     const int   noadouble;
856 {
857     struct adouble      ad;
858     struct stat         st;
859     char                filebuf[8192];
860     int                 sfd, dfd, len, err = AFP_OK;
861     ssize_t             cc;
862
863
864     if (newname) { 
865       if ((sfd = open( ad_path( src, ADFLAGS_HF ), O_RDONLY, 0 )) < 0 ) {
866         switch ( errno ) {
867         case ENOENT :
868             break; /* just copy the data fork */
869         case EACCES :
870             return( AFPERR_ACCESS );
871         default :
872             return( AFPERR_PARAM );
873         }
874       } else {
875         if (( dfd = open( ad_path( dst, ADFLAGS_HF ), O_WRONLY|O_CREAT,
876                 ad_mode( ad_path( dst, ADFLAGS_HF ), 0666 ))) < 0 ) {
877             close( sfd );
878             switch ( errno ) {
879             case ENOENT :
880                 return( AFPERR_NOOBJ );
881             case EACCES :
882                 return( AFPERR_ACCESS );
883             case EROFS:
884                 return AFPERR_VLOCK;
885             default :
886                 return( AFPERR_PARAM );
887             }
888         }
889
890         /* copy the file */
891 #ifdef SENDFILE_FLAVOR_LINUX
892         if (fstat(sfd, &st) == 0) {
893           if ((cc = sendfile(dfd, sfd, NULL, st.st_size)) < 0) {
894             switch (errno) {
895             case EDQUOT:
896             case EFBIG:
897             case ENOSPC:
898               err = AFPERR_DFULL;
899               break;
900             case EROFS:
901               err = AFPERR_VLOCK;
902               break;
903             default:
904               err = AFPERR_PARAM;
905             }
906           }
907           goto copyheader_done;
908         }
909 #endif
910         while (1) {
911           if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
912             if (errno == EINTR) 
913               continue;
914             err = AFPERR_PARAM;
915             break;
916           }
917
918           if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0))
919             break;
920         }
921
922 copyheader_done:
923         close(sfd);
924         close(dfd);
925         if (err < 0) {
926           unlink(ad_path(dst, ADFLAGS_HF));
927           return err;
928         }
929       }
930     }
931
932     /* data fork copying */
933     if (( sfd = open( src, O_RDONLY, 0 )) < 0 ) {
934         switch ( errno ) {
935         case ENOENT :
936             return( AFPERR_NOOBJ );
937         case EACCES :
938             return( AFPERR_ACCESS );
939         default :
940             return( AFPERR_PARAM );
941         }
942     }
943
944     if (( dfd = open( dst, O_WRONLY|O_CREAT, ad_mode( dst, 0666 ))) < 0 ) {
945         close( sfd );
946         switch ( errno ) {
947         case ENOENT :
948             return( AFPERR_NOOBJ );
949         case EACCES :
950             return( AFPERR_ACCESS );
951         case EROFS:
952             return AFPERR_VLOCK;
953         default :
954             return( AFPERR_PARAM );
955         }
956     }
957
958 #ifdef SENDFILE_FLAVOR_LINUX
959     if (fstat(sfd, &st) == 0) {
960       if ((cc = sendfile(dfd, sfd, NULL, st.st_size)) < 0) {
961         switch (errno) {
962         case EDQUOT:
963         case EFBIG:
964         case ENOSPC:
965           err = AFPERR_DFULL;
966           break;
967         default:
968           err = AFPERR_PARAM;
969         }
970       }
971       goto copydata_done;
972     }
973 #endif
974
975     while (1) {
976       if ((cc = read( sfd, filebuf, sizeof( filebuf ))) < 0) {
977         if (errno == EINTR)
978           continue;
979         
980         err = AFPERR_PARAM;
981         break;
982       }
983       
984       if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
985         break;
986       }
987     }
988     
989 copydata_done:
990     close(sfd);
991     close(dfd);
992     if (err < 0) {
993       unlink(ad_path(dst, ADFLAGS_HF));
994       unlink(dst);
995       return err;
996     }
997     
998     if (newname) {
999       memset(&ad, 0, sizeof(ad));
1000       if ( ad_open( dst, noadouble | ADFLAGS_HF, O_RDWR|O_CREAT,
1001                     0666, &ad) < 0 ) {
1002         switch ( errno ) {
1003         case ENOENT :
1004           return noadouble ? AFP_OK : AFPERR_NOOBJ;
1005         case EACCES :
1006           return( AFPERR_ACCESS );
1007         case EROFS:
1008           return AFPERR_VLOCK;
1009         default :
1010           return( AFPERR_PARAM );
1011         }
1012       }
1013
1014       len = strlen( newname );
1015       ad_setentrylen( &ad, ADEID_NAME, len );
1016       memcpy(ad_entry( &ad, ADEID_NAME ), newname, len );
1017       ad_flush( &ad, ADFLAGS_HF );
1018       ad_close( &ad, ADFLAGS_HF );
1019     }
1020     
1021     return( AFP_OK );
1022 }
1023
1024
1025 int deletefile( file )
1026     char                *file;
1027 {
1028     struct adouble      ad;
1029     int                 adflags, err = AFP_OK;
1030
1031     /* try to open both at once */
1032     adflags = ADFLAGS_DF|ADFLAGS_HF;
1033     memset(&ad, 0, sizeof(ad));
1034     if ( ad_open( file, adflags, O_RDWR, 0, &ad ) < 0 ) {
1035           switch (errno) {
1036           case ENOENT:
1037             adflags = ADFLAGS_DF;
1038             /* that failed. now try to open just the data fork */
1039             memset(&ad, 0, sizeof(ad));
1040             if ( ad_open( file, adflags, O_RDWR, 0, &ad ) < 0 ) {
1041               switch (errno) {
1042               case ENOENT:
1043                 return AFPERR_NOOBJ;
1044               case EACCES:
1045                 return AFPERR_ACCESS;
1046               default:
1047                 return AFPERR_PARAM;
1048               }
1049             }
1050             break;
1051
1052           case EACCES:
1053             return( AFPERR_ACCESS );
1054           case EROFS:
1055             return AFPERR_VLOCK;
1056           default:
1057             return( AFPERR_PARAM );
1058           }
1059     }
1060
1061     if ((adflags & ADFLAGS_HF) &&
1062         (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR, 0, 0) < 0 )) {
1063       ad_close( &ad, adflags );
1064       return( AFPERR_BUSY );
1065     }
1066
1067     if (ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0 ) < 0) {
1068       err = AFPERR_BUSY;
1069       goto delete_unlock;
1070     }
1071
1072     if ( unlink( ad_path( file, ADFLAGS_HF )) < 0 ) {
1073         switch ( errno ) {
1074         case EPERM:
1075         case EACCES :
1076             err = AFPERR_ACCESS;
1077             goto delete_unlock;
1078         case EROFS:
1079             err = AFPERR_VLOCK;
1080             goto delete_unlock;
1081         case ENOENT :
1082             break;
1083         default :
1084             err = AFPERR_PARAM;
1085             goto delete_unlock;
1086         }
1087     }
1088
1089     if ( unlink( file ) < 0 ) {
1090         switch ( errno ) {
1091         case EPERM:
1092         case EACCES :
1093             err = AFPERR_ACCESS;
1094             break;
1095         case EROFS:
1096             err = AFPERR_VLOCK;
1097             break;
1098         case ENOENT :
1099             break;
1100         default :
1101             err = AFPERR_PARAM;
1102             break;
1103         }
1104     }
1105
1106 delete_unlock:
1107     if (adflags & ADFLAGS_HF)
1108       ad_tmplock(&ad, ADEID_RFORK, ADLOCK_CLR, 0, 0);
1109     ad_tmplock(&ad, ADEID_DFORK, ADLOCK_CLR, 0, 0);
1110     ad_close( &ad, adflags );
1111     return err;
1112 }
1113
1114
1115 #if AD_VERSION > AD_VERSION1
1116 /* return a file id */
1117 int afp_createid(obj, ibuf, ibuflen, rbuf, rbuflen )
1118     AFPObj      *obj;
1119     char        *ibuf, *rbuf;
1120     int         ibuflen, *rbuflen;
1121 {
1122     struct stat         st;
1123     struct adouble      ad;
1124     struct vol          *vol;
1125     struct dir          *dir;
1126     char                *path, *upath;
1127     int                 len;
1128     cnid_t              did, id;
1129     u_short             vid;
1130     
1131     syslog(LOG_INFO, "afp_createid");
1132     *rbuflen = 0;
1133     ibuf += 2;
1134
1135     memcpy(&vid, ibuf, sizeof(vid));
1136     ibuf += sizeof(vid);
1137
1138     if (( vol = getvolbyvid( vid )) == NULL ) {
1139         return( AFPERR_PARAM);
1140     }
1141
1142     if (vol->v_flags & AFPVOL_RO)
1143         return AFPERR_VLOCK;
1144
1145     memcpy(&did, ibuf, sizeof( did ));
1146     ibuf += sizeof(did);
1147
1148     if (( dir = dirsearch( vol, did )) == NULL ) {
1149         return( AFPERR_PARAM );
1150     }
1151
1152     if (( path = cname( vol, dir, &ibuf )) == NULL ) {
1153         return( AFPERR_PARAM );
1154     }
1155
1156     if ( *path == '\0' ) {
1157         return( AFPERR_BADTYPE );
1158     }
1159
1160     upath = mtoupath(vol, path);
1161     if (stat(upath, &st) < 0) {
1162       switch (errno) {
1163       case EPERM:
1164       case EACCES:
1165         return AFPERR_ACCESS;
1166       case ENOENT:
1167         return AFPERR_NOOBJ;
1168       default:
1169         return AFPERR_PARAM;
1170       }
1171     }
1172
1173     if (id = cnid_lookup(vol->v_db, &st, did, upath, len = strlen(upath))) {
1174       memcpy(rbuf, &id, sizeof(id));
1175       *rbuflen = sizeof(id);
1176       return AFPERR_EXISTID;
1177     }
1178
1179     memset(&ad, 0, sizeof(ad));
1180     if (ad_open( upath, ADFLAGS_HF, O_RDONLY, 0, &ad ) < 0)
1181       id = 0;
1182     else {
1183       memcpy(&id, ad_entry(&ad, ADEID_DID), sizeof(id));
1184       ad_close(upath, ADFLAGS_HF);
1185     }
1186
1187     if (id = cnid_add(vol->v_db, &st, did, upath, len, id)) {
1188       memcpy(rbuf, &id, sizeof(id));
1189       *rbuflen = sizeof(id);
1190       return AFP_OK;
1191     }
1192
1193     switch (errno) {
1194     case EROFS:
1195       return AFPERR_VLOCK;
1196       break;
1197     case EPERM:
1198     case EACCES:
1199       return AFPERR_ACCESS;
1200       break;
1201     default:
1202       syslog(LOG_ERR, "afp_createid: cnid_add: %m");
1203       return AFPERR_PARAM;
1204     }
1205 }
1206
1207 /* resolve a file id */
1208 int afp_resolveid(obj, ibuf, ibuflen, rbuf, rbuflen )
1209     AFPObj      *obj;
1210     char        *ibuf, *rbuf;
1211     int         ibuflen, *rbuflen;
1212 {
1213     struct stat         st;
1214     struct vol          *vol;
1215     struct dir          *dir;
1216     char                *upath;
1217     int                 err, buflen;
1218     cnid_t              id;
1219     u_int16_t           vid, bitmap;
1220     
1221     syslog(LOG_INFO, "afp_resolveid");
1222     *rbuflen = 0;
1223     ibuf += 2;
1224
1225     memcpy(&vid, ibuf, sizeof(vid));
1226     ibuf += sizeof(vid);
1227
1228     if (( vol = getvolbyvid( vid )) == NULL ) {
1229         return( AFPERR_PARAM);
1230     }
1231
1232     memcpy(&id, ibuf, sizeof( id ));
1233     ibuf += sizeof(id);
1234
1235     if ((upath = cnid_resolve(vol->v_db, &id)) == NULL) {
1236       return AFPERR_BADID;
1237     }
1238
1239     if (( dir = dirsearch( vol, id )) == NULL ) {
1240       return( AFPERR_PARAM );
1241     }
1242
1243     if ((movecwd(vol, dir) < 0) || (stat(upath, &st) < 0)) {
1244       switch (errno) {
1245       case EACCES:
1246       case EPERM:
1247         return AFPERR_ACCESS;
1248       case ENOENT:
1249         return AFPERR_NOID;
1250       default:
1251         return AFPERR_PARAM;
1252       }
1253     }
1254
1255     /* directories are bad */
1256     if (S_ISDIR(st.st_mode))
1257       return AFPERR_BADTYPE;
1258
1259     memcpy(&bitmap, ibuf, sizeof(bitmap));
1260     bitmap = ntohs( bitmap );
1261
1262     if ((err = getfilparams(vol, bitmap, utompath(vol, upath), curdir, &st,
1263                            rbuf + sizeof(bitmap), &buflen)) != AFP_OK)
1264       return err;
1265
1266     *rbuflen = buflen + sizeof(bitmap);
1267     memcpy(rbuf, ibuf, sizeof(bitmap));
1268     return AFP_OK;
1269 }
1270
1271 int afp_deleteid(obj, ibuf, ibuflen, rbuf, rbuflen )
1272     AFPObj      *obj;
1273     char        *ibuf, *rbuf;
1274     int         ibuflen, *rbuflen;
1275 {
1276     struct stat         st;
1277     struct vol          *vol;
1278     struct dir          *dir;
1279     char                *upath;
1280     int                 err;
1281     cnid_t              id;
1282     u_short             vid;
1283     
1284     syslog(LOG_INFO, "afp_deleteid");
1285     *rbuflen = 0;
1286     ibuf += 2;
1287
1288     memcpy(&vid, ibuf, sizeof(vid));
1289     ibuf += sizeof(vid);
1290
1291     if (( vol = getvolbyvid( vid )) == NULL ) {
1292         return( AFPERR_PARAM);
1293     }
1294
1295     if (vol->v_flags & AFPVOL_RO)
1296         return AFPERR_VLOCK;
1297
1298     memcpy(&id, ibuf, sizeof( id ));
1299     ibuf += sizeof(id);
1300
1301     if ((upath = cnid_resolve(vol->v_db, &id)) == NULL) {
1302       return AFPERR_NOID;
1303     }
1304
1305     if (( dir = dirsearch( vol, id )) == NULL ) {
1306       return( AFPERR_PARAM );
1307     }
1308
1309     err = AFP_OK;
1310     if ((movecwd(vol, dir) < 0) || (stat(upath, &st) < 0)) {
1311       switch (errno) {
1312       case EACCES:
1313       case EPERM:
1314         return AFPERR_ACCESS;
1315       case ENOENT:
1316         /* still try to delete the id */
1317         err = AFPERR_NOOBJ;
1318         break;
1319       default:
1320         return AFPERR_PARAM;
1321       }
1322     }
1323
1324     /* directories are bad */
1325     if (S_ISDIR(st.st_mode))
1326       return AFPERR_BADTYPE;
1327
1328     if (cnid_delete(vol->v_db, id)) {
1329       switch (errno) {
1330       case EROFS:
1331         return AFPERR_VLOCK;
1332       case EPERM:
1333       case EACCES:
1334         return AFPERR_ACCESS;
1335       default:
1336         return AFPERR_PARAM;
1337       }
1338     }
1339
1340     return err;
1341 }
1342 #endif
1343
1344 #define APPLETEMP ".AppleTempXXXXXX"
1345 int afp_exchangefiles(obj, ibuf, ibuflen, rbuf, rbuflen )
1346     AFPObj      *obj;
1347     char        *ibuf, *rbuf;
1348     int         ibuflen, *rbuflen;
1349 {
1350     struct stat         srcst, destst;
1351     struct vol          *vol;
1352     struct dir          *dir, *sdir;
1353     char                *spath, temp[17], *path, *p;
1354     char                *supath, *upath;
1355     int                 err;
1356 #if AD_VERSION > AD_VERSION1
1357     int                 slen, dlen;
1358 #endif
1359     cnid_t              sid, did;
1360     u_int16_t           vid;
1361     
1362     syslog(LOG_INFO, "afp_exchangefiles");
1363     *rbuflen = 0;
1364     ibuf += 2;
1365
1366     memcpy(&vid, ibuf, sizeof(vid));
1367     ibuf += sizeof(vid);
1368
1369     if (( vol = getvolbyvid( vid )) == NULL ) {
1370         return( AFPERR_PARAM);
1371     }
1372
1373     if (vol->v_flags & AFPVOL_RO)
1374         return AFPERR_VLOCK;
1375
1376     /* source and destination dids */
1377     memcpy(&sid, ibuf, sizeof(sid));
1378     ibuf += sizeof(sid);
1379     memcpy(&did, ibuf, sizeof(did));
1380     ibuf += sizeof(did);
1381
1382     /* source file */
1383     if ((dir = dirsearch( vol, sid )) == NULL ) {
1384         return( AFPERR_PARAM );
1385     }
1386
1387     if (( path = cname( vol, dir, &ibuf )) == NULL ) {
1388         return( AFPERR_PARAM );
1389     }
1390
1391     if ( *path == '\0' ) {
1392         return( AFPERR_BADTYPE );
1393     }
1394
1395     upath = mtoupath(vol, path);
1396     if (stat(upath, &srcst) < 0) {
1397       switch (errno) {
1398       case ENOENT:
1399         return AFPERR_NOID;
1400       case EPERM:
1401       case EACCES:
1402         return AFPERR_ACCESS;
1403       default:
1404         return AFPERR_PARAM;
1405       }
1406     }
1407
1408     /* save some stuff */
1409     sdir = curdir;
1410     spath = obj->oldtmp;
1411     supath = obj->newtmp;
1412     strcpy(spath, path);
1413     strcpy(supath, upath); /* this is for the cnid changing */
1414     p = ctoupath( vol, sdir, spath);
1415
1416     /* look for the source cnid. if it doesn't exist, don't worry about
1417      * it. */
1418 #if AD_VERSION > AD_VERSION1
1419     sid = cnid_lookup(vol->v_db, &srcst, sdir->d_did, supath, 
1420                       slen = strlen(supath));
1421 #endif
1422
1423     if (( dir = dirsearch( vol, did )) == NULL ) {
1424         return( AFPERR_PARAM );
1425     }
1426
1427     if (( path = cname( vol, dir, &ibuf )) == NULL ) {
1428         return( AFPERR_PARAM );
1429     }
1430
1431     if ( *path == '\0' ) {
1432         return( AFPERR_BADTYPE );
1433     }
1434
1435     /* FPExchangeFiles is the only call that can return the SameObj
1436      * error */
1437     if ((curdir == sdir) && strcmp(spath, path) == 0)
1438       return AFPERR_SAMEOBJ;
1439
1440     upath = mtoupath(vol, path);
1441     if (stat(upath, &destst) < 0) {
1442       switch (errno) {
1443       case ENOENT:
1444         return AFPERR_NOID;
1445       case EPERM:
1446       case EACCES:
1447         return AFPERR_ACCESS;
1448       default:
1449         return AFPERR_PARAM;
1450       }
1451     }
1452
1453 #if AD_VERSION > AD_VERSION1
1454     /* look for destination id. */
1455     did = cnid_lookup(vol->v_db, &destst, curdir->d_did, upath, 
1456                       dlen = strlen(upath));
1457 #endif
1458
1459     /* construct a temp name. 
1460      * NOTE: the temp file will be in the dest file's directory. it
1461      * will also be inaccessible from AFP. */
1462     memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
1463     if (!mktemp(temp))
1464       return AFPERR_MISC;
1465
1466     /* now, quickly rename the file. we error if we can't. */
1467     if ((err = renamefile(p, temp, temp, vol_noadouble(vol))) < 0)
1468       goto err_exchangefile;
1469     of_rename(vol, sdir, spath, curdir, temp);
1470
1471     /* rename destination to source */
1472     if ((err = renamefile(path, p, spath, vol_noadouble(vol))) < 0) 
1473       goto err_src_to_tmp;
1474     of_rename(vol, curdir, path, sdir, spath);
1475
1476     /* rename temp to destination */
1477     if ((err = renamefile(temp, upath, path, vol_noadouble(vol))) < 0) 
1478       goto err_dest_to_src;
1479     of_rename(vol, curdir, temp, curdir, path);
1480     
1481 #if AD_VERSION > AD_VERSION1
1482     /* id's need switching. src -> dest and dest -> src. */
1483     if (sid && (cnid_update(vol->v_db, sid, &destst, curdir->d_did, 
1484                             upath, dlen) < 0)) {
1485       switch (errno) {
1486       case EPERM:
1487       case EACCES:
1488         err = AFPERR_ACCESS;
1489       default:
1490         err = AFPERR_PARAM;
1491       }
1492       goto err_temp_to_dest;
1493     }
1494
1495     if (did && (cnid_update(vol->v_db, did, &srcst, sdir->d_did,
1496                             supath, slen) < 0)) {
1497       switch (errno) {
1498       case EPERM:
1499       case EACCES:
1500         err = AFPERR_ACCESS;
1501       default:
1502         err = AFPERR_PARAM;
1503       }
1504
1505       if (sid)
1506         cnid_update(vol->v_db, sid, &srcst, sdir->d_did, supath, slen);
1507       goto err_temp_to_dest;
1508     }
1509 #endif
1510     return AFP_OK;
1511
1512
1513     /* all this stuff is so that we can unwind a failed operation 
1514      * properly. */
1515 err_temp_to_dest:
1516     /* rename dest to temp */
1517     renamefile(upath, temp, temp, vol_noadouble(vol));
1518     of_rename(vol, curdir, upath, curdir, temp);
1519
1520 err_dest_to_src:
1521     /* rename source back to dest */
1522     renamefile(p, upath, path, vol_noadouble(vol));
1523     of_rename(vol, sdir, spath, curdir, path);
1524
1525 err_src_to_tmp:
1526     /* rename temp back to source */
1527     renamefile(temp, p, spath, vol_noadouble(vol));
1528     of_rename(vol, curdir, temp, sdir, spath);
1529
1530 err_exchangefile:
1531     return err;
1532 }