2 * $Id: file.c,v 1.141 2010-03-12 15:16:49 franklahm 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 */
33 #include <sys/param.h>
35 #include <atalk/adouble.h>
36 #include <atalk/vfs.h>
37 #include <atalk/logger.h>
38 #include <atalk/afp.h>
39 #include <atalk/util.h>
40 #include <atalk/cnid.h>
41 #include <atalk/unix.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, int islink)
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((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
120 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
121 linkflag |= htons(FINDERINFO_ISALIAS);
122 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
123 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
124 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
128 /** Only enter if no appledouble information and no finder information found. */
129 if (chk_ext && (em = getextmap( upath ))) {
130 memcpy(data, em->em_type, sizeof( em->em_type ));
131 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
136 /* ---------------------
138 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, u_int32_t utf8)
143 aint = strlen( name );
147 if (utf8_encoding()) {
148 /* but name is an utf8 mac name */
151 /* global static variable... */
153 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
162 if (aint > MACFILELEN)
169 if (aint > 255) /* FIXME safeguard, anyway if no ascii char it's game over*/
172 utf8 = vol->v_kTextEncoding;
173 memcpy(data, &utf8, sizeof(utf8));
174 data += sizeof(utf8);
177 memcpy(data, &temp, sizeof(temp));
178 data += sizeof(temp);
181 memcpy( data, src, aint );
191 * FIXME: PDINFO is UTF8 and doesn't need adp
193 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
194 (1 << FILPBIT_CDATE) |\
195 (1 << FILPBIT_MDATE) |\
196 (1 << FILPBIT_BDATE) |\
197 (1 << FILPBIT_FINFO) |\
198 (1 << FILPBIT_RFLEN) |\
199 (1 << FILPBIT_EXTRFLEN) |\
200 (1 << FILPBIT_PDINFO) |\
201 (1 << FILPBIT_FNUM) |\
202 (1 << FILPBIT_UNIXPR)))
204 /* -------------------------- */
205 u_int32_t get_id(struct vol *vol, struct adouble *adp, const struct stat *st,
206 const cnid_t did, char *upath, const int len)
208 static int first = 1; /* mark if this func is called the first time */
210 u_int32_t dbcnid = CNID_INVALID;
213 if (vol->v_cdb != NULL) {
214 /* prime aint with what we think is the cnid, set did to zero for
215 catching moved files */
216 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp);
218 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid);
219 /* Throw errors if cnid_add fails. */
220 if (dbcnid == CNID_INVALID) {
222 case CNID_ERR_CLOSE: /* the db is closed */
225 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
226 afp_errno = AFPERR_PARAM;
229 afp_errno = AFPERR_PARAM;
232 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
233 /* we have to do it here for "dbd" because it uses "lazy opening" */
234 /* In order to not end in a loop somehow with goto restart below */
236 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) {
237 cnid_close(vol->v_cdb);
238 free(vol->v_cnidscheme);
239 vol->v_cnidscheme = strdup("tdb");
241 int flags = CNID_FLAG_MEMORY;
242 if ((vol->v_flags & AFPVOL_NODEV)) {
243 flags |= CNID_FLAG_NODEV;
245 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
247 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
249 /* deactivate cnid caching/storing in AppleDouble files and set ro mode*/
250 vol->v_flags &= ~AFPVOL_CACHE;
251 vol->v_flags |= AFPVOL_RO;
253 /* kill ourself with SIGUSR2 aka msg pending */
254 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
255 "Check server messages for details. Switching to read-only mode.");
256 kill(getpid(), SIGUSR2);
258 goto restart; /* not try again with the temp CNID db */
261 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
262 "Check server messages for details, can't recover from this state!");
266 afp_errno = AFPERR_MISC;
270 else if (adp && (adcnid != dbcnid)) {
271 /* Update the ressource fork. For a folder adp is always null */
272 LOG(log_debug, logtype_afpd, "get_id: calling ad_setid. adcnid: %u, dbcnid: %u", htonl(adcnid), htonl(dbcnid));
273 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
284 /* -------------------------- */
285 int getmetadata(struct vol *vol,
287 struct path *path, struct dir *dir,
288 char *buf, size_t *buflen, struct adouble *adp)
290 char *data, *l_nameoff = NULL, *upath;
291 char *utf_nameoff = NULL;
296 u_char achar, fdType[4];
302 LOG(log_debug9, logtype_afpd, "begin getmetadata:");
305 upath = path->u_name;
310 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
311 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
312 || (bitmap & (1 << FILPBIT_FNUM))) {
314 id = get_id(vol, adp, st, dir->d_did, upath, strlen(upath));
317 if (id == CNID_INVALID)
320 path->m_name = utompath(vol, upath, id, utf8_encoding());
323 while ( bitmap != 0 ) {
324 while (( bitmap & 1 ) == 0 ) {
332 ad_getattr(adp, &ashort);
333 } else if (vol_inv_dots(vol) && *upath == '.') {
334 ashort = htons(ATTRBIT_INVISIBLE);
338 /* FIXME do we want a visual clue if the file is read only
341 accessmode( ".", &ma, dir , NULL);
342 if ((ma.ma_user & AR_UWRITE)) {
343 accessmode( upath, &ma, dir , st);
344 if (!(ma.ma_user & AR_UWRITE)) {
345 ashort |= htons(ATTRBIT_NOWRITE);
349 memcpy(data, &ashort, sizeof( ashort ));
350 data += sizeof( ashort );
354 memcpy(data, &dir->d_did, sizeof( u_int32_t ));
355 data += sizeof( u_int32_t );
359 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
360 aint = AD_DATE_FROM_UNIX(st->st_mtime);
361 memcpy(data, &aint, sizeof( aint ));
362 data += sizeof( aint );
366 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
367 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
368 aint = AD_DATE_FROM_UNIX(st->st_mtime);
371 aint = AD_DATE_FROM_UNIX(st->st_mtime);
373 memcpy(data, &aint, sizeof( int ));
374 data += sizeof( int );
378 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
379 aint = AD_DATE_START;
380 memcpy(data, &aint, sizeof( int ));
381 data += sizeof( int );
385 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
386 data += ADEDLEN_FINDERI;
391 data += sizeof( u_int16_t );
395 memset(data, 0, sizeof(u_int16_t));
396 data += sizeof( u_int16_t );
400 memcpy(data, &id, sizeof( id ));
401 data += sizeof( id );
405 if (st->st_size > 0xffffffff)
408 aint = htonl( st->st_size );
409 memcpy(data, &aint, sizeof( aint ));
410 data += sizeof( aint );
415 if (adp->ad_rlen > 0xffffffff)
418 aint = htonl( adp->ad_rlen);
422 memcpy(data, &aint, sizeof( aint ));
423 data += sizeof( aint );
426 /* Current client needs ProDOS info block for this file.
427 Use simple heuristic and let the Mac "type" string tell
428 us what the PD file code should be. Everything gets a
429 subtype of 0x0000 unless the original value was hashed
430 to "pXYZ" when we created it. See IA, Ver 2.
431 <shirsch@adelphia.net> */
432 case FILPBIT_PDINFO :
433 if (afp_version >= 30) { /* UTF8 name */
434 utf8 = kTextEncodingUTF8;
436 data += sizeof( u_int16_t );
438 memcpy(data, &aint, sizeof( aint ));
439 data += sizeof( aint );
443 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
445 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
449 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
453 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
457 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
461 else if ( fdType[0] == 'p' ) {
463 ashort = (fdType[2] * 256) + fdType[3];
477 memcpy(data, &ashort, sizeof( ashort ));
478 data += sizeof( ashort );
479 memset(data, 0, sizeof( ashort ));
480 data += sizeof( ashort );
483 case FILPBIT_EXTDFLEN:
484 aint = htonl(st->st_size >> 32);
485 memcpy(data, &aint, sizeof( aint ));
486 data += sizeof( aint );
487 aint = htonl(st->st_size);
488 memcpy(data, &aint, sizeof( aint ));
489 data += sizeof( aint );
491 case FILPBIT_EXTRFLEN:
494 aint = htonl(adp->ad_rlen >> 32);
495 memcpy(data, &aint, sizeof( aint ));
496 data += sizeof( aint );
498 aint = htonl(adp->ad_rlen);
499 memcpy(data, &aint, sizeof( aint ));
500 data += sizeof( aint );
502 case FILPBIT_UNIXPR :
503 /* accessmode may change st_mode with ACLs */
504 accessmode( upath, &ma, dir , st);
506 aint = htonl(st->st_uid);
507 memcpy( data, &aint, sizeof( aint ));
508 data += sizeof( aint );
509 aint = htonl(st->st_gid);
510 memcpy( data, &aint, sizeof( aint ));
511 data += sizeof( aint );
514 type == slnk indicates an OSX style symlink,
515 we have to add S_IFLNK to the mode, otherwise
516 10.3 clients freak out. */
520 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
521 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
527 memcpy( data, &aint, sizeof( aint ));
528 data += sizeof( aint );
530 *data++ = ma.ma_user;
531 *data++ = ma.ma_world;
532 *data++ = ma.ma_group;
533 *data++ = ma.ma_owner;
537 return( AFPERR_BITMAP );
543 ashort = htons( data - buf );
544 memcpy(l_nameoff, &ashort, sizeof( ashort ));
545 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
548 ashort = htons( data - buf );
549 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
550 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
552 *buflen = data - buf;
556 /* ----------------------- */
557 int getfilparams(struct vol *vol,
559 struct path *path, struct dir *dir,
560 char *buf, size_t *buflen )
562 struct adouble ad, *adp;
567 LOG(log_debug9, logtype_default, "begin getfilparams:");
570 opened = PARAM_NEED_ADP(bitmap);
575 int flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0;
577 adp = of_ad(vol, path, &ad);
578 upath = path->u_name;
580 if ( ad_metadata( upath, flags|ADFLAGS_CREATE, adp) < 0 ) {
583 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
584 upath, strerror(errno));
585 return AFPERR_ACCESS;
587 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
596 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
598 ad_close_metadata( adp);
601 LOG(log_debug9, logtype_afpd, "end getfilparams:");
607 /* ----------------------------- */
608 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
610 struct adouble ad, *adp;
613 struct ofork *of = NULL;
615 int creatf, did, openf, retvalue = AFP_OK;
621 creatf = (unsigned char) *ibuf++;
623 memcpy(&vid, ibuf, sizeof( vid ));
624 ibuf += sizeof( vid );
626 if (NULL == ( vol = getvolbyvid( vid )) ) {
627 return( AFPERR_PARAM );
630 if (vol->v_flags & AFPVOL_RO)
633 memcpy(&did, ibuf, sizeof( did));
634 ibuf += sizeof( did );
636 if (NULL == ( dir = dirlookup( vol, did )) ) {
640 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
641 return get_afp_errno(AFPERR_PARAM);
644 if ( *s_path->m_name == '\0' ) {
645 return( AFPERR_BADTYPE );
648 upath = s_path->u_name;
650 /* if upath is deleted we already in trouble anyway */
651 if ((of = of_findname(s_path))) {
654 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
658 /* on a hard create, fail if file exists and is open */
661 openf = O_RDWR|O_CREAT|O_TRUNC;
663 /* on a soft create, if the file is open then ad_open won't fail
664 because open syscall is not called
669 openf = O_RDWR|O_CREAT|O_EXCL;
672 if ( ad_open( upath, ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF|ADFLAGS_CREATE,
673 openf, 0666, adp) < 0 ) {
677 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
678 return ( AFPERR_NOOBJ );
680 return( AFPERR_EXIST );
682 return( AFPERR_ACCESS );
685 return( AFPERR_DFULL );
687 return( AFPERR_PARAM );
690 if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */
691 /* on noadouble volumes, just creating the data fork is ok */
692 if (vol_noadouble(vol)) {
693 ad_close( adp, ADFLAGS_DF );
694 goto createfile_done;
696 /* FIXME with hard create on an existing file, we already
697 * corrupted the data file.
699 netatalk_unlink( upath );
700 ad_close( adp, ADFLAGS_DF );
701 return AFPERR_ACCESS;
704 path = s_path->m_name;
705 ad_setname(adp, path);
707 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
713 if (vol->v_flags & AFPVOL_DROPBOX) {
714 retvalue = matchfile2dirperms(upath, vol, did);
716 #endif /* DROPKLUDGE */
718 setvoltime(obj, vol );
723 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
729 u_int16_t vid, bitmap;
734 memcpy(&vid, ibuf, sizeof( vid ));
735 ibuf += sizeof( vid );
736 if (NULL == ( vol = getvolbyvid( vid )) ) {
737 return( AFPERR_PARAM );
740 if (vol->v_flags & AFPVOL_RO)
743 memcpy(&did, ibuf, sizeof( did ));
744 ibuf += sizeof( did );
745 if (NULL == ( dir = dirlookup( vol, did )) ) {
746 return afp_errno; /* was AFPERR_NOOBJ */
749 memcpy(&bitmap, ibuf, sizeof( bitmap ));
750 bitmap = ntohs( bitmap );
751 ibuf += sizeof( bitmap );
753 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
754 return get_afp_errno(AFPERR_PARAM);
757 if (path_isadir(s_path)) {
758 return( AFPERR_BADTYPE ); /* it's a directory */
761 if ( s_path->st_errno != 0 ) {
762 return( AFPERR_NOOBJ );
765 if ((u_long)ibuf & 1 ) {
769 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
770 setvoltime(obj, vol );
777 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
780 extern struct path Cur_Path;
782 int setfilparams(struct vol *vol,
783 struct path *path, u_int16_t f_bitmap, char *buf )
785 struct adouble ad, *adp;
787 int bit, isad = 1, err = AFP_OK;
789 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
790 u_int16_t ashort, bshort, oshort;
793 u_int16_t upriv_bit = 0;
797 int change_mdate = 0;
798 int change_parent_mdate = 0;
803 u_int16_t bitmap = f_bitmap;
804 u_int32_t cdate,bdate;
805 u_char finder_buf[32];
808 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
811 adp = of_ad(vol, path, &ad);
812 upath = path->u_name;
814 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
815 return AFPERR_ACCESS;
818 /* with unix priv maybe we have to change adouble file priv first */
820 while ( bitmap != 0 ) {
821 while (( bitmap & 1 ) == 0 ) {
828 memcpy(&ashort, buf, sizeof( ashort ));
829 buf += sizeof( ashort );
833 memcpy(&cdate, buf, sizeof(cdate));
834 buf += sizeof( cdate );
837 memcpy(&newdate, buf, sizeof( newdate ));
838 buf += sizeof( newdate );
842 memcpy(&bdate, buf, sizeof( bdate));
843 buf += sizeof( bdate );
847 memcpy(finder_buf, buf, 32 );
848 if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
853 char buf[PATH_MAX+1];
854 if ((fp=open(path->u_name,O_RDONLY))>=0){
855 if ((len=read(fp,buf,PATH_MAX+1))){
856 if (unlink(path->u_name)==0){
858 erc = symlink(buf, path->u_name);
867 goto setfilparam_done;
872 case FILPBIT_UNIXPR :
873 if (!vol_unix_priv(vol)) {
874 /* this volume doesn't use unix priv */
880 change_parent_mdate = 1;
882 memcpy( &aint, buf, sizeof( aint ));
883 f_uid = ntohl (aint);
884 buf += sizeof( aint );
885 memcpy( &aint, buf, sizeof( aint ));
886 f_gid = ntohl (aint);
887 buf += sizeof( aint );
888 setfilowner(vol, f_uid, f_gid, path);
890 memcpy( &upriv, buf, sizeof( upriv ));
891 buf += sizeof( upriv );
892 upriv = ntohl (upriv);
893 if ((upriv & S_IWUSR)) {
894 setfilunixmode(vol, path, upriv);
901 case FILPBIT_PDINFO :
902 if (afp_version < 30) { /* else it's UTF8 name */
905 /* Keep special case to support crlf translations */
906 if ((unsigned int) achar == 0x04) {
907 fdType = (u_char *)"TEXT";
910 xyy[0] = ( u_char ) 'p';
921 /* break while loop */
930 /* second try with adouble open
932 if ( ad_open_metadata( upath, 0, O_CREAT, adp) < 0) {
933 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
935 * For some things, we don't need an adouble header:
936 * - change of modification date
937 * - UNIX privs (Bug-ID #2863424)
939 if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
940 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
941 return AFPERR_ACCESS;
943 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
945 } else if ((ad_get_HF_flags( adp ) & O_CREAT) ) {
946 ad_setname(adp, path->m_name);
951 while ( bitmap != 0 ) {
952 while (( bitmap & 1 ) == 0 ) {
959 ad_getattr(adp, &bshort);
961 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
962 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
966 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
967 change_parent_mdate = 1;
968 ad_setattr(adp, bshort);
971 ad_setdate(adp, AD_DATE_CREATE, cdate);
976 ad_setdate(adp, AD_DATE_BACKUP, bdate);
979 if (default_type( ad_entry( adp, ADEID_FINDERI ))
981 ((em = getextmap( path->m_name )) &&
982 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
983 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
984 || ((em = getdefextmap()) &&
985 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
986 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
988 memcpy(finder_buf, ufinderi, 8 );
990 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
992 case FILPBIT_UNIXPR :
994 setfilunixmode(vol, path, upriv);
997 case FILPBIT_PDINFO :
998 if (afp_version < 30) { /* else it's UTF8 name */
999 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1000 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1005 err = AFPERR_BITMAP;
1006 goto setfilparam_done;
1013 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1014 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1018 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1019 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1025 ad_close_metadata( adp);
1029 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1030 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1031 bitmap = 1<<FILPBIT_MDATE;
1032 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1036 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1042 * renamefile and copyfile take the old and new unix pathnames
1043 * and the new mac name.
1045 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1046 * passing -1 means this is not used, src path is a full path
1047 * src the source path
1048 * dst the dest filename in current dir
1049 * newname the dest mac name
1050 * adp adouble struct of src file, if open, or & zeroed one
1053 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1057 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1060 return( AFPERR_NOOBJ );
1063 return( AFPERR_ACCESS );
1065 return AFPERR_VLOCK;
1066 case EXDEV : /* Cross device move -- try copy */
1067 /* NOTE: with open file it's an error because after the copy we will
1068 * get two files, it's fixable for our process (eg reopen the new file, get the
1069 * locks, and so on. But it doesn't solve the case with a second process
1071 if (adp->ad_open_forks) {
1072 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1073 return AFPERR_OLOCK; /* little lie */
1075 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1076 /* on error copyfile delete dest */
1079 return deletefile(vol, sdir_fd, src, 0);
1081 return( AFPERR_PARAM );
1085 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1089 /* try to undo the data fork rename,
1090 * we know we are on the same device
1093 unix_rename(-1, dst, sdir_fd, src );
1094 /* return the first error */
1097 return AFPERR_NOOBJ;
1100 return AFPERR_ACCESS ;
1102 return AFPERR_VLOCK;
1104 return AFPERR_PARAM ;
1109 /* don't care if we can't open the newly renamed ressource fork
1111 if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
1112 ad_setname(adp, newname);
1114 ad_close( adp, ADFLAGS_HF );
1121 convert a Mac long name to an utf8 name,
1123 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1127 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1133 /* ---------------- */
1134 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1141 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1147 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1148 if (afp_version >= 30) {
1149 /* convert it to UTF8
1151 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1155 strncpy( newname, ibuf, plen );
1156 newname[ plen ] = '\0';
1158 if (strlen(newname) != plen) {
1159 /* there's \0 in newname, e.g. it's a pathname not
1167 memcpy(&hint, ibuf, sizeof(hint));
1168 ibuf += sizeof(hint);
1170 memcpy(&len16, ibuf, sizeof(len16));
1171 ibuf += sizeof(len16);
1172 plen = ntohs(len16);
1175 if (plen > AFPOBJ_TMPSIZ) {
1178 strncpy( newname, ibuf, plen );
1179 newname[ plen ] = '\0';
1180 if (strlen(newname) != plen) {
1189 /* -----------------------------------
1191 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1193 struct vol *s_vol, *d_vol;
1195 char *newname, *p, *upath;
1196 struct path *s_path;
1197 u_int32_t sdid, ddid;
1198 int err, retvalue = AFP_OK;
1199 u_int16_t svid, dvid;
1201 struct adouble ad, *adp;
1207 memcpy(&svid, ibuf, sizeof( svid ));
1208 ibuf += sizeof( svid );
1209 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1210 return( AFPERR_PARAM );
1213 memcpy(&sdid, ibuf, sizeof( sdid ));
1214 ibuf += sizeof( sdid );
1215 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1219 memcpy(&dvid, ibuf, sizeof( dvid ));
1220 ibuf += sizeof( dvid );
1221 memcpy(&ddid, ibuf, sizeof( ddid ));
1222 ibuf += sizeof( ddid );
1224 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1225 return get_afp_errno(AFPERR_PARAM);
1227 if ( path_isadir(s_path) ) {
1228 return( AFPERR_BADTYPE );
1231 /* don't allow copies when the file is open.
1232 * XXX: the spec only calls for read/deny write access.
1233 * however, copyfile doesn't have any of that info,
1234 * and locks need to stay coherent. as a result,
1235 * we just balk if the file is opened already. */
1237 adp = of_ad(s_vol, s_path, &ad);
1239 if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1240 return AFPERR_DENYCONF;
1242 denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1243 getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1246 retvalue = AFPERR_DENYCONF;
1250 newname = obj->newtmp;
1251 strcpy( newname, s_path->m_name );
1253 p = ctoupath( s_vol, curdir, newname );
1255 retvalue = AFPERR_PARAM;
1260 /* FIXME svid != dvid && dvid's user can't read svid */
1262 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1263 retvalue = AFPERR_PARAM;
1267 if (d_vol->v_flags & AFPVOL_RO) {
1268 retvalue = AFPERR_VLOCK;
1272 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1273 retvalue = afp_errno;
1277 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1278 retvalue = get_afp_errno(AFPERR_NOOBJ);
1282 if ( *s_path->m_name != '\0' ) {
1283 retvalue =path_error(s_path, AFPERR_NOOBJ);
1287 /* one of the handful of places that knows about the path type */
1288 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1289 retvalue = AFPERR_PARAM;
1292 /* newname is always only a filename so curdir *is* its
1295 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1296 retvalue =AFPERR_PARAM;
1300 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1307 if (vol->v_flags & AFPVOL_DROPBOX) {
1308 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1310 #endif /* DROPKLUDGE */
1312 setvoltime(obj, d_vol );
1315 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1319 /* ----------------------- */
1320 static int copy_all(const int dfd, const void *buf,
1326 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1329 while (buflen > 0) {
1330 if ((cc = write(dfd, buf, buflen)) < 0) {
1342 LOG(log_debug9, logtype_afpd, "end copy_all:");
1348 /* --------------------------
1349 * copy only the fork data stream
1351 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1358 if (eid == ADEID_DFORK) {
1359 sfd = ad_data_fileno(ads);
1360 dfd = ad_data_fileno(add);
1363 sfd = ad_reso_fileno(ads);
1364 dfd = ad_reso_fileno(add);
1367 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1370 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1373 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1374 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1378 #define BUF 128*1024*1024
1380 if (fstat(sfd, &st) == 0) {
1383 if ( offset >= st.st_size) {
1386 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1387 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1390 case EINVAL: /* there's no guarantee that all fs support sendfile */
1399 lseek(sfd, offset, SEEK_SET);
1403 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1410 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1417 /* ----------------------------------
1418 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1419 * because we are doing it elsewhere.
1420 * currently if newname is NULL then adp is NULL.
1422 int copyfile(const struct vol *s_vol,
1423 const struct vol *d_vol,
1428 struct adouble *adp)
1430 struct adouble ads, add;
1437 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1438 sfd, src, dst, newname);
1441 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1445 adflags = ADFLAGS_DF;
1447 adflags |= ADFLAGS_HF;
1450 if (ad_openat(sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1455 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1456 /* no resource fork, don't create one for dst file */
1457 adflags &= ~ADFLAGS_HF;
1460 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1462 if (stat_result < 0) {
1463 /* unlikely but if fstat fails, the default file mode will be 0666. */
1464 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1467 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1468 if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1470 ad_close( adp, adflags );
1471 if (EEXIST != ret_err) {
1472 deletefile(d_vol, -1, dst, 0);
1475 return AFPERR_EXIST;
1479 * XXX if the source and the dest don't use the same resource type it's broken
1481 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1482 /* copy the data fork */
1483 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1484 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1492 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1493 /* set the new name in the resource fork */
1494 ad_copy_header(&add, adp);
1495 ad_setname(&add, newname);
1498 ad_close( adp, adflags );
1500 if (ad_close( &add, adflags ) <0) {
1505 deletefile(d_vol, -1, dst, 0);
1507 else if (stat_result == 0) {
1508 /* set dest modification date to src date */
1511 ut.actime = ut.modtime = st.st_mtime;
1513 /* FIXME netatalk doesn't use resource fork file date
1514 * but maybe we should set its modtime too.
1519 switch ( ret_err ) {
1525 return AFPERR_DFULL;
1527 return AFPERR_NOOBJ;
1529 return AFPERR_ACCESS;
1531 return AFPERR_VLOCK;
1533 return AFPERR_PARAM;
1537 /* -----------------------------------
1538 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1539 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1541 when deletefile is called we don't have lock on it, file is closed (for us)
1542 untrue if called by renamefile
1544 ad_open always try to open file RDWR first and ad_lock takes care of
1545 WRITE lock on read only file.
1548 static int check_attrib(struct adouble *adp)
1550 u_int16_t bshort = 0;
1552 ad_getattr(adp, &bshort);
1554 * Does kFPDeleteInhibitBit (bit 8) set?
1556 if ((bshort & htons(ATTRBIT_NODELETE))) {
1557 return AFPERR_OLOCK;
1559 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1565 * dirfd can be used for unlinkat semantics
1567 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1570 struct adouble *adp = NULL;
1571 int adflags, err = AFP_OK;
1574 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1576 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1578 /* was EACCESS error try to get only metadata */
1579 /* we never want to create a resource fork here, we are going to delete it
1580 * moreover sometimes deletefile is called with a no existent file and
1581 * ad_open would create a 0 byte resource fork
1583 if ( ad_metadataat(dirfd, file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
1584 if ((err = check_attrib(&ad))) {
1585 ad_close_metadata(&ad);
1592 /* try to open both forks at once */
1593 adflags = ADFLAGS_DF;
1594 if ( ad_openat(dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, 0, &ad ) < 0 ) {
1599 case EACCES: /* maybe it's a file with no write mode for us */
1600 break; /* was return AFPERR_ACCESS;*/
1613 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1614 adflags |= ADFLAGS_HF;
1615 /* FIXME we have a pb here because we want to know if a file is open
1616 * there's a 'priority inversion' if you can't open the ressource fork RW
1617 * you can delete it if it's open because you can't get a write lock.
1619 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1622 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1624 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1630 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1632 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1634 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1635 cnid_delete(vol->v_cdb, id);
1641 ad_close_metadata(&ad);
1644 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1649 /* ------------------------------------ */
1650 /* return a file id */
1651 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1660 struct path *s_path;
1666 memcpy(&vid, ibuf, sizeof(vid));
1667 ibuf += sizeof(vid);
1669 if (NULL == ( vol = getvolbyvid( vid )) ) {
1670 return( AFPERR_PARAM);
1673 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1677 if (vol->v_flags & AFPVOL_RO)
1678 return AFPERR_VLOCK;
1680 memcpy(&did, ibuf, sizeof( did ));
1681 ibuf += sizeof(did);
1683 if (NULL == ( dir = dirlookup( vol, did )) ) {
1684 return afp_errno; /* was AFPERR_PARAM */
1687 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1688 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1691 if ( path_isadir(s_path) ) {
1692 return( AFPERR_BADTYPE );
1695 upath = s_path->u_name;
1696 switch (s_path->st_errno) {
1698 break; /* success */
1701 return AFPERR_ACCESS;
1703 return AFPERR_NOOBJ;
1705 return AFPERR_PARAM;
1708 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1709 memcpy(rbuf, &id, sizeof(id));
1710 *rbuflen = sizeof(id);
1711 return AFPERR_EXISTID;
1714 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1715 memcpy(rbuf, &id, sizeof(id));
1716 *rbuflen = sizeof(id);
1723 /* ------------------------------- */
1729 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1732 struct reenum *param = data;
1733 struct vol *vol = param->vol;
1734 cnid_t did = param->did;
1737 if ( lstat(de->d_name, &path.st)<0 )
1740 /* update or add to cnid */
1741 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1743 #if AD_VERSION > AD_VERSION1
1744 if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1745 struct adouble ad, *adp;
1749 path.u_name = de->d_name;
1751 adp = of_ad(vol, &path, &ad);
1753 if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1756 if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1759 ad_close_metadata(adp);
1761 #endif /* AD_VERSION > AD_VERSION1 */
1766 /* --------------------
1767 * Ok the db is out of synch with the dir.
1768 * but if it's a deleted file we don't want to do it again and again.
1771 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1777 if (vol->v_cdb == NULL) {
1781 /* FIXME use of_statdir ? */
1782 if (lstat(name, &st)) {
1786 if (dirreenumerate(dir, &st)) {
1787 /* we already did it once and the dir haven't been modified */
1792 data.did = dir->d_did;
1793 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1794 setdiroffcnt(curdir, &st, ret);
1795 dir->d_flags |= DIRF_CNID;
1801 /* ------------------------------
1802 resolve a file id */
1803 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1812 u_int16_t vid, bitmap;
1814 static char buffer[12 + MAXPATHLEN + 1];
1815 int len = 12 + MAXPATHLEN + 1;
1820 memcpy(&vid, ibuf, sizeof(vid));
1821 ibuf += sizeof(vid);
1823 if (NULL == ( vol = getvolbyvid( vid )) ) {
1824 return( AFPERR_PARAM);
1827 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1831 memcpy(&id, ibuf, sizeof( id ));
1836 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1840 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1841 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1844 if (NULL == ( dir = dirlookup( vol, id )) ) {
1845 return AFPERR_NOID; /* idem AFPERR_PARAM */
1847 if (movecwd(vol, dir) < 0) {
1851 return AFPERR_ACCESS;
1855 return AFPERR_PARAM;
1859 memset(&path, 0, sizeof(path));
1860 path.u_name = upath;
1861 if ( of_stat(&path) < 0 ) {
1863 /* with nfs and our working directory is deleted */
1864 if (errno == ESTALE) {
1868 if ( errno == ENOENT && !retry) {
1869 /* cnid db is out of sync, reenumerate the directory and update ids */
1870 reenumerate_id(vol, ".", dir);
1878 return AFPERR_ACCESS;
1882 return AFPERR_PARAM;
1886 /* directories are bad */
1887 if (S_ISDIR(path.st.st_mode)) {
1888 /* OS9 and OSX don't return the same error code */
1889 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1892 memcpy(&bitmap, ibuf, sizeof(bitmap));
1893 bitmap = ntohs( bitmap );
1894 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1898 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1899 rbuf + sizeof(bitmap), &buflen))) {
1902 *rbuflen = buflen + sizeof(bitmap);
1903 memcpy(rbuf, ibuf, sizeof(bitmap));
1908 /* ------------------------------ */
1909 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1919 static char buffer[12 + MAXPATHLEN + 1];
1920 int len = 12 + MAXPATHLEN + 1;
1925 memcpy(&vid, ibuf, sizeof(vid));
1926 ibuf += sizeof(vid);
1928 if (NULL == ( vol = getvolbyvid( vid )) ) {
1929 return( AFPERR_PARAM);
1932 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1936 if (vol->v_flags & AFPVOL_RO)
1937 return AFPERR_VLOCK;
1939 memcpy(&id, ibuf, sizeof( id ));
1943 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1947 if (NULL == ( dir = dirlookup( vol, id )) ) {
1948 return( AFPERR_PARAM );
1952 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1956 return AFPERR_ACCESS;
1961 /* still try to delete the id */
1965 return AFPERR_PARAM;
1968 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1969 return AFPERR_BADTYPE;
1971 if (cnid_delete(vol->v_cdb, fileid)) {
1974 return AFPERR_VLOCK;
1977 return AFPERR_ACCESS;
1979 return AFPERR_PARAM;
1986 /* ------------------------------ */
1987 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
1991 if (path->st_errno) {
1992 switch (path->st_errno) {
1994 afp_errno = AFPERR_NOID;
1998 afp_errno = AFPERR_ACCESS;
2001 afp_errno = AFPERR_PARAM;
2006 /* we use file_access both for legacy Mac perm and
2007 * for unix privilege, rename will take care of folder perms
2009 if (file_access(path, OPENACC_WR ) < 0) {
2010 afp_errno = AFPERR_ACCESS;
2014 if ((*of = of_findname(path))) {
2015 /* reuse struct adouble so it won't break locks */
2019 ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
2021 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2023 * The user must have the Read & Write privilege for both files in order to use this command.
2025 ad_close(adp, ADFLAGS_HF);
2026 afp_errno = AFPERR_ACCESS;
2033 #define APPLETEMP ".AppleTempXXXXXX"
2035 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2037 struct stat srcst, destst;
2039 struct dir *dir, *sdir;
2040 char *spath, temp[17], *p;
2041 char *supath, *upath;
2046 struct adouble *adsp = NULL;
2047 struct adouble *addp = NULL;
2048 struct ofork *s_of = NULL;
2049 struct ofork *d_of = NULL;
2062 memcpy(&vid, ibuf, sizeof(vid));
2063 ibuf += sizeof(vid);
2065 if (NULL == ( vol = getvolbyvid( vid )) ) {
2066 return( AFPERR_PARAM);
2069 if ((vol->v_flags & AFPVOL_RO))
2070 return AFPERR_VLOCK;
2072 /* source and destination dids */
2073 memcpy(&sid, ibuf, sizeof(sid));
2074 ibuf += sizeof(sid);
2075 memcpy(&did, ibuf, sizeof(did));
2076 ibuf += sizeof(did);
2079 if (NULL == (dir = dirlookup( vol, sid )) ) {
2080 return afp_errno; /* was AFPERR_PARAM */
2083 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2084 return get_afp_errno(AFPERR_NOOBJ);
2087 if ( path_isadir(path) ) {
2088 return AFPERR_BADTYPE; /* it's a dir */
2091 /* save some stuff */
2094 spath = obj->oldtmp;
2095 supath = obj->newtmp;
2096 strcpy(spath, path->m_name);
2097 strcpy(supath, path->u_name); /* this is for the cnid changing */
2098 p = absupath( vol, sdir, supath);
2100 /* pathname too long */
2101 return AFPERR_PARAM ;
2104 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2105 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2109 /* ***** from here we may have resource fork open **** */
2111 /* look for the source cnid. if it doesn't exist, don't worry about
2113 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2115 if (NULL == ( dir = dirlookup( vol, did )) ) {
2116 err = afp_errno; /* was AFPERR_PARAM */
2117 goto err_exchangefile;
2120 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2121 err = get_afp_errno(AFPERR_NOOBJ);
2122 goto err_exchangefile;
2125 if ( path_isadir(path) ) {
2126 err = AFPERR_BADTYPE;
2127 goto err_exchangefile;
2130 /* FPExchangeFiles is the only call that can return the SameObj
2132 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2133 err = AFPERR_SAMEOBJ;
2134 goto err_exchangefile;
2137 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2138 if (!(addp = find_adouble( path, &d_of, &add))) {
2140 goto err_exchangefile;
2144 /* they are not on the same device and at least one is open
2145 * FIXME broken for for crossdev and adouble v2
2148 crossdev = (srcst.st_dev != destst.st_dev);
2149 if (/* (d_of || s_of) && */ crossdev) {
2151 goto err_exchangefile;
2154 /* look for destination id. */
2155 upath = path->u_name;
2156 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2158 /* construct a temp name.
2159 * NOTE: the temp file will be in the dest file's directory. it
2160 * will also be inaccessible from AFP. */
2161 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2162 if (!mktemp(temp)) {
2164 goto err_exchangefile;
2168 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2169 ad_close(adsp, ADFLAGS_HF);
2170 ad_close(addp, ADFLAGS_HF);
2173 /* now, quickly rename the file. we error if we can't. */
2174 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2175 goto err_exchangefile;
2176 of_rename(vol, s_of, sdir, spath, curdir, temp);
2178 /* rename destination to source */
2179 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2180 goto err_src_to_tmp;
2181 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2183 /* rename temp to destination */
2184 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2185 goto err_dest_to_src;
2186 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2188 /* id's need switching. src -> dest and dest -> src.
2189 * we need to re-stat() if it was a cross device copy.
2192 cnid_delete(vol->v_cdb, sid);
2195 cnid_delete(vol->v_cdb, did);
2197 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2198 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2200 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2201 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2206 err = AFPERR_ACCESS;
2211 goto err_temp_to_dest;
2214 /* here we need to reopen if crossdev */
2215 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2220 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2225 /* change perms, src gets dest perm and vice versa */
2230 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2231 err = AFP_OK; /* ignore error */
2232 goto err_temp_to_dest;
2236 * we need to exchange ACL entries as well
2238 /* exchange_acls(vol, p, upath); */
2243 path->m_name = NULL;
2244 path->u_name = upath;
2246 setfilunixmode(vol, path, destst.st_mode);
2247 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2254 setfilunixmode(vol, path, srcst.st_mode);
2255 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2257 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2258 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2263 goto err_exchangefile;
2265 /* all this stuff is so that we can unwind a failed operation
2268 /* rename dest to temp */
2269 renamefile(vol, -1, upath, temp, temp, adsp);
2270 of_rename(vol, s_of, curdir, upath, curdir, temp);
2273 /* rename source back to dest */
2274 renamefile(vol, -1, p, upath, path->m_name, addp);
2275 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2278 /* rename temp back to source */
2279 renamefile(vol, -1, temp, p, spath, adsp);
2280 of_rename(vol, s_of, curdir, temp, sdir, spath);
2283 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2284 ad_close(adsp, ADFLAGS_HF);
2286 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2287 ad_close(addp, ADFLAGS_HF);