2 * Copyright (c) 1990,1993 Regents of The University of Michigan.
3 * All Rights Reserved. See COPYRIGHT.
8 #endif /* HAVE_CONFIG_H */
16 #include <sys/param.h>
18 #include <atalk/adouble.h>
19 #include <atalk/vfs.h>
20 #include <atalk/logger.h>
21 #include <atalk/afp.h>
22 #include <atalk/util.h>
23 #include <atalk/cnid.h>
24 #include <atalk/unix.h>
25 #include <atalk/globals.h>
26 #include <atalk/fce_api.h>
28 #include "directory.h"
37 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
38 * field bytes subfield bytes
41 * ioFlFndrInfo 16 -> type 4 type field
42 * creator 4 creator field
43 * flags 2 finder flags:
45 * location 4 location in window
46 * folder 2 window that contains file
48 * ioFlXFndrInfo 16 -> iconID 2 icon id
50 * script 1 script system
52 * commentID 2 comment id
53 * putawayID 4 home directory id
56 const u_char ufinderi[ADEDLEN_FINDERI] = {
57 0, 0, 0, 0, 0, 0, 0, 0,
58 1, 0, 0, 0, 0, 0, 0, 0,
59 0, 0, 0, 0, 0, 0, 0, 0,
60 0, 0, 0, 0, 0, 0, 0, 0
63 static const u_char old_ufinderi[] = {
64 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
67 /* ----------------------
69 static int default_type(void *finder)
71 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
76 /* FIXME path : unix or mac name ? (for now it's unix name ) */
77 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
79 void *ad_finder = NULL;
83 ad_finder = ad_entry(adp, ADEID_FINDERI);
86 memcpy(data, ad_finder, ADEDLEN_FINDERI);
89 memcpy(data, ufinderi, ADEDLEN_FINDERI);
90 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
93 ashort = htons(FINDERINFO_INVISIBLE);
94 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
100 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
101 linkflag |= htons(FINDERINFO_ISALIAS);
102 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
103 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
104 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
110 /* ---------------------
112 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, uint32_t utf8)
117 aint = strlen( name );
121 if (utf8_encoding()) {
122 /* but name is an utf8 mac name */
125 /* global static variable... */
127 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
136 if (aint > MACFILELEN)
143 if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
144 aint = UTF8FILELEN_EARLY;
146 utf8 = vol->v_kTextEncoding;
147 memcpy(data, &utf8, sizeof(utf8));
148 data += sizeof(utf8);
151 memcpy(data, &temp, sizeof(temp));
152 data += sizeof(temp);
155 memcpy( data, src, aint );
165 * FIXME: PDINFO is UTF8 and doesn't need adp
167 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
168 (1 << FILPBIT_CDATE) |\
169 (1 << FILPBIT_MDATE) |\
170 (1 << FILPBIT_BDATE) |\
171 (1 << FILPBIT_FINFO) |\
172 (1 << FILPBIT_RFLEN) |\
173 (1 << FILPBIT_EXTRFLEN) |\
174 (1 << FILPBIT_PDINFO) |\
175 (1 << FILPBIT_FNUM) |\
176 (1 << FILPBIT_UNIXPR)))
179 * @brief Get CNID for did/upath args both from database and adouble file
181 * 1. Get the objects CNID as stored in its adouble file
182 * 2. Get the objects CNID from the database
183 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
184 * 4. In case 2 and 3 differ, store 3 in the adouble file
186 * @param vol (rw) volume
187 * @param adp (rw) adouble struct of object upath, might be NULL
188 * @param st (r) stat of upath, must NOT be NULL
189 * @param did (r) parent CNID of upath
190 * @param upath (r) name of object
191 * @param len (r) strlen of upath
193 uint32_t get_id(struct vol *vol,
195 const struct stat *st,
200 static int first = 1; /* mark if this func is called the first time */
202 uint32_t dbcnid = CNID_INVALID;
205 if (vol->v_cdb != NULL) {
206 /* prime aint with what we think is the cnid, set did to zero for
207 catching moved files */
208 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
210 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
211 /* Throw errors if cnid_add fails. */
212 if (dbcnid == CNID_INVALID) {
214 case CNID_ERR_CLOSE: /* the db is closed */
217 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
218 afp_errno = AFPERR_PARAM;
221 afp_errno = AFPERR_PARAM;
224 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
225 /* we have to do it here for "dbd" because it uses "lazy opening" */
226 /* In order to not end in a loop somehow with goto restart below */
228 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
229 cnid_close(vol->v_cdb);
230 free(vol->v_cnidscheme);
231 vol->v_cnidscheme = strdup("tdb");
233 int flags = CNID_FLAG_MEMORY;
234 if ((vol->v_flags & AFPVOL_NODEV)) {
235 flags |= CNID_FLAG_NODEV;
237 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
239 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
242 vol->v_flags |= AFPVOL_RO;
244 /* kill ourself with SIGUSR2 aka msg pending */
245 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
246 "Check server messages for details. Switching to read-only mode.");
247 kill(getpid(), SIGUSR2);
249 goto restart; /* not try again with the temp CNID db */
252 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
253 "Check server messages for details, can't recover from this state!");
257 afp_errno = AFPERR_MISC;
261 else if (adp && (adcnid != dbcnid)) { /* 4 */
262 /* Update the ressource fork. For a folder adp is always null */
263 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
264 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
265 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
276 /* -------------------------- */
277 int getmetadata(struct vol *vol,
279 struct path *path, struct dir *dir,
280 char *buf, size_t *buflen, struct adouble *adp)
282 char *data, *l_nameoff = NULL, *upath;
283 char *utf_nameoff = NULL;
288 u_char achar, fdType[4];
293 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
295 upath = path->u_name;
299 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
300 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
301 || (bitmap & (1 << FILPBIT_FNUM))) {
304 struct dir *cachedfile;
305 int len = strlen(upath);
306 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
307 id = cachedfile->d_did;
309 id = get_id(vol, adp, st, dir->d_did, upath, len);
311 /* Add it to the cache */
312 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
313 ntohl(dir->d_did), upath, ntohl(id));
315 /* Get macname from unixname first */
316 if (path->m_name == NULL) {
317 if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
318 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
324 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
325 || (bconchar(fullpath, '/') != BSTR_OK)
326 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
327 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
331 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
332 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
336 if ((dircache_add(vol, cachedfile)) != 0) {
337 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
345 if (id == CNID_INVALID)
349 path->m_name = utompath(vol, upath, id, utf8_encoding());
352 while ( bitmap != 0 ) {
353 while (( bitmap & 1 ) == 0 ) {
361 ad_getattr(adp, &ashort);
362 } else if (vol_inv_dots(vol) && *upath == '.') {
363 ashort = htons(ATTRBIT_INVISIBLE);
367 /* FIXME do we want a visual clue if the file is read only
370 accessmode(vol, ".", &ma, dir , NULL);
371 if ((ma.ma_user & AR_UWRITE)) {
372 accessmode(vol, upath, &ma, dir , st);
373 if (!(ma.ma_user & AR_UWRITE)) {
374 ashort |= htons(ATTRBIT_NOWRITE);
378 memcpy(data, &ashort, sizeof( ashort ));
379 data += sizeof( ashort );
380 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
381 path->u_name, ntohs(ashort));
385 memcpy(data, &dir->d_did, sizeof( uint32_t ));
386 data += sizeof( uint32_t );
387 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
388 path->u_name, ntohl(dir->d_did));
392 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
393 aint = AD_DATE_FROM_UNIX(st->st_mtime);
394 memcpy(data, &aint, sizeof( aint ));
395 data += sizeof( aint );
399 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
400 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
401 aint = AD_DATE_FROM_UNIX(st->st_mtime);
404 aint = AD_DATE_FROM_UNIX(st->st_mtime);
406 memcpy(data, &aint, sizeof( int ));
407 data += sizeof( int );
411 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
412 aint = AD_DATE_START;
413 memcpy(data, &aint, sizeof( int ));
414 data += sizeof( int );
418 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
419 data += ADEDLEN_FINDERI;
424 data += sizeof( uint16_t );
428 memset(data, 0, sizeof(uint16_t));
429 data += sizeof( uint16_t );
433 memcpy(data, &id, sizeof( id ));
434 data += sizeof( id );
435 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
436 path->u_name, ntohl(id));
440 if (st->st_size > 0xffffffff)
443 aint = htonl( st->st_size );
444 memcpy(data, &aint, sizeof( aint ));
445 data += sizeof( aint );
450 if (adp->ad_rlen > 0xffffffff)
453 aint = htonl( adp->ad_rlen);
457 memcpy(data, &aint, sizeof( aint ));
458 data += sizeof( aint );
461 /* Current client needs ProDOS info block for this file.
462 Use simple heuristic and let the Mac "type" string tell
463 us what the PD file code should be. Everything gets a
464 subtype of 0x0000 unless the original value was hashed
465 to "pXYZ" when we created it. See IA, Ver 2.
466 <shirsch@adelphia.net> */
467 case FILPBIT_PDINFO :
468 if (afp_version >= 30) { /* UTF8 name */
469 utf8 = kTextEncodingUTF8;
471 data += sizeof( uint16_t );
473 memcpy(data, &aint, sizeof( aint ));
474 data += sizeof( aint );
478 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
480 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
484 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
488 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
492 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
496 else if ( fdType[0] == 'p' ) {
498 ashort = (fdType[2] * 256) + fdType[3];
512 memcpy(data, &ashort, sizeof( ashort ));
513 data += sizeof( ashort );
514 memset(data, 0, sizeof( ashort ));
515 data += sizeof( ashort );
518 case FILPBIT_EXTDFLEN:
519 aint = htonl(st->st_size >> 32);
520 memcpy(data, &aint, sizeof( aint ));
521 data += sizeof( aint );
522 aint = htonl(st->st_size);
523 memcpy(data, &aint, sizeof( aint ));
524 data += sizeof( aint );
526 case FILPBIT_EXTRFLEN:
529 aint = htonl(adp->ad_rlen >> 32);
530 memcpy(data, &aint, sizeof( aint ));
531 data += sizeof( aint );
533 aint = htonl(adp->ad_rlen);
534 memcpy(data, &aint, sizeof( aint ));
535 data += sizeof( aint );
537 case FILPBIT_UNIXPR :
538 /* accessmode may change st_mode with ACLs */
539 accessmode(vol, upath, &ma, dir , st);
541 aint = htonl(st->st_uid);
542 memcpy( data, &aint, sizeof( aint ));
543 data += sizeof( aint );
544 aint = htonl(st->st_gid);
545 memcpy( data, &aint, sizeof( aint ));
546 data += sizeof( aint );
549 type == slnk indicates an OSX style symlink,
550 we have to add S_IFLNK to the mode, otherwise
551 10.3 clients freak out. */
555 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
556 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
562 memcpy( data, &aint, sizeof( aint ));
563 data += sizeof( aint );
565 *data++ = ma.ma_user;
566 *data++ = ma.ma_world;
567 *data++ = ma.ma_group;
568 *data++ = ma.ma_owner;
572 return( AFPERR_BITMAP );
578 ashort = htons( data - buf );
579 memcpy(l_nameoff, &ashort, sizeof( ashort ));
580 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
583 ashort = htons( data - buf );
584 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
585 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
587 *buflen = data - buf;
591 /* ----------------------- */
592 int getfilparams(struct vol *vol,
594 struct path *path, struct dir *dir,
595 char *buf, size_t *buflen )
597 struct adouble ad, *adp;
602 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
604 opened = PARAM_NEED_ADP(bitmap);
609 flags = (bitmap & (1 << FILPBIT_ATTR)) ? ADFLAGS_CHECK_OF : 0;
611 adp = of_ad(vol, path, &ad);
612 upath = path->u_name;
614 if ( ad_metadata( upath, flags, adp) < 0 ) {
617 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
618 upath, strerror(errno));
619 return AFPERR_ACCESS;
621 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
630 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
631 ad_close(adp, ADFLAGS_HF | flags);
636 /* ----------------------------- */
637 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
642 struct ofork *of = NULL;
644 int creatf, did, openf, retvalue = AFP_OK;
650 creatf = (unsigned char) *ibuf++;
652 memcpy(&vid, ibuf, sizeof( vid ));
653 ibuf += sizeof( vid );
655 if (NULL == ( vol = getvolbyvid( vid )) )
656 return( AFPERR_PARAM );
658 if (vol->v_flags & AFPVOL_RO)
661 memcpy(&did, ibuf, sizeof( did));
662 ibuf += sizeof( did );
664 if (NULL == ( dir = dirlookup( vol, did )) )
667 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
668 return get_afp_errno(AFPERR_PARAM);
669 if ( *s_path->m_name == '\0' )
670 return( AFPERR_BADTYPE );
672 upath = s_path->u_name;
675 /* if upath is deleted we already in trouble anyway */
676 if ((of = of_findname(s_path))) {
684 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
686 /* on a soft create, if the file is open then ad_open won't fail
687 because open syscall is not called */
688 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
690 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
694 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
695 return ( AFPERR_NOOBJ );
697 return( AFPERR_EXIST );
699 return( AFPERR_ACCESS );
702 return( AFPERR_DFULL );
704 return( AFPERR_PARAM );
707 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
708 /* FIXME with hard create on an existing file, we already
709 * corrupted the data file.
711 netatalk_unlink( upath );
712 ad_close( &ad, ADFLAGS_DF );
713 return AFPERR_ACCESS;
716 path = s_path->m_name;
717 ad_setname(&ad, path);
720 if (lstat(upath, &st) != 0) {
721 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
722 upath, strerror(errno));
723 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
727 (void)get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath));
730 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
731 fce_register_new_file(s_path);
736 setvoltime(obj, vol );
741 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
747 uint16_t vid, bitmap;
752 memcpy(&vid, ibuf, sizeof( vid ));
753 ibuf += sizeof( vid );
754 if (NULL == ( vol = getvolbyvid( vid )) ) {
755 return( AFPERR_PARAM );
758 if (vol->v_flags & AFPVOL_RO)
761 memcpy(&did, ibuf, sizeof( did ));
762 ibuf += sizeof( did );
763 if (NULL == ( dir = dirlookup( vol, did )) ) {
764 return afp_errno; /* was AFPERR_NOOBJ */
767 memcpy(&bitmap, ibuf, sizeof( bitmap ));
768 bitmap = ntohs( bitmap );
769 ibuf += sizeof( bitmap );
771 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
772 return get_afp_errno(AFPERR_PARAM);
775 if (path_isadir(s_path)) {
776 return( AFPERR_BADTYPE ); /* it's a directory */
779 if ( s_path->st_errno != 0 ) {
780 return( AFPERR_NOOBJ );
783 if ((u_long)ibuf & 1 ) {
787 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
788 setvoltime(obj, vol );
795 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
798 extern struct path Cur_Path;
800 int setfilparams(struct vol *vol,
801 struct path *path, uint16_t f_bitmap, char *buf )
803 struct adouble ad, *adp;
805 int bit, isad = 1, err = AFP_OK;
807 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
808 uint16_t ashort, bshort, oshort;
811 uint16_t upriv_bit = 0;
813 int change_mdate = 0;
814 int change_parent_mdate = 0;
819 uint16_t bitmap = f_bitmap;
820 uint32_t cdate,bdate;
821 u_char finder_buf[32];
825 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
828 adp = of_ad(vol, path, &ad);
829 upath = path->u_name;
831 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
832 return AFPERR_ACCESS;
835 /* with unix priv maybe we have to change adouble file priv first */
837 while ( bitmap != 0 ) {
838 while (( bitmap & 1 ) == 0 ) {
845 memcpy(&ashort, buf, sizeof( ashort ));
846 buf += sizeof( ashort );
850 memcpy(&cdate, buf, sizeof(cdate));
851 buf += sizeof( cdate );
854 memcpy(&newdate, buf, sizeof( newdate ));
855 buf += sizeof( newdate );
859 memcpy(&bdate, buf, sizeof( bdate));
860 buf += sizeof( bdate );
864 memcpy(finder_buf, buf, 32 );
865 if (memcmp(buf, "slnkrhap", 8) == 0 && !S_ISLNK(path->st.st_mode)) {
869 char buf[PATH_MAX+1];
870 if ((fp = open(path->u_name, O_RDONLY)) >= 0) {
871 if ((len = read(fp, buf, PATH_MAX+1))) {
872 if (unlink(path->u_name) == 0) {
874 erc = symlink(buf, path->u_name);
883 goto setfilparam_done;
889 case FILPBIT_UNIXPR :
890 if (!vol_unix_priv(vol)) {
891 /* this volume doesn't use unix priv */
897 change_parent_mdate = 1;
899 memcpy( &aint, buf, sizeof( aint ));
900 f_uid = ntohl (aint);
901 buf += sizeof( aint );
902 memcpy( &aint, buf, sizeof( aint ));
903 f_gid = ntohl (aint);
904 buf += sizeof( aint );
905 setfilowner(vol, f_uid, f_gid, path);
907 memcpy( &upriv, buf, sizeof( upriv ));
908 buf += sizeof( upriv );
909 upriv = ntohl (upriv);
910 if ((upriv & S_IWUSR)) {
911 setfilunixmode(vol, path, upriv);
918 case FILPBIT_PDINFO :
919 if (afp_version < 30) { /* else it's UTF8 name */
922 /* Keep special case to support crlf translations */
923 if ((unsigned int) achar == 0x04) {
924 fdType = (u_char *)"TEXT";
927 xyy[0] = ( u_char ) 'p';
938 /* break while loop */
947 /* second try with adouble open
949 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
950 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
952 * For some things, we don't need an adouble header:
953 * - change of modification date
954 * - UNIX privs (Bug-ID #2863424)
956 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
957 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
958 return AFPERR_ACCESS;
960 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
962 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
963 ad_setname(adp, path->m_name);
968 while ( bitmap != 0 ) {
969 while (( bitmap & 1 ) == 0 ) {
976 ad_getattr(adp, &bshort);
978 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
979 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
983 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
984 change_parent_mdate = 1;
985 ad_setattr(adp, bshort);
988 ad_setdate(adp, AD_DATE_CREATE, cdate);
993 ad_setdate(adp, AD_DATE_BACKUP, bdate);
996 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
998 case FILPBIT_UNIXPR :
1000 setfilunixmode(vol, path, upriv);
1003 case FILPBIT_PDINFO :
1004 if (afp_version < 30) { /* else it's UTF8 name */
1005 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1006 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1011 err = AFPERR_BITMAP;
1012 goto setfilparam_done;
1019 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1020 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1024 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1025 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1031 ad_close(adp, ADFLAGS_HF);
1034 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1035 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1036 bitmap = 1<<FILPBIT_MDATE;
1037 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1041 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1047 * renamefile and copyfile take the old and new unix pathnames
1048 * and the new mac name.
1050 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1051 * passing -1 means this is not used, src path is a full path
1052 * src the source path
1053 * dst the dest filename in current dir
1054 * newname the dest mac name
1055 * adp adouble struct of src file, if open, or & zeroed one
1058 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1062 LOG(log_debug, logtype_afpd,
1063 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1065 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1068 return( AFPERR_NOOBJ );
1071 return( AFPERR_ACCESS );
1073 return AFPERR_VLOCK;
1074 case EXDEV : /* Cross device move -- try copy */
1075 /* NOTE: with open file it's an error because after the copy we will
1076 * get two files, it's fixable for our process (eg reopen the new file, get the
1077 * locks, and so on. But it doesn't solve the case with a second process
1079 if (adp->ad_open_forks) {
1080 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1081 return AFPERR_OLOCK; /* little lie */
1083 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1084 /* on error copyfile delete dest */
1087 return deletefile(vol, sdir_fd, src, 0);
1089 return( AFPERR_PARAM );
1093 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1097 /* try to undo the data fork rename,
1098 * we know we are on the same device
1101 unix_rename(-1, dst, sdir_fd, src );
1102 /* return the first error */
1105 return AFPERR_NOOBJ;
1108 return AFPERR_ACCESS ;
1110 return AFPERR_VLOCK;
1112 return AFPERR_PARAM ;
1117 /* don't care if we can't open the newly renamed ressource fork */
1118 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1119 ad_setname(adp, newname);
1121 ad_close( adp, ADFLAGS_HF );
1128 convert a Mac long name to an utf8 name,
1130 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1134 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1140 /* ---------------- */
1141 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1148 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1154 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1155 if (afp_version >= 30) {
1156 /* convert it to UTF8
1158 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1162 strncpy( newname, ibuf, plen );
1163 newname[ plen ] = '\0';
1165 if (strlen(newname) != plen) {
1166 /* there's \0 in newname, e.g. it's a pathname not
1174 memcpy(&hint, ibuf, sizeof(hint));
1175 ibuf += sizeof(hint);
1177 memcpy(&len16, ibuf, sizeof(len16));
1178 ibuf += sizeof(len16);
1179 plen = ntohs(len16);
1182 if (plen > AFPOBJ_TMPSIZ) {
1185 strncpy( newname, ibuf, plen );
1186 newname[ plen ] = '\0';
1187 if (strlen(newname) != plen) {
1196 /* -----------------------------------
1198 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1200 struct vol *s_vol, *d_vol;
1202 char *newname, *p, *upath;
1203 struct path *s_path;
1204 uint32_t sdid, ddid;
1205 int err, retvalue = AFP_OK;
1206 uint16_t svid, dvid;
1208 struct adouble ad, *adp;
1214 memcpy(&svid, ibuf, sizeof( svid ));
1215 ibuf += sizeof( svid );
1216 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1217 return( AFPERR_PARAM );
1220 memcpy(&sdid, ibuf, sizeof( sdid ));
1221 ibuf += sizeof( sdid );
1222 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1226 memcpy(&dvid, ibuf, sizeof( dvid ));
1227 ibuf += sizeof( dvid );
1228 memcpy(&ddid, ibuf, sizeof( ddid ));
1229 ibuf += sizeof( ddid );
1231 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1232 return get_afp_errno(AFPERR_PARAM);
1234 if ( path_isadir(s_path) ) {
1235 return( AFPERR_BADTYPE );
1238 /* don't allow copies when the file is open.
1239 * XXX: the spec only calls for read/deny write access.
1240 * however, copyfile doesn't have any of that info,
1241 * and locks need to stay coherent. as a result,
1242 * we just balk if the file is opened already. */
1244 adp = of_ad(s_vol, s_path, &ad);
1246 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1247 return AFPERR_DENYCONF;
1249 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1250 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1253 retvalue = AFPERR_DENYCONF;
1257 newname = obj->newtmp;
1258 strcpy( newname, s_path->m_name );
1260 p = ctoupath( s_vol, curdir, newname );
1262 retvalue = AFPERR_PARAM;
1266 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1267 retvalue = AFPERR_PARAM;
1271 if (d_vol->v_flags & AFPVOL_RO) {
1272 retvalue = AFPERR_VLOCK;
1276 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1277 retvalue = afp_errno;
1281 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1282 retvalue = get_afp_errno(AFPERR_NOOBJ);
1286 if ( *s_path->m_name != '\0' ) {
1287 retvalue =path_error(s_path, AFPERR_NOOBJ);
1291 /* one of the handful of places that knows about the path type */
1292 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1293 retvalue = AFPERR_PARAM;
1296 /* newname is always only a filename so curdir *is* its
1299 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1300 retvalue =AFPERR_PARAM;
1304 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1310 setvoltime(obj, d_vol );
1313 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1317 /* ----------------------------------
1318 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1319 * because we are doing it elsewhere.
1320 * currently if newname is NULL then adp is NULL.
1322 int copyfile(const struct vol *s_vol,
1323 const struct vol *d_vol,
1328 struct adouble *adp)
1330 struct adouble ads, add;
1337 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1338 sfd, src, dst, newname);
1341 ad_init(&ads, s_vol);
1345 adflags = ADFLAGS_DF | ADFLAGS_RF | ADFLAGS_NORF;
1347 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1352 if (!AD_RSRC_OPEN(adp))
1353 /* no resource fork, don't create one for dst file */
1354 adflags &= ~ADFLAGS_RF;
1356 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1358 if (stat_result < 0) {
1359 /* unlikely but if fstat fails, the default file mode will be 0666. */
1360 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1363 ad_init(&add, d_vol);
1364 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1366 ad_close( adp, adflags );
1367 if (EEXIST != ret_err) {
1368 deletefile(d_vol, -1, dst, 0);
1371 return AFPERR_EXIST;
1374 if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1375 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1378 if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1379 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1384 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1385 /* set the new name in the resource fork */
1386 ad_copy_header(&add, adp);
1387 ad_setname(&add, newname);
1390 ad_close( adp, adflags );
1392 if (ad_close( &add, adflags ) <0) {
1397 deletefile(d_vol, -1, dst, 0);
1399 else if (stat_result == 0) {
1400 /* set dest modification date to src date */
1403 ut.actime = ut.modtime = st.st_mtime;
1405 /* FIXME netatalk doesn't use resource fork file date
1406 * but maybe we should set its modtime too.
1411 switch ( ret_err ) {
1417 return AFPERR_DFULL;
1419 return AFPERR_NOOBJ;
1421 return AFPERR_ACCESS;
1423 return AFPERR_VLOCK;
1425 return AFPERR_PARAM;
1429 /* -----------------------------------
1430 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1431 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1433 when deletefile is called we don't have lock on it, file is closed (for us)
1434 untrue if called by renamefile
1436 ad_open always try to open file RDWR first and ad_lock takes care of
1437 WRITE lock on read only file.
1440 static int check_attrib(struct adouble *adp)
1442 uint16_t bshort = 0;
1444 ad_getattr(adp, &bshort);
1446 * Does kFPDeleteInhibitBit (bit 8) set?
1448 if ((bshort & htons(ATTRBIT_NODELETE))) {
1449 return AFPERR_OLOCK;
1451 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1457 * dirfd can be used for unlinkat semantics
1459 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1462 struct adouble *adp = NULL;
1463 int adflags, err = AFP_OK;
1466 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1470 /* was EACCESS error try to get only metadata */
1471 /* we never want to create a resource fork here, we are going to delete it
1472 * moreover sometimes deletefile is called with a no existent file and
1473 * ad_open would create a 0 byte resource fork
1475 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1476 if ((err = check_attrib(&ad))) {
1477 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1484 /* try to open both forks at once */
1485 adflags = ADFLAGS_DF;
1486 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1491 case EACCES: /* maybe it's a file with no write mode for us */
1492 break; /* was return AFPERR_ACCESS;*/
1505 if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1506 adflags |= ADFLAGS_RF;
1507 /* FIXME we have a pb here because we want to know if a file is open
1508 * there's a 'priority inversion' if you can't open the ressource fork RW
1509 * you can delete it if it's open because you can't get a write lock.
1511 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1514 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1516 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1522 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1523 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1525 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1527 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1528 cnid_delete(vol->v_cdb, id);
1534 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1537 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1542 /* ------------------------------------ */
1543 /* return a file id */
1544 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1553 struct path *s_path;
1559 memcpy(&vid, ibuf, sizeof(vid));
1560 ibuf += sizeof(vid);
1562 if (NULL == ( vol = getvolbyvid( vid )) ) {
1563 return( AFPERR_PARAM);
1566 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1570 if (vol->v_flags & AFPVOL_RO)
1571 return AFPERR_VLOCK;
1573 memcpy(&did, ibuf, sizeof( did ));
1574 ibuf += sizeof(did);
1576 if (NULL == ( dir = dirlookup( vol, did )) ) {
1577 return afp_errno; /* was AFPERR_PARAM */
1580 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1581 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1584 if ( path_isadir(s_path) ) {
1585 return( AFPERR_BADTYPE );
1588 upath = s_path->u_name;
1589 switch (s_path->st_errno) {
1591 break; /* success */
1594 return AFPERR_ACCESS;
1596 return AFPERR_NOOBJ;
1598 return AFPERR_PARAM;
1601 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1602 memcpy(rbuf, &id, sizeof(id));
1603 *rbuflen = sizeof(id);
1604 return AFPERR_EXISTID;
1607 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1608 memcpy(rbuf, &id, sizeof(id));
1609 *rbuflen = sizeof(id);
1616 /* ------------------------------- */
1622 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1625 struct reenum *param = data;
1626 struct vol *vol = param->vol;
1627 cnid_t did = param->did;
1630 if ( lstat(de->d_name, &path.st) < 0 )
1633 /* update or add to cnid */
1634 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1639 /* --------------------
1640 * Ok the db is out of synch with the dir.
1641 * but if it's a deleted file we don't want to do it again and again.
1644 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1650 if (vol->v_cdb == NULL) {
1654 /* FIXME use of_statdir ? */
1655 if (lstat(name, &st)) {
1659 if (dirreenumerate(dir, &st)) {
1660 /* we already did it once and the dir haven't been modified */
1661 return dir->d_offcnt;
1665 data.did = dir->d_did;
1666 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1667 setdiroffcnt(curdir, &st, ret);
1668 dir->d_flags |= DIRF_CNID;
1674 /* ------------------------------
1675 resolve a file id */
1676 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1685 uint16_t vid, bitmap;
1687 static char buffer[12 + MAXPATHLEN + 1];
1688 int len = 12 + MAXPATHLEN + 1;
1693 memcpy(&vid, ibuf, sizeof(vid));
1694 ibuf += sizeof(vid);
1696 if (NULL == ( vol = getvolbyvid( vid )) ) {
1697 return( AFPERR_PARAM);
1700 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1704 memcpy(&id, ibuf, sizeof( id ));
1709 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1713 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1714 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1717 if (NULL == ( dir = dirlookup( vol, id )) ) {
1718 return AFPERR_NOID; /* idem AFPERR_PARAM */
1720 if (movecwd(vol, dir) < 0) {
1724 return AFPERR_ACCESS;
1728 return AFPERR_PARAM;
1732 memset(&path, 0, sizeof(path));
1733 path.u_name = upath;
1734 if ( of_stat(&path) < 0 ) {
1736 /* with nfs and our working directory is deleted */
1737 if (errno == ESTALE) {
1741 if ( errno == ENOENT && !retry) {
1742 /* cnid db is out of sync, reenumerate the directory and update ids */
1743 reenumerate_id(vol, ".", dir);
1751 return AFPERR_ACCESS;
1755 return AFPERR_PARAM;
1759 /* directories are bad */
1760 if (S_ISDIR(path.st.st_mode)) {
1761 /* OS9 and OSX don't return the same error code */
1762 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1765 memcpy(&bitmap, ibuf, sizeof(bitmap));
1766 bitmap = ntohs( bitmap );
1767 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1771 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1772 rbuf + sizeof(bitmap), &buflen))) {
1775 *rbuflen = buflen + sizeof(bitmap);
1776 memcpy(rbuf, ibuf, sizeof(bitmap));
1781 /* ------------------------------ */
1782 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1792 static char buffer[12 + MAXPATHLEN + 1];
1793 int len = 12 + MAXPATHLEN + 1;
1798 memcpy(&vid, ibuf, sizeof(vid));
1799 ibuf += sizeof(vid);
1801 if (NULL == ( vol = getvolbyvid( vid )) ) {
1802 return( AFPERR_PARAM);
1805 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1809 if (vol->v_flags & AFPVOL_RO)
1810 return AFPERR_VLOCK;
1812 memcpy(&id, ibuf, sizeof( id ));
1816 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1820 if (NULL == ( dir = dirlookup( vol, id )) ) {
1821 if (afp_errno == AFPERR_NOOBJ) {
1825 return( AFPERR_PARAM );
1829 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1833 return AFPERR_ACCESS;
1838 /* still try to delete the id */
1842 return AFPERR_PARAM;
1845 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1846 return AFPERR_BADTYPE;
1849 if (cnid_delete(vol->v_cdb, fileid)) {
1852 return AFPERR_VLOCK;
1855 return AFPERR_ACCESS;
1857 return AFPERR_PARAM;
1864 /* ------------------------------ */
1865 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
1869 if (path->st_errno) {
1870 switch (path->st_errno) {
1872 afp_errno = AFPERR_NOID;
1876 afp_errno = AFPERR_ACCESS;
1879 afp_errno = AFPERR_PARAM;
1884 /* we use file_access both for legacy Mac perm and
1885 * for unix privilege, rename will take care of folder perms
1887 if (file_access(path, OPENACC_WR ) < 0) {
1888 afp_errno = AFPERR_ACCESS;
1892 if ((*of = of_findname(path))) {
1893 /* reuse struct adouble so it won't break locks */
1897 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
1899 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1901 * The user must have the Read & Write privilege for both files in order to use this command.
1903 ad_close(adp, ADFLAGS_HF);
1904 afp_errno = AFPERR_ACCESS;
1911 #define APPLETEMP ".AppleTempXXXXXX"
1913 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1915 struct stat srcst, destst;
1917 struct dir *dir, *sdir;
1918 char *spath, temp[17], *p;
1919 char *supath, *upath;
1924 struct adouble *adsp = NULL;
1925 struct adouble *addp = NULL;
1926 struct ofork *s_of = NULL;
1927 struct ofork *d_of = NULL;
1940 memcpy(&vid, ibuf, sizeof(vid));
1941 ibuf += sizeof(vid);
1943 if (NULL == ( vol = getvolbyvid( vid )) ) {
1944 return( AFPERR_PARAM);
1947 if ((vol->v_flags & AFPVOL_RO))
1948 return AFPERR_VLOCK;
1950 /* source and destination dids */
1951 memcpy(&sid, ibuf, sizeof(sid));
1952 ibuf += sizeof(sid);
1953 memcpy(&did, ibuf, sizeof(did));
1954 ibuf += sizeof(did);
1957 if (NULL == (dir = dirlookup( vol, sid )) ) {
1958 return afp_errno; /* was AFPERR_PARAM */
1961 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1962 return get_afp_errno(AFPERR_NOOBJ);
1965 if ( path_isadir(path) ) {
1966 return AFPERR_BADTYPE; /* it's a dir */
1969 /* save some stuff */
1972 spath = obj->oldtmp;
1973 supath = obj->newtmp;
1974 strcpy(spath, path->m_name);
1975 strcpy(supath, path->u_name); /* this is for the cnid changing */
1976 p = absupath( vol, sdir, supath);
1978 /* pathname too long */
1979 return AFPERR_PARAM ;
1983 if (!(adsp = find_adouble( path, &s_of, &ads))) {
1987 /* ***** from here we may have resource fork open **** */
1989 /* look for the source cnid. if it doesn't exist, don't worry about
1991 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
1993 if (NULL == ( dir = dirlookup( vol, did )) ) {
1994 err = afp_errno; /* was AFPERR_PARAM */
1995 goto err_exchangefile;
1998 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1999 err = get_afp_errno(AFPERR_NOOBJ);
2000 goto err_exchangefile;
2003 if ( path_isadir(path) ) {
2004 err = AFPERR_BADTYPE;
2005 goto err_exchangefile;
2008 /* FPExchangeFiles is the only call that can return the SameObj
2010 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2011 err = AFPERR_SAMEOBJ;
2012 goto err_exchangefile;
2016 if (!(addp = find_adouble( path, &d_of, &add))) {
2018 goto err_exchangefile;
2022 /* they are not on the same device and at least one is open
2023 * FIXME broken for for crossdev and adouble v2
2026 crossdev = (srcst.st_dev != destst.st_dev);
2027 if (/* (d_of || s_of) && */ crossdev) {
2029 goto err_exchangefile;
2032 /* look for destination id. */
2033 upath = path->u_name;
2034 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2036 /* construct a temp name.
2037 * NOTE: the temp file will be in the dest file's directory. it
2038 * will also be inaccessible from AFP. */
2039 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2040 if (!mktemp(temp)) {
2042 goto err_exchangefile;
2046 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2047 ad_close(adsp, ADFLAGS_HF);
2048 ad_close(addp, ADFLAGS_HF);
2051 /* now, quickly rename the file. we error if we can't. */
2052 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2053 goto err_exchangefile;
2054 of_rename(vol, s_of, sdir, spath, curdir, temp);
2056 /* rename destination to source */
2057 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2058 goto err_src_to_tmp;
2059 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2061 /* rename temp to destination */
2062 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2063 goto err_dest_to_src;
2064 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2066 /* id's need switching. src -> dest and dest -> src.
2067 * we need to re-stat() if it was a cross device copy.
2070 cnid_delete(vol->v_cdb, sid);
2072 cnid_delete(vol->v_cdb, did);
2074 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2075 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2077 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2078 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2083 err = AFPERR_ACCESS;
2088 goto err_temp_to_dest;
2091 /* here we need to reopen if crossdev */
2092 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2097 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2102 /* change perms, src gets dest perm and vice versa */
2107 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2108 err = AFP_OK; /* ignore error */
2109 goto err_temp_to_dest;
2113 * we need to exchange ACL entries as well
2115 /* exchange_acls(vol, p, upath); */
2120 path->m_name = NULL;
2121 path->u_name = upath;
2123 setfilunixmode(vol, path, destst.st_mode);
2124 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2131 setfilunixmode(vol, path, srcst.st_mode);
2132 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2134 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2135 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2140 goto err_exchangefile;
2142 /* all this stuff is so that we can unwind a failed operation
2145 /* rename dest to temp */
2146 renamefile(vol, -1, upath, temp, temp, adsp);
2147 of_rename(vol, s_of, curdir, upath, curdir, temp);
2150 /* rename source back to dest */
2151 renamefile(vol, -1, p, upath, path->m_name, addp);
2152 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2155 /* rename temp back to source */
2156 renamefile(vol, -1, temp, p, spath, adsp);
2157 of_rename(vol, s_of, curdir, temp, sdir, spath);
2160 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2161 ad_close(adsp, ADFLAGS_HF);
2163 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2164 ad_close(addp, ADFLAGS_HF);
2168 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2169 (void)dir_remove(vol, cached);
2170 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2171 (void)dir_remove(vol, cached);