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