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