2 * $Id: file.c,v 1.102 2006-09-19 23:00:49 didg Exp $
4 * Copyright (c) 1990,1993 Regents of The University of Michigan.
5 * All Rights Reserved. See COPYRIGHT.
10 #endif /* HAVE_CONFIG_H */
18 #else /* STDC_HEADERS */
22 #endif /* HAVE_STRCHR */
23 char *strchr (), *strrchr ();
26 #define memcpy(d,s,n) bcopy ((s), (d), (n))
27 #define memmove(d,s,n) bcopy ((s), (d), (n))
28 #endif /* ! HAVE_MEMCPY */
29 #endif /* STDC_HEADERS */
31 #include <atalk/adouble.h>
36 #include <atalk/logger.h>
37 #include <sys/param.h>
40 #include <atalk/afp.h>
41 #include <atalk/util.h>
42 #include <atalk/cnid.h>
43 #include "directory.h"
52 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
53 * field bytes subfield bytes
56 * ioFlFndrInfo 16 -> type 4 type field
57 * creator 4 creator field
58 * flags 2 finder flags:
60 * location 4 location in window
61 * folder 2 window that contains file
63 * ioFlXFndrInfo 16 -> iconID 2 icon id
65 * script 1 script system
67 * commentID 2 comment id
68 * putawayID 4 home directory id
71 const u_char ufinderi[ADEDLEN_FINDERI] = {
72 0, 0, 0, 0, 0, 0, 0, 0,
73 1, 0, 0, 0, 0, 0, 0, 0,
74 0, 0, 0, 0, 0, 0, 0, 0,
75 0, 0, 0, 0, 0, 0, 0, 0
78 static const u_char old_ufinderi[] = {
79 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
82 /* ----------------------
84 static int default_type(void *finder)
86 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
91 /* FIXME path : unix or mac name ? (for now it's unix name ) */
92 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data)
95 void *ad_finder = NULL;
99 ad_finder = ad_entry(adp, ADEID_FINDERI);
102 memcpy(data, ad_finder, ADEDLEN_FINDERI);
104 if (default_type(ad_finder))
108 memcpy(data, ufinderi, ADEDLEN_FINDERI);
110 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
113 ashort = htons(FINDERINFO_INVISIBLE);
114 memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
117 /** Only enter if no appledouble information and no finder information found. */
118 if (chk_ext && (em = getextmap( upath ))) {
119 memcpy(data, em->em_type, sizeof( em->em_type ));
120 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
125 /* ---------------------
127 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, u_int32_t utf8)
132 aint = strlen( name );
136 if (utf8_encoding()) {
137 /* but name is an utf8 mac name */
140 /* global static variable... */
142 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
151 if (aint > MACFILELEN)
158 if (aint > 255) /* FIXME safeguard, anyway if no ascii char it's game over*/
161 utf8 = vol->v_mac?htonl(vol->v_mac->kTextEncoding):0; /* htonl(utf8) */
162 memcpy(data, &utf8, sizeof(utf8));
163 data += sizeof(utf8);
166 memcpy(data, &temp, sizeof(temp));
167 data += sizeof(temp);
170 memcpy( data, src, aint );
180 * FIXME: PDINFO is UTF8 and doesn't need adp
182 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
183 (1 << FILPBIT_CDATE) |\
184 (1 << FILPBIT_MDATE) |\
185 (1 << FILPBIT_BDATE) |\
186 (1 << FILPBIT_FINFO) |\
187 (1 << FILPBIT_RFLEN) |\
188 (1 << FILPBIT_EXTRFLEN) |\
189 (1 << FILPBIT_PDINFO) |\
190 (1 << FILPBIT_UNIXPR)))
192 /* -------------------------- */
193 u_int32_t get_id(struct vol *vol, struct adouble *adp, const struct stat *st,
194 const cnid_t did, char *upath, const int len)
198 #if AD_VERSION > AD_VERSION1
200 if ((aint = ad_getid(adp, st->st_dev, st->st_ino, did, vol->v_stamp))) {
205 if (vol->v_cdb != NULL) {
206 aint = cnid_add(vol->v_cdb, st, did, upath, len, aint);
207 /* Throw errors if cnid_add fails. */
208 if (aint == CNID_INVALID) {
210 case CNID_ERR_CLOSE: /* the db is closed */
213 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
214 afp_errno = AFPERR_PARAM;
217 afp_errno = AFPERR_PARAM;
220 afp_errno = AFPERR_MISC;
224 #if AD_VERSION > AD_VERSION1
226 /* update the ressource fork
227 * for a folder adp is always null
229 if (ad_setid(adp, st->st_dev, st->st_ino, aint, did, vol->v_stamp)) {
230 ad_flush(adp, ADFLAGS_HF);
238 /* -------------------------- */
239 int getmetadata(struct vol *vol,
241 struct path *path, struct dir *dir,
242 char *buf, int *buflen, struct adouble *adp, int attrbits )
244 char *data, *l_nameoff = NULL, *upath;
245 char *utf_nameoff = NULL;
250 u_char achar, fdType[4];
256 LOG(log_info, logtype_afpd, "begin getmetadata:");
259 upath = path->u_name;
264 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
265 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
266 || (bitmap & (1 << FILPBIT_FNUM))) {
268 id = get_id(vol, adp, st, dir->d_did, upath, strlen(upath));
274 path->m_name = utompath(vol, upath, id, utf8_encoding());
277 while ( bitmap != 0 ) {
278 while (( bitmap & 1 ) == 0 ) {
286 ad_getattr(adp, &ashort);
287 } else if (vol_inv_dots(vol) && *upath == '.') {
288 ashort = htons(ATTRBIT_INVISIBLE);
292 /* FIXME do we want a visual clue if the file is read only
295 accessmode( ".", &ma, dir , NULL);
296 if ((ma.ma_user & AR_UWRITE)) {
297 accessmode( upath, &ma, dir , st);
298 if (!(ma.ma_user & AR_UWRITE)) {
299 attrbits |= ATTRBIT_NOWRITE;
304 ashort = htons(ntohs(ashort) | attrbits);
305 memcpy(data, &ashort, sizeof( ashort ));
306 data += sizeof( ashort );
310 memcpy(data, &dir->d_did, sizeof( u_int32_t ));
311 data += sizeof( u_int32_t );
315 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
316 aint = AD_DATE_FROM_UNIX(st->st_mtime);
317 memcpy(data, &aint, sizeof( aint ));
318 data += sizeof( aint );
322 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
323 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
324 aint = AD_DATE_FROM_UNIX(st->st_mtime);
327 aint = AD_DATE_FROM_UNIX(st->st_mtime);
329 memcpy(data, &aint, sizeof( int ));
330 data += sizeof( int );
334 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
335 aint = AD_DATE_START;
336 memcpy(data, &aint, sizeof( int ));
337 data += sizeof( int );
341 get_finderinfo(vol, upath, adp, (char *)data);
342 data += ADEDLEN_FINDERI;
347 data += sizeof( u_int16_t );
351 memset(data, 0, sizeof(u_int16_t));
352 data += sizeof( u_int16_t );
356 memcpy(data, &id, sizeof( id ));
357 data += sizeof( id );
361 if (st->st_size > 0xffffffff)
364 aint = htonl( st->st_size );
365 memcpy(data, &aint, sizeof( aint ));
366 data += sizeof( aint );
371 if (adp->ad_rlen > 0xffffffff)
374 aint = htonl( adp->ad_rlen);
378 memcpy(data, &aint, sizeof( aint ));
379 data += sizeof( aint );
382 /* Current client needs ProDOS info block for this file.
383 Use simple heuristic and let the Mac "type" string tell
384 us what the PD file code should be. Everything gets a
385 subtype of 0x0000 unless the original value was hashed
386 to "pXYZ" when we created it. See IA, Ver 2.
387 <shirsch@adelphia.net> */
388 case FILPBIT_PDINFO :
389 if (afp_version >= 30) { /* UTF8 name */
390 utf8 = kTextEncodingUTF8;
392 data += sizeof( u_int16_t );
394 memcpy(data, &aint, sizeof( aint ));
395 data += sizeof( aint );
399 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
401 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
405 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
409 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
413 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
417 else if ( fdType[0] == 'p' ) {
419 ashort = (fdType[2] * 256) + fdType[3];
433 memcpy(data, &ashort, sizeof( ashort ));
434 data += sizeof( ashort );
435 memset(data, 0, sizeof( ashort ));
436 data += sizeof( ashort );
439 case FILPBIT_EXTDFLEN:
440 aint = htonl(st->st_size >> 32);
441 memcpy(data, &aint, sizeof( aint ));
442 data += sizeof( aint );
443 aint = htonl(st->st_size);
444 memcpy(data, &aint, sizeof( aint ));
445 data += sizeof( aint );
447 case FILPBIT_EXTRFLEN:
450 aint = htonl(adp->ad_rlen >> 32);
451 memcpy(data, &aint, sizeof( aint ));
452 data += sizeof( aint );
454 aint = htonl(adp->ad_rlen);
455 memcpy(data, &aint, sizeof( aint ));
456 data += sizeof( aint );
458 case FILPBIT_UNIXPR :
459 /* accessmode may change st_mode with ACLs */
460 accessmode( upath, &ma, dir , st);
462 aint = htonl(st->st_uid);
463 memcpy( data, &aint, sizeof( aint ));
464 data += sizeof( aint );
465 aint = htonl(st->st_gid);
466 memcpy( data, &aint, sizeof( aint ));
467 data += sizeof( aint );
470 type == slnk indicates an OSX style symlink,
471 we have to add S_IFLNK to the mode, otherwise
472 10.3 clients freak out. */
476 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
477 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
483 memcpy( data, &aint, sizeof( aint ));
484 data += sizeof( aint );
486 *data++ = ma.ma_user;
487 *data++ = ma.ma_world;
488 *data++ = ma.ma_group;
489 *data++ = ma.ma_owner;
493 return( AFPERR_BITMAP );
499 ashort = htons( data - buf );
500 memcpy(l_nameoff, &ashort, sizeof( ashort ));
501 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
504 ashort = htons( data - buf );
505 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
506 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
508 *buflen = data - buf;
512 /* ----------------------- */
513 int getfilparams(struct vol *vol,
515 struct path *path, struct dir *dir,
516 char *buf, int *buflen )
518 struct adouble ad, *adp;
521 u_int16_t attrbits = 0;
526 LOG(log_info, logtype_default, "begin getfilparams:");
529 opened = PARAM_NEED_ADP(bitmap);
532 upath = path->u_name;
533 if ((of = of_findname(path))) {
535 attrbits = ((of->of_ad->ad_df.adf_refcount > 0) ? ATTRBIT_DOPEN : 0);
536 attrbits |= ((of->of_ad->ad_hf.adf_refcount > of->of_ad->ad_df.adf_refcount)? ATTRBIT_ROPEN : 0);
538 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
542 if ( ad_metadata( upath, 0, adp) < 0 ) {
545 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
546 upath, strerror(errno));
547 return AFPERR_ACCESS;
549 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
559 we need to check if the file is open by another process.
560 it's slow so we only do it if we have to:
561 - bitmap is requested.
562 - we don't already have the answer!
564 if ((bitmap & (1 << FILPBIT_ATTR))) {
565 if (!(attrbits & ATTRBIT_ROPEN)) {
566 attrbits |= ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_OPEN_RD) > 0? ATTRBIT_ROPEN : 0;
567 attrbits |= ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_OPEN_WR) > 0? ATTRBIT_ROPEN : 0;
569 if (!(attrbits & ATTRBIT_DOPEN)) {
570 attrbits |= ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_OPEN_RD) > 0? ATTRBIT_DOPEN : 0;
571 attrbits |= ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_OPEN_WR) > 0? ATTRBIT_DOPEN : 0;
576 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp, attrbits);
578 ad_close_metadata( adp);
581 LOG(log_info, logtype_afpd, "end getfilparams:");
587 /* ----------------------------- */
588 int afp_createfile(obj, ibuf, ibuflen, rbuf, rbuflen )
590 char *ibuf, *rbuf _U_;
591 int ibuflen _U_, *rbuflen;
593 struct adouble ad, *adp;
596 struct ofork *of = NULL;
598 int creatf, did, openf, retvalue = AFP_OK;
604 LOG(log_info, logtype_afpd, "begin afp_createfile:");
609 creatf = (unsigned char) *ibuf++;
611 memcpy(&vid, ibuf, sizeof( vid ));
612 ibuf += sizeof( vid );
614 if (NULL == ( vol = getvolbyvid( vid )) ) {
615 return( AFPERR_PARAM );
618 if (vol->v_flags & AFPVOL_RO)
621 memcpy(&did, ibuf, sizeof( did));
622 ibuf += sizeof( did );
624 if (NULL == ( dir = dirlookup( vol, did )) ) {
628 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
629 return get_afp_errno(AFPERR_PARAM);
632 if ( *s_path->m_name == '\0' ) {
633 return( AFPERR_BADTYPE );
636 upath = s_path->u_name;
637 if (0 != (ret = check_name(vol, upath)))
640 /* if upath is deleted we already in trouble anyway */
641 if ((of = of_findname(s_path))) {
644 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
648 /* on a hard create, fail if file exists and is open */
651 openf = O_RDWR|O_CREAT|O_TRUNC;
653 /* on a soft create, if the file is open then ad_open won't fail
654 because open syscall is not called
659 openf = O_RDWR|O_CREAT|O_EXCL;
662 if ( ad_open( upath, vol_noadouble(vol)|ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF,
663 openf, 0666, adp) < 0 ) {
667 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
668 return ( AFPERR_NOOBJ );
670 return( AFPERR_EXIST );
672 return( AFPERR_ACCESS );
675 return( AFPERR_DFULL );
677 return( AFPERR_PARAM );
680 if ( ad_hfileno( adp ) == -1 ) {
681 /* on noadouble volumes, just creating the data fork is ok */
682 if (vol_noadouble(vol)) {
683 ad_close( adp, ADFLAGS_DF );
684 goto createfile_done;
686 /* FIXME with hard create on an existing file, we already
687 * corrupted the data file.
689 netatalk_unlink( upath );
690 ad_close( adp, ADFLAGS_DF );
691 return AFPERR_ACCESS;
694 path = s_path->m_name;
695 ad_setname(adp, path);
696 ad_flush( adp, ADFLAGS_DF|ADFLAGS_HF );
697 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
703 if (vol->v_flags & AFPVOL_DROPBOX) {
704 retvalue = matchfile2dirperms(upath, vol, did);
706 #endif /* DROPKLUDGE */
708 setvoltime(obj, vol );
711 LOG(log_info, logtype_afpd, "end afp_createfile");
717 int afp_setfilparams(obj, ibuf, ibuflen, rbuf, rbuflen )
719 char *ibuf, *rbuf _U_;
720 int ibuflen _U_, *rbuflen;
726 u_int16_t vid, bitmap;
729 LOG(log_info, logtype_afpd, "begin afp_setfilparams:");
735 memcpy(&vid, ibuf, sizeof( vid ));
736 ibuf += sizeof( vid );
737 if (NULL == ( vol = getvolbyvid( vid )) ) {
738 return( AFPERR_PARAM );
741 if (vol->v_flags & AFPVOL_RO)
744 memcpy(&did, ibuf, sizeof( did ));
745 ibuf += sizeof( did );
746 if (NULL == ( dir = dirlookup( vol, did )) ) {
747 return afp_errno; /* was AFPERR_NOOBJ */
750 memcpy(&bitmap, ibuf, sizeof( bitmap ));
751 bitmap = ntohs( bitmap );
752 ibuf += sizeof( bitmap );
754 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
755 return get_afp_errno(AFPERR_PARAM);
758 if (path_isadir(s_path)) {
759 return( AFPERR_BADTYPE ); /* it's a directory */
762 if ( s_path->st_errno != 0 ) {
763 return( AFPERR_NOOBJ );
766 if ((u_long)ibuf & 1 ) {
770 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
771 setvoltime(obj, vol );
775 LOG(log_info, logtype_afpd, "end afp_setfilparams:");
782 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
785 extern struct path Cur_Path;
787 int setfilparams(struct vol *vol,
788 struct path *path, u_int16_t f_bitmap, char *buf )
790 struct adouble ad, *adp;
792 int bit, isad = 1, err = AFP_OK;
794 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
795 u_int16_t ashort, bshort;
798 u_int16_t upriv_bit = 0;
802 int change_mdate = 0;
803 int change_parent_mdate = 0;
808 u_int16_t bitmap = f_bitmap;
809 u_int32_t cdate,bdate;
810 u_char finder_buf[32];
813 LOG(log_info, logtype_afpd, "begin setfilparams:");
816 upath = path->u_name;
817 adp = of_ad(vol, path, &ad);
820 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
821 return AFPERR_ACCESS;
824 /* with unix priv maybe we have to change adouble file priv first */
826 while ( bitmap != 0 ) {
827 while (( bitmap & 1 ) == 0 ) {
834 memcpy(&ashort, buf, sizeof( ashort ));
835 buf += sizeof( ashort );
839 memcpy(&cdate, buf, sizeof(cdate));
840 buf += sizeof( cdate );
843 memcpy(&newdate, buf, sizeof( newdate ));
844 buf += sizeof( newdate );
848 memcpy(&bdate, buf, sizeof( bdate));
849 buf += sizeof( bdate );
853 memcpy(finder_buf, buf, 32 );
856 case FILPBIT_UNIXPR :
857 if (!vol_unix_priv(vol)) {
858 /* this volume doesn't use unix priv */
864 change_parent_mdate = 1;
866 memcpy( &aint, buf, sizeof( aint ));
867 f_uid = ntohl (aint);
868 buf += sizeof( aint );
869 memcpy( &aint, buf, sizeof( aint ));
870 f_gid = ntohl (aint);
871 buf += sizeof( aint );
872 setfilowner(vol, f_uid, f_gid, path);
874 memcpy( &upriv, buf, sizeof( upriv ));
875 buf += sizeof( upriv );
876 upriv = ntohl (upriv);
877 if ((upriv & S_IWUSR)) {
878 setfilunixmode(vol, path, upriv);
885 case FILPBIT_PDINFO :
886 if (afp_version < 30) { /* else it's UTF8 name */
889 /* Keep special case to support crlf translations */
890 if ((unsigned int) achar == 0x04) {
891 fdType = (u_char *)"TEXT";
894 xyy[0] = ( u_char ) 'p';
905 /* break while loop */
914 /* second try with adouble open
916 if ( ad_open_metadata( upath, vol_noadouble(vol), O_CREAT, adp) < 0) {
917 /* for some things, we don't need an adouble header */
918 if (f_bitmap & ~(1<<FILPBIT_MDATE)) {
919 return vol_noadouble(vol) ? AFP_OK : AFPERR_ACCESS;
922 } else if ((ad_get_HF_flags( adp ) & O_CREAT) ) {
923 ad_setname(adp, path->m_name);
928 while ( bitmap != 0 ) {
929 while (( bitmap & 1 ) == 0 ) {
936 ad_getattr(adp, &bshort);
937 if ((bshort & htons(ATTRBIT_INVISIBLE)) !=
938 (ashort & htons(ATTRBIT_INVISIBLE) & htons(ATTRBIT_SETCLR)) )
939 change_parent_mdate = 1;
940 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
941 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
945 ad_setattr(adp, bshort);
948 ad_setdate(adp, AD_DATE_CREATE, cdate);
953 ad_setdate(adp, AD_DATE_BACKUP, bdate);
956 if (default_type( ad_entry( adp, ADEID_FINDERI ))
958 ((em = getextmap( path->m_name )) &&
959 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
960 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
961 || ((em = getdefextmap()) &&
962 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
963 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
965 memcpy(finder_buf, ufinderi, 8 );
967 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
969 case FILPBIT_UNIXPR :
971 setfilunixmode(vol, path, upriv);
974 case FILPBIT_PDINFO :
975 if (afp_version < 30) { /* else it's UTF8 name */
976 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
977 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
983 goto setfilparam_done;
990 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
991 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
995 ad_setdate(adp, AD_DATE_MODIFY, newdate);
996 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1001 ad_flush_metadata( adp);
1002 ad_close_metadata( adp);
1006 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1007 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1008 bitmap = 1<<FILPBIT_MDATE;
1009 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1013 LOG(log_info, logtype_afpd, "end setfilparams:");
1019 * renamefile and copyfile take the old and new unix pathnames
1020 * and the new mac name.
1022 * src the source path
1023 * dst the dest filename in current dir
1024 * newname the dest mac name
1025 * adp adouble struct of src file, if open, or & zeroed one
1028 int renamefile(vol, src, dst, newname, adp )
1029 const struct vol *vol;
1030 char *src, *dst, *newname;
1031 struct adouble *adp;
1036 LOG(log_info, logtype_afpd, "begin renamefile:");
1039 if ( unix_rename( src, dst ) < 0 ) {
1042 return( AFPERR_NOOBJ );
1045 return( AFPERR_ACCESS );
1047 return AFPERR_VLOCK;
1048 case EXDEV : /* Cross device move -- try copy */
1049 /* NOTE: with open file it's an error because after the copy we will
1050 * get two files, it's fixable for our process (eg reopen the new file, get the
1051 * locks, and so on. But it doesn't solve the case with a second process
1053 if (adp->ad_df.adf_refcount || adp->ad_hf.adf_refcount) {
1054 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1055 return AFPERR_OLOCK; /* little lie */
1057 if (AFP_OK != ( rc = copyfile(vol, vol, src, dst, newname, NULL )) ) {
1058 /* on error copyfile delete dest */
1061 return deletefile(vol, src, 0);
1063 return( AFPERR_PARAM );
1067 if (vol->vfs->rf_renamefile(vol, src, dst) < 0 ) {
1071 /* try to undo the data fork rename,
1072 * we know we are on the same device
1075 unix_rename( dst, src );
1076 /* return the first error */
1079 return AFPERR_NOOBJ;
1082 return AFPERR_ACCESS ;
1084 return AFPERR_VLOCK;
1086 return AFPERR_PARAM ;
1091 /* don't care if we can't open the newly renamed ressource fork
1093 if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
1094 ad_setname(adp, newname);
1095 ad_flush( adp, ADFLAGS_HF );
1096 ad_close( adp, ADFLAGS_HF );
1099 LOG(log_info, logtype_afpd, "end renamefile:");
1106 convert a Mac long name to an utf8 name,
1108 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1112 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1118 /* ---------------- */
1119 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1126 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1132 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1133 if (afp_version >= 30) {
1134 /* convert it to UTF8
1136 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1140 strncpy( newname, ibuf, plen );
1141 newname[ plen ] = '\0';
1143 if (strlen(newname) != plen) {
1144 /* there's \0 in newname, e.g. it's a pathname not
1152 memcpy(&hint, ibuf, sizeof(hint));
1153 ibuf += sizeof(hint);
1155 memcpy(&len16, ibuf, sizeof(len16));
1156 ibuf += sizeof(len16);
1157 plen = ntohs(len16);
1160 if (plen > AFPOBJ_TMPSIZ) {
1163 strncpy( newname, ibuf, plen );
1164 newname[ plen ] = '\0';
1165 if (strlen(newname) != plen) {
1174 /* -----------------------------------
1176 int afp_copyfile(obj, ibuf, ibuflen, rbuf, rbuflen )
1178 char *ibuf, *rbuf _U_;
1179 int ibuflen _U_, *rbuflen;
1181 struct vol *s_vol, *d_vol;
1183 char *newname, *p, *upath;
1184 struct path *s_path;
1185 u_int32_t sdid, ddid;
1186 int err, retvalue = AFP_OK;
1187 u_int16_t svid, dvid;
1189 struct adouble ad, *adp;
1193 LOG(log_info, logtype_afpd, "begin afp_copyfile:");
1199 memcpy(&svid, ibuf, sizeof( svid ));
1200 ibuf += sizeof( svid );
1201 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1202 return( AFPERR_PARAM );
1205 memcpy(&sdid, ibuf, sizeof( sdid ));
1206 ibuf += sizeof( sdid );
1207 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1211 memcpy(&dvid, ibuf, sizeof( dvid ));
1212 ibuf += sizeof( dvid );
1213 memcpy(&ddid, ibuf, sizeof( ddid ));
1214 ibuf += sizeof( ddid );
1216 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1217 return get_afp_errno(AFPERR_PARAM);
1219 if ( path_isadir(s_path) ) {
1220 return( AFPERR_BADTYPE );
1223 /* don't allow copies when the file is open.
1224 * XXX: the spec only calls for read/deny write access.
1225 * however, copyfile doesn't have any of that info,
1226 * and locks need to stay coherent. as a result,
1227 * we just balk if the file is opened already. */
1229 adp = of_ad(s_vol, s_path, &ad);
1231 if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1232 return AFPERR_DENYCONF;
1234 denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1235 getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1236 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1238 return AFPERR_DENYCONF;
1241 newname = obj->newtmp;
1242 strcpy( newname, s_path->m_name );
1244 p = ctoupath( s_vol, curdir, newname );
1246 return AFPERR_PARAM;
1250 /* FIXME svid != dvid && dvid's user can't read svid */
1252 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1253 return( AFPERR_PARAM );
1256 if (d_vol->v_flags & AFPVOL_RO)
1257 return AFPERR_VLOCK;
1259 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1263 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1264 return get_afp_errno(AFPERR_NOOBJ);
1266 if ( *s_path->m_name != '\0' ) {
1267 path_error(s_path, AFPERR_PARAM);
1270 /* one of the handful of places that knows about the path type */
1271 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1272 return( AFPERR_PARAM );
1274 /* newname is always only a filename so curdir *is* its
1277 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1278 return( AFPERR_PARAM );
1280 if ( (err = copyfile(s_vol, d_vol, p, upath , newname, adp)) < 0 ) {
1286 if (vol->v_flags & AFPVOL_DROPBOX) {
1287 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1289 #endif /* DROPKLUDGE */
1291 setvoltime(obj, d_vol );
1294 LOG(log_info, logtype_afpd, "end afp_copyfile:");
1300 /* ----------------------- */
1301 static __inline__ int copy_all(const int dfd, const void *buf,
1307 LOG(log_info, logtype_afpd, "begin copy_all:");
1310 while (buflen > 0) {
1311 if ((cc = write(dfd, buf, buflen)) < 0) {
1323 LOG(log_info, logtype_afpd, "end copy_all:");
1329 /* -------------------------- */
1330 static int copy_fd(int dfd, int sfd)
1336 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1337 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1341 #define BUF 128*1024*1024
1343 if (fstat(sfd, &st) == 0) {
1346 if ( offset >= st.st_size) {
1349 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1350 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1353 case EINVAL: /* there's no guarantee that all fs support sendfile */
1362 lseek(sfd, offset, SEEK_SET);
1366 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1373 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1380 /* ----------------------------------
1381 * if newname is NULL (from directory.c) we don't want to copy ressource fork.
1382 * because we are doing it elsewhere.
1383 * currently if newname is NULL then adp is NULL.
1385 int copyfile(s_vol, d_vol, src, dst, newname, adp )
1386 const struct vol *s_vol, *d_vol;
1387 char *src, *dst, *newname;
1388 struct adouble *adp;
1390 struct adouble ads, add;
1394 int noadouble = vol_noadouble(d_vol);
1399 LOG(log_info, logtype_afpd, "begin copyfile:");
1403 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1406 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1407 adflags = ADFLAGS_DF;
1409 adflags |= ADFLAGS_HF;
1412 if (ad_open(src , adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1417 if (ad_hfileno(adp) == -1) {
1418 /* no resource fork, don't create one for dst file */
1419 adflags &= ~ADFLAGS_HF;
1422 stat_result = fstat(ad_dfileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1424 if (stat_result < 0) {
1425 /* unlikely but if fstat fails, the default file mode will be 0666. */
1426 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1429 if (ad_open(dst , adflags | noadouble, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1431 ad_close( adp, adflags );
1432 if (EEXIST != ret_err) {
1433 deletefile(d_vol, dst, 0);
1436 return AFPERR_EXIST;
1438 /* XXX if the source and the dest don't use the same resource type it's broken
1440 if (ad_hfileno(adp) == -1 || 0 == (err = copy_fd(ad_hfileno(&add), ad_hfileno(adp)))){
1441 /* copy the data fork */
1442 err = copy_fd(ad_dfileno(&add), ad_dfileno(adp));
1445 /* Now, reopen destination file */
1449 ad_close( adp, adflags );
1451 if (ad_close( &add, adflags ) <0) {
1455 deletefile(d_vol, dst, 0);
1459 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1460 /* set the new name in the resource fork */
1461 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1462 if (ad_open_metadata(dst , noadouble, 0, &add) < 0) {
1466 ad_setname(&add, newname);
1467 ad_flush_metadata( &add);
1468 if (ad_close_metadata( &add)) {
1475 deletefile(d_vol, dst, 0);
1477 else if (stat_result == 0) {
1478 /* set dest modification date to src date */
1481 ut.actime = ut.modtime = st.st_mtime;
1483 /* FIXME netatalk doesn't use resource fork file date
1484 * but maybe we should set its modtime too.
1489 LOG(log_info, logtype_afpd, "end copyfile:");
1493 switch ( ret_err ) {
1499 return AFPERR_DFULL;
1501 return AFPERR_NOOBJ;
1503 return AFPERR_ACCESS;
1505 return AFPERR_VLOCK;
1507 return AFPERR_PARAM;
1511 /* -----------------------------------
1512 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1513 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1515 when deletefile is called we don't have lock on it, file is closed (for us)
1516 untrue if called by renamefile
1518 ad_open always try to open file RDWR first and ad_lock takes care of
1519 WRITE lock on read only file.
1521 int deletefile( vol, file, checkAttrib )
1522 const struct vol *vol;
1527 struct adouble *adp = &ad;
1528 int adflags, err = AFP_OK;
1531 LOG(log_info, logtype_afpd, "begin deletefile:");
1534 /* try to open both forks at once */
1535 adflags = ADFLAGS_DF|ADFLAGS_HF;
1536 ad_init(&ad, vol->v_adouble, vol->v_ad_options); /* OK */
1538 if ( ad_open( file, adflags, O_RDONLY, 0, &ad ) < 0 ) {
1541 if (adflags == ADFLAGS_DF)
1542 return AFPERR_NOOBJ;
1544 /* that failed. now try to open just the data fork */
1545 adflags = ADFLAGS_DF;
1549 adp = NULL; /* maybe it's a file with no write mode for us */
1550 break; /* was return AFPERR_ACCESS;*/
1552 return AFPERR_VLOCK;
1554 return( AFPERR_PARAM );
1557 break; /* from the while */
1560 * Does kFPDeleteInhibitBit (bit 8) set?
1565 if ( ad_metadata( file , 0, &ad) == 0 ) {
1566 ad_getattr(&ad, &bshort);
1567 ad_close_metadata( &ad);
1568 if ((bshort & htons(ATTRBIT_NODELETE))) {
1569 return AFPERR_OLOCK;
1574 if (adp && (adflags & ADFLAGS_HF) ) {
1575 /* FIXME we have a pb here because we want to know if a file is open
1576 * there's a 'priority inversion' if you can't open the ressource fork RW
1577 * you can delete it if it's open because you can't get a write lock.
1579 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1582 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1584 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1585 ad_close( &ad, adflags );
1586 return( AFPERR_BUSY );
1590 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1593 else if (!(err = vol->vfs->rf_deletefile(vol, file)) && !(err = netatalk_unlink( file )) ) {
1595 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file))))
1597 cnid_delete(vol->v_cdb, id);
1601 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1604 LOG(log_info, logtype_afpd, "end deletefile:");
1610 /* ------------------------------------ */
1611 /* return a file id */
1612 int afp_createid(obj, ibuf, ibuflen, rbuf, rbuflen )
1615 int ibuflen _U_, *rbuflen;
1624 struct path *s_path;
1627 LOG(log_info, logtype_afpd, "begin afp_createid:");
1634 memcpy(&vid, ibuf, sizeof(vid));
1635 ibuf += sizeof(vid);
1637 if (NULL == ( vol = getvolbyvid( vid )) ) {
1638 return( AFPERR_PARAM);
1641 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1645 if (vol->v_flags & AFPVOL_RO)
1646 return AFPERR_VLOCK;
1648 memcpy(&did, ibuf, sizeof( did ));
1649 ibuf += sizeof(did);
1651 if (NULL == ( dir = dirlookup( vol, did )) ) {
1652 return afp_errno; /* was AFPERR_PARAM */
1655 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1656 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1659 if ( path_isadir(s_path) ) {
1660 return( AFPERR_BADTYPE );
1663 upath = s_path->u_name;
1664 switch (s_path->st_errno) {
1666 break; /* success */
1669 return AFPERR_ACCESS;
1671 return AFPERR_NOOBJ;
1673 return AFPERR_PARAM;
1676 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1677 memcpy(rbuf, &id, sizeof(id));
1678 *rbuflen = sizeof(id);
1679 return AFPERR_EXISTID;
1682 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1683 memcpy(rbuf, &id, sizeof(id));
1684 *rbuflen = sizeof(id);
1689 LOG(log_info, logtype_afpd, "ending afp_createid...:");
1694 /* ------------------------------- */
1700 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1703 struct reenum *param = data;
1704 struct vol *vol = param->vol;
1705 cnid_t did = param->did;
1708 memset(&path, 0, sizeof(path));
1710 if ( stat(de->d_name, &path.st)<0 )
1713 /* update or add to cnid */
1714 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1716 #if AD_VERSION > AD_VERSION1
1717 if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1718 struct adouble ad, *adp;
1722 path.u_name = de->d_name;
1724 adp = of_ad(vol, &path, &ad);
1726 if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1729 if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1730 ad_flush_metadata(adp);
1732 ad_close_metadata(adp);
1734 #endif /* AD_VERSION > AD_VERSION1 */
1739 /* --------------------
1740 * Ok the db is out of synch with the dir.
1741 * but if it's a deleted file we don't want to do it again and again.
1744 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1750 if (vol->v_cdb == NULL) {
1754 /* FIXME use of_statdir ? */
1755 if (stat(name, &st)) {
1759 if (dirreenumerate(dir, &st)) {
1760 /* we already did it once and the dir haven't been modified */
1765 data.did = dir->d_did;
1766 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1767 setdiroffcnt(curdir, &st, ret);
1768 dir->d_flags |= DIRF_CNID;
1774 /* ------------------------------
1775 resolve a file id */
1776 int afp_resolveid(obj, ibuf, ibuflen, rbuf, rbuflen )
1779 int ibuflen _U_, *rbuflen;
1785 int err, buflen, retry=0;
1787 u_int16_t vid, bitmap;
1789 static char buffer[12 + MAXPATHLEN + 1];
1790 int len = 12 + MAXPATHLEN + 1;
1793 LOG(log_info, logtype_afpd, "begin afp_resolveid:");
1799 memcpy(&vid, ibuf, sizeof(vid));
1800 ibuf += sizeof(vid);
1802 if (NULL == ( vol = getvolbyvid( vid )) ) {
1803 return( AFPERR_PARAM);
1806 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1810 memcpy(&id, ibuf, sizeof( id ));
1815 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1819 memset(&path, 0, sizeof(path));
1820 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1821 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1824 if (NULL == ( dir = dirlookup( vol, id )) ) {
1825 return AFPERR_NOID; /* idem AFPERR_PARAM */
1827 path.u_name = upath;
1828 if (movecwd(vol, dir) < 0) {
1832 return AFPERR_ACCESS;
1836 return AFPERR_PARAM;
1840 if ( of_stat(&path) < 0 ) {
1842 /* with nfs and our working directory is deleted */
1843 if (errno == ESTALE) {
1847 if ( errno == ENOENT && !retry) {
1848 /* cnid db is out of sync, reenumerate the directory and update ids */
1849 reenumerate_id(vol, ".", dir);
1857 return AFPERR_ACCESS;
1861 return AFPERR_PARAM;
1865 /* directories are bad */
1866 if (S_ISDIR(path.st.st_mode))
1867 return AFPERR_BADTYPE;
1869 memcpy(&bitmap, ibuf, sizeof(bitmap));
1870 bitmap = ntohs( bitmap );
1871 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1875 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1876 rbuf + sizeof(bitmap), &buflen))) {
1879 *rbuflen = buflen + sizeof(bitmap);
1880 memcpy(rbuf, ibuf, sizeof(bitmap));
1883 LOG(log_info, logtype_afpd, "end afp_resolveid:");
1889 /* ------------------------------ */
1890 int afp_deleteid(obj, ibuf, ibuflen, rbuf, rbuflen )
1892 char *ibuf, *rbuf _U_;
1893 int ibuflen _U_, *rbuflen;
1903 static char buffer[12 + MAXPATHLEN + 1];
1904 int len = 12 + MAXPATHLEN + 1;
1907 LOG(log_info, logtype_afpd, "begin afp_deleteid:");
1913 memcpy(&vid, ibuf, sizeof(vid));
1914 ibuf += sizeof(vid);
1916 if (NULL == ( vol = getvolbyvid( vid )) ) {
1917 return( AFPERR_PARAM);
1920 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1924 if (vol->v_flags & AFPVOL_RO)
1925 return AFPERR_VLOCK;
1927 memcpy(&id, ibuf, sizeof( id ));
1931 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1935 if (NULL == ( dir = dirlookup( vol, id )) ) {
1936 return( AFPERR_PARAM );
1940 if ((movecwd(vol, dir) < 0) || (stat(upath, &st) < 0)) {
1944 return AFPERR_ACCESS;
1949 /* still try to delete the id */
1953 return AFPERR_PARAM;
1956 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1957 return AFPERR_BADTYPE;
1959 if (cnid_delete(vol->v_cdb, fileid)) {
1962 return AFPERR_VLOCK;
1965 return AFPERR_ACCESS;
1967 return AFPERR_PARAM;
1972 LOG(log_info, logtype_afpd, "end afp_deleteid:");
1978 /* ------------------------------ */
1979 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
1983 if (path->st_errno) {
1984 switch (path->st_errno) {
1986 afp_errno = AFPERR_NOID;
1990 afp_errno = AFPERR_ACCESS;
1993 afp_errno = AFPERR_PARAM;
1998 /* we use file_access both for legacy Mac perm and
1999 * for unix privilege, rename will take care of folder perms
2001 if (file_access(path, OPENACC_WR ) < 0) {
2002 afp_errno = AFPERR_ACCESS;
2006 if ((*of = of_findname(path))) {
2007 /* reuse struct adouble so it won't break locks */
2011 ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
2012 if ( !ret && ad_hfileno(adp) != -1 && !(adp->ad_hf.adf_flags & ( O_RDWR | O_WRONLY))) {
2014 * The user must have the Read & Write privilege for both files in order to use this command.
2016 ad_close(adp, ADFLAGS_HF);
2017 afp_errno = AFPERR_ACCESS;
2024 #define APPLETEMP ".AppleTempXXXXXX"
2026 int afp_exchangefiles(obj, ibuf, ibuflen, rbuf, rbuflen )
2028 char *ibuf, *rbuf _U_ ;
2029 int ibuflen _U_, *rbuflen;
2031 struct stat srcst, destst;
2033 struct dir *dir, *sdir;
2034 char *spath, temp[17], *p;
2035 char *supath, *upath;
2040 struct adouble *adsp = NULL;
2041 struct adouble *addp = NULL;
2042 struct ofork *s_of = NULL;
2043 struct ofork *d_of = NULL;
2054 LOG(log_info, logtype_afpd, "begin afp_exchangefiles:");
2060 memcpy(&vid, ibuf, sizeof(vid));
2061 ibuf += sizeof(vid);
2063 if (NULL == ( vol = getvolbyvid( vid )) ) {
2064 return( AFPERR_PARAM);
2067 if ((vol->v_flags & AFPVOL_RO))
2068 return AFPERR_VLOCK;
2070 /* source and destination dids */
2071 memcpy(&sid, ibuf, sizeof(sid));
2072 ibuf += sizeof(sid);
2073 memcpy(&did, ibuf, sizeof(did));
2074 ibuf += sizeof(did);
2077 if (NULL == (dir = dirlookup( vol, sid )) ) {
2078 return afp_errno; /* was AFPERR_PARAM */
2081 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2082 return get_afp_errno(AFPERR_NOOBJ);
2085 if ( path_isadir(path) ) {
2086 return AFPERR_BADTYPE; /* it's a dir */
2089 /* save some stuff */
2092 spath = obj->oldtmp;
2093 supath = obj->newtmp;
2094 strcpy(spath, path->m_name);
2095 strcpy(supath, path->u_name); /* this is for the cnid changing */
2096 p = absupath( vol, sdir, supath);
2098 /* pathname too long */
2099 return AFPERR_PARAM ;
2102 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2103 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2107 /* ***** from here we may have resource fork open **** */
2109 /* look for the source cnid. if it doesn't exist, don't worry about
2111 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2113 if (NULL == ( dir = dirlookup( vol, did )) ) {
2114 err = afp_errno; /* was AFPERR_PARAM */
2115 goto err_exchangefile;
2118 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2119 err = get_afp_errno(AFPERR_NOOBJ);
2120 goto err_exchangefile;
2123 if ( path_isadir(path) ) {
2124 err = AFPERR_BADTYPE;
2125 goto err_exchangefile;
2128 /* FPExchangeFiles is the only call that can return the SameObj
2130 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2131 err = AFPERR_SAMEOBJ;
2132 goto err_exchangefile;
2135 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2136 if (!(addp = find_adouble( path, &d_of, &add))) {
2138 goto err_exchangefile;
2142 /* they are not on the same device and at least one is open
2143 * FIXME broken for for crossdev and adouble v2
2146 crossdev = (srcst.st_dev != destst.st_dev);
2147 if (/* (d_of || s_of) && */ crossdev) {
2149 goto err_exchangefile;
2152 /* look for destination id. */
2153 upath = path->u_name;
2154 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2156 /* construct a temp name.
2157 * NOTE: the temp file will be in the dest file's directory. it
2158 * will also be inaccessible from AFP. */
2159 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2160 if (!mktemp(temp)) {
2162 goto err_exchangefile;
2166 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2167 ad_close(adsp, ADFLAGS_HF);
2168 ad_close(addp, ADFLAGS_HF);
2171 /* now, quickly rename the file. we error if we can't. */
2172 if ((err = renamefile(vol, p, temp, temp, adsp)) != AFP_OK)
2173 goto err_exchangefile;
2174 of_rename(vol, s_of, sdir, spath, curdir, temp);
2176 /* rename destination to source */
2177 if ((err = renamefile(vol, upath, p, spath, addp)) != AFP_OK)
2178 goto err_src_to_tmp;
2179 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2181 /* rename temp to destination */
2182 if ((err = renamefile(vol, temp, upath, path->m_name, adsp)) != AFP_OK)
2183 goto err_dest_to_src;
2184 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2186 /* id's need switching. src -> dest and dest -> src.
2187 * we need to re-stat() if it was a cross device copy.
2190 cnid_delete(vol->v_cdb, sid);
2193 cnid_delete(vol->v_cdb, did);
2195 if ((did && ( (crossdev && stat( upath, &srcst) < 0) ||
2196 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2198 (sid && ( (crossdev && stat(p, &destst) < 0) ||
2199 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2204 err = AFPERR_ACCESS;
2209 goto err_temp_to_dest;
2212 /* here we need to reopen if crossdev */
2213 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2215 ad_flush( addp, ADFLAGS_HF );
2218 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2220 ad_flush( adsp, ADFLAGS_HF );
2223 /* change perms, src gets dest perm and vice versa */
2228 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2229 err = AFP_OK; /* ignore error */
2230 goto err_temp_to_dest;
2234 * we need to exchange ACL entries as well
2236 /* exchange_acls(vol, p, upath); */
2241 path->m_name = NULL;
2242 path->u_name = upath;
2244 setfilunixmode(vol, path, destst.st_mode);
2245 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2252 setfilunixmode(vol, path, srcst.st_mode);
2253 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2255 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2256 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2261 LOG(log_info, logtype_afpd, "ending afp_exchangefiles:");
2265 goto err_exchangefile;
2267 /* all this stuff is so that we can unwind a failed operation
2270 /* rename dest to temp */
2271 renamefile(vol, upath, temp, temp, adsp);
2272 of_rename(vol, s_of, curdir, upath, curdir, temp);
2275 /* rename source back to dest */
2276 renamefile(vol, p, upath, path->m_name, addp);
2277 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2280 /* rename temp back to source */
2281 renamefile(vol, temp, p, spath, adsp);
2282 of_rename(vol, s_of, curdir, temp, sdir, spath);
2285 if ( !s_of && adsp && ad_hfileno(adsp) != -1 ) {
2286 ad_close(adsp, ADFLAGS_HF);
2288 if ( !d_of && addp && ad_hfileno(addp) != -1 ) {
2289 ad_close(addp, ADFLAGS_HF);