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)
80 void *ad_finder = NULL;
84 ad_finder = ad_entry(adp, ADEID_FINDERI);
87 memcpy(data, ad_finder, ADEDLEN_FINDERI);
89 if (default_type(ad_finder))
93 memcpy(data, ufinderi, ADEDLEN_FINDERI);
95 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
98 ashort = htons(FINDERINFO_INVISIBLE);
99 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
105 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
106 linkflag |= htons(FINDERINFO_ISALIAS);
107 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
108 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
109 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
113 /** Only enter if no appledouble information and no finder information found. */
114 if (chk_ext && (em = getextmap( upath ))) {
115 memcpy(data, em->em_type, sizeof( em->em_type ));
116 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
121 /* ---------------------
123 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, uint32_t utf8)
128 aint = strlen( name );
132 if (utf8_encoding()) {
133 /* but name is an utf8 mac name */
136 /* global static variable... */
138 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
147 if (aint > MACFILELEN)
154 if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
155 aint = UTF8FILELEN_EARLY;
157 utf8 = vol->v_kTextEncoding;
158 memcpy(data, &utf8, sizeof(utf8));
159 data += sizeof(utf8);
162 memcpy(data, &temp, sizeof(temp));
163 data += sizeof(temp);
166 memcpy( data, src, aint );
176 * FIXME: PDINFO is UTF8 and doesn't need adp
178 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
179 (1 << FILPBIT_CDATE) |\
180 (1 << FILPBIT_MDATE) |\
181 (1 << FILPBIT_BDATE) |\
182 (1 << FILPBIT_FINFO) |\
183 (1 << FILPBIT_RFLEN) |\
184 (1 << FILPBIT_EXTRFLEN) |\
185 (1 << FILPBIT_PDINFO) |\
186 (1 << FILPBIT_FNUM) |\
187 (1 << FILPBIT_UNIXPR)))
190 * @brief Get CNID for did/upath args both from database and adouble file
192 * 1. Get the objects CNID as stored in its adouble file
193 * 2. Get the objects CNID from the database
194 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
195 * 4. In case 2 and 3 differ, store 3 in the adouble file
197 * @param vol (rw) volume
198 * @param adp (rw) adouble struct of object upath, might be NULL
199 * @param st (r) stat of upath, must NOT be NULL
200 * @param did (r) parent CNID of upath
201 * @param upath (r) name of object
202 * @param len (r) strlen of upath
204 uint32_t get_id(struct vol *vol,
206 const struct stat *st,
211 static int first = 1; /* mark if this func is called the first time */
213 uint32_t dbcnid = CNID_INVALID;
216 if (vol->v_cdb != NULL) {
217 /* prime aint with what we think is the cnid, set did to zero for
218 catching moved files */
219 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
221 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
222 /* Throw errors if cnid_add fails. */
223 if (dbcnid == CNID_INVALID) {
225 case CNID_ERR_CLOSE: /* the db is closed */
228 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
229 afp_errno = AFPERR_PARAM;
232 afp_errno = AFPERR_PARAM;
235 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
236 /* we have to do it here for "dbd" because it uses "lazy opening" */
237 /* In order to not end in a loop somehow with goto restart below */
239 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
240 cnid_close(vol->v_cdb);
241 free(vol->v_cnidscheme);
242 vol->v_cnidscheme = strdup("tdb");
244 int flags = CNID_FLAG_MEMORY;
245 if ((vol->v_flags & AFPVOL_NODEV)) {
246 flags |= CNID_FLAG_NODEV;
248 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
250 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
253 vol->v_flags |= AFPVOL_RO;
255 /* kill ourself with SIGUSR2 aka msg pending */
256 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
257 "Check server messages for details. Switching to read-only mode.");
258 kill(getpid(), SIGUSR2);
260 goto restart; /* not try again with the temp CNID db */
263 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
264 "Check server messages for details, can't recover from this state!");
268 afp_errno = AFPERR_MISC;
272 else if (adp && (adcnid != dbcnid)) { /* 4 */
273 /* Update the ressource fork. For a folder adp is always null */
274 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
275 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
276 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
287 /* -------------------------- */
288 int getmetadata(struct vol *vol,
290 struct path *path, struct dir *dir,
291 char *buf, size_t *buflen, struct adouble *adp)
293 char *data, *l_nameoff = NULL, *upath;
294 char *utf_nameoff = NULL;
299 u_char achar, fdType[4];
304 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
306 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))) {
315 struct dir *cachedfile;
316 int len = strlen(upath);
317 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
318 id = cachedfile->d_did;
320 id = get_id(vol, adp, st, dir->d_did, upath, len);
322 /* Add it to the cache */
323 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
324 ntohl(dir->d_did), upath, ntohl(id));
326 /* Get macname from unixname first */
327 if (path->m_name == NULL) {
328 if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
329 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
335 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
336 || (bconchar(fullpath, '/') != BSTR_OK)
337 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
338 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
342 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
343 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
347 if ((dircache_add(vol, cachedfile)) != 0) {
348 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
356 if (id == CNID_INVALID)
360 path->m_name = utompath(vol, upath, id, utf8_encoding());
363 while ( bitmap != 0 ) {
364 while (( bitmap & 1 ) == 0 ) {
372 ad_getattr(adp, &ashort);
373 } else if (vol_inv_dots(vol) && *upath == '.') {
374 ashort = htons(ATTRBIT_INVISIBLE);
378 /* FIXME do we want a visual clue if the file is read only
381 accessmode(vol, ".", &ma, dir , NULL);
382 if ((ma.ma_user & AR_UWRITE)) {
383 accessmode(vol, upath, &ma, dir , st);
384 if (!(ma.ma_user & AR_UWRITE)) {
385 ashort |= htons(ATTRBIT_NOWRITE);
389 memcpy(data, &ashort, sizeof( ashort ));
390 data += sizeof( ashort );
391 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
392 path->u_name, ntohs(ashort));
396 memcpy(data, &dir->d_did, sizeof( uint32_t ));
397 data += sizeof( uint32_t );
398 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
399 path->u_name, ntohl(dir->d_did));
403 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
404 aint = AD_DATE_FROM_UNIX(st->st_mtime);
405 memcpy(data, &aint, sizeof( aint ));
406 data += sizeof( aint );
410 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
411 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
412 aint = AD_DATE_FROM_UNIX(st->st_mtime);
415 aint = AD_DATE_FROM_UNIX(st->st_mtime);
417 memcpy(data, &aint, sizeof( int ));
418 data += sizeof( int );
422 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
423 aint = AD_DATE_START;
424 memcpy(data, &aint, sizeof( int ));
425 data += sizeof( int );
429 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
430 data += ADEDLEN_FINDERI;
435 data += sizeof( uint16_t );
439 memset(data, 0, sizeof(uint16_t));
440 data += sizeof( uint16_t );
444 memcpy(data, &id, sizeof( id ));
445 data += sizeof( id );
446 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
447 path->u_name, ntohl(id));
451 if (st->st_size > 0xffffffff)
454 aint = htonl( st->st_size );
455 memcpy(data, &aint, sizeof( aint ));
456 data += sizeof( aint );
461 if (adp->ad_rlen > 0xffffffff)
464 aint = htonl( adp->ad_rlen);
468 memcpy(data, &aint, sizeof( aint ));
469 data += sizeof( aint );
472 /* Current client needs ProDOS info block for this file.
473 Use simple heuristic and let the Mac "type" string tell
474 us what the PD file code should be. Everything gets a
475 subtype of 0x0000 unless the original value was hashed
476 to "pXYZ" when we created it. See IA, Ver 2.
477 <shirsch@adelphia.net> */
478 case FILPBIT_PDINFO :
479 if (afp_version >= 30) { /* UTF8 name */
480 utf8 = kTextEncodingUTF8;
482 data += sizeof( uint16_t );
484 memcpy(data, &aint, sizeof( aint ));
485 data += sizeof( aint );
489 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
491 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
495 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
499 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
503 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
507 else if ( fdType[0] == 'p' ) {
509 ashort = (fdType[2] * 256) + fdType[3];
523 memcpy(data, &ashort, sizeof( ashort ));
524 data += sizeof( ashort );
525 memset(data, 0, sizeof( ashort ));
526 data += sizeof( ashort );
529 case FILPBIT_EXTDFLEN:
530 aint = htonl(st->st_size >> 32);
531 memcpy(data, &aint, sizeof( aint ));
532 data += sizeof( aint );
533 aint = htonl(st->st_size);
534 memcpy(data, &aint, sizeof( aint ));
535 data += sizeof( aint );
537 case FILPBIT_EXTRFLEN:
540 aint = htonl(adp->ad_rlen >> 32);
541 memcpy(data, &aint, sizeof( aint ));
542 data += sizeof( aint );
544 aint = htonl(adp->ad_rlen);
545 memcpy(data, &aint, sizeof( aint ));
546 data += sizeof( aint );
548 case FILPBIT_UNIXPR :
549 /* accessmode may change st_mode with ACLs */
550 accessmode(vol, upath, &ma, dir , st);
552 aint = htonl(st->st_uid);
553 memcpy( data, &aint, sizeof( aint ));
554 data += sizeof( aint );
555 aint = htonl(st->st_gid);
556 memcpy( data, &aint, sizeof( aint ));
557 data += sizeof( aint );
560 type == slnk indicates an OSX style symlink,
561 we have to add S_IFLNK to the mode, otherwise
562 10.3 clients freak out. */
566 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
567 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
573 memcpy( data, &aint, sizeof( aint ));
574 data += sizeof( aint );
576 *data++ = ma.ma_user;
577 *data++ = ma.ma_world;
578 *data++ = ma.ma_group;
579 *data++ = ma.ma_owner;
583 return( AFPERR_BITMAP );
589 ashort = htons( data - buf );
590 memcpy(l_nameoff, &ashort, sizeof( ashort ));
591 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
594 ashort = htons( data - buf );
595 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
596 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
598 *buflen = data - buf;
602 /* ----------------------- */
603 int getfilparams(struct vol *vol,
605 struct path *path, struct dir *dir,
606 char *buf, size_t *buflen )
608 struct adouble ad, *adp;
613 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
615 opened = PARAM_NEED_ADP(bitmap);
620 flags = (bitmap & (1 << FILPBIT_ATTR)) ? ADFLAGS_CHECK_OF : 0;
622 adp = of_ad(vol, path, &ad);
623 upath = path->u_name;
625 if ( ad_metadata( upath, flags, adp) < 0 ) {
628 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
629 upath, strerror(errno));
630 return AFPERR_ACCESS;
632 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
641 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
642 ad_close(adp, ADFLAGS_HF | flags);
647 /* ----------------------------- */
648 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
653 struct ofork *of = NULL;
655 int creatf, did, openf, retvalue = AFP_OK;
661 creatf = (unsigned char) *ibuf++;
663 memcpy(&vid, ibuf, sizeof( vid ));
664 ibuf += sizeof( vid );
666 if (NULL == ( vol = getvolbyvid( vid )) )
667 return( AFPERR_PARAM );
669 if (vol->v_flags & AFPVOL_RO)
672 memcpy(&did, ibuf, sizeof( did));
673 ibuf += sizeof( did );
675 if (NULL == ( dir = dirlookup( vol, did )) )
678 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
679 return get_afp_errno(AFPERR_PARAM);
680 if ( *s_path->m_name == '\0' )
681 return( AFPERR_BADTYPE );
683 upath = s_path->u_name;
686 /* if upath is deleted we already in trouble anyway */
687 if ((of = of_findname(s_path))) {
695 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
697 /* on a soft create, if the file is open then ad_open won't fail
698 because open syscall is not called */
699 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
701 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
705 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
706 return ( AFPERR_NOOBJ );
708 return( AFPERR_EXIST );
710 return( AFPERR_ACCESS );
713 return( AFPERR_DFULL );
715 return( AFPERR_PARAM );
718 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
719 /* FIXME with hard create on an existing file, we already
720 * corrupted the data file.
722 netatalk_unlink( upath );
723 ad_close( &ad, ADFLAGS_DF );
724 return AFPERR_ACCESS;
727 path = s_path->m_name;
728 ad_setname(&ad, path);
731 if (lstat(upath, &st) != 0) {
732 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
733 upath, strerror(errno));
734 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
738 (void)get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath));
741 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
742 fce_register_new_file(s_path);
747 setvoltime(obj, vol );
752 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
758 uint16_t vid, bitmap;
763 memcpy(&vid, ibuf, sizeof( vid ));
764 ibuf += sizeof( vid );
765 if (NULL == ( vol = getvolbyvid( vid )) ) {
766 return( AFPERR_PARAM );
769 if (vol->v_flags & AFPVOL_RO)
772 memcpy(&did, ibuf, sizeof( did ));
773 ibuf += sizeof( did );
774 if (NULL == ( dir = dirlookup( vol, did )) ) {
775 return afp_errno; /* was AFPERR_NOOBJ */
778 memcpy(&bitmap, ibuf, sizeof( bitmap ));
779 bitmap = ntohs( bitmap );
780 ibuf += sizeof( bitmap );
782 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
783 return get_afp_errno(AFPERR_PARAM);
786 if (path_isadir(s_path)) {
787 return( AFPERR_BADTYPE ); /* it's a directory */
790 if ( s_path->st_errno != 0 ) {
791 return( AFPERR_NOOBJ );
794 if ((u_long)ibuf & 1 ) {
798 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
799 setvoltime(obj, vol );
806 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
809 extern struct path Cur_Path;
811 int setfilparams(struct vol *vol,
812 struct path *path, uint16_t f_bitmap, char *buf )
814 struct adouble ad, *adp;
816 int bit, isad = 1, err = AFP_OK;
818 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
819 uint16_t ashort, bshort, oshort;
822 uint16_t upriv_bit = 0;
824 int change_mdate = 0;
825 int change_parent_mdate = 0;
830 uint16_t bitmap = f_bitmap;
831 uint32_t cdate,bdate;
832 u_char finder_buf[32];
836 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
839 adp = of_ad(vol, path, &ad);
840 upath = path->u_name;
842 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
843 return AFPERR_ACCESS;
846 /* with unix priv maybe we have to change adouble file priv first */
848 while ( bitmap != 0 ) {
849 while (( bitmap & 1 ) == 0 ) {
856 memcpy(&ashort, buf, sizeof( ashort ));
857 buf += sizeof( ashort );
861 memcpy(&cdate, buf, sizeof(cdate));
862 buf += sizeof( cdate );
865 memcpy(&newdate, buf, sizeof( newdate ));
866 buf += sizeof( newdate );
870 memcpy(&bdate, buf, sizeof( bdate));
871 buf += sizeof( bdate );
875 memcpy(finder_buf, buf, 32 );
876 if (memcmp(buf, "slnkrhap", 8) == 0 && !S_ISLNK(path->st.st_mode)) {
880 char buf[PATH_MAX+1];
881 if ((fp = open(path->u_name, O_RDONLY)) >= 0) {
882 if ((len = read(fp, buf, PATH_MAX+1))) {
883 if (unlink(path->u_name) == 0) {
885 erc = symlink(buf, path->u_name);
894 goto setfilparam_done;
900 case FILPBIT_UNIXPR :
901 if (!vol_unix_priv(vol)) {
902 /* this volume doesn't use unix priv */
908 change_parent_mdate = 1;
910 memcpy( &aint, buf, sizeof( aint ));
911 f_uid = ntohl (aint);
912 buf += sizeof( aint );
913 memcpy( &aint, buf, sizeof( aint ));
914 f_gid = ntohl (aint);
915 buf += sizeof( aint );
916 setfilowner(vol, f_uid, f_gid, path);
918 memcpy( &upriv, buf, sizeof( upriv ));
919 buf += sizeof( upriv );
920 upriv = ntohl (upriv);
921 if ((upriv & S_IWUSR)) {
922 setfilunixmode(vol, path, upriv);
929 case FILPBIT_PDINFO :
930 if (afp_version < 30) { /* else it's UTF8 name */
933 /* Keep special case to support crlf translations */
934 if ((unsigned int) achar == 0x04) {
935 fdType = (u_char *)"TEXT";
938 xyy[0] = ( u_char ) 'p';
949 /* break while loop */
958 /* second try with adouble open
960 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
961 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
963 * For some things, we don't need an adouble header:
964 * - change of modification date
965 * - UNIX privs (Bug-ID #2863424)
967 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
968 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
969 return AFPERR_ACCESS;
971 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
973 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
974 ad_setname(adp, path->m_name);
979 while ( bitmap != 0 ) {
980 while (( bitmap & 1 ) == 0 ) {
987 ad_getattr(adp, &bshort);
989 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
990 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
994 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
995 change_parent_mdate = 1;
996 ad_setattr(adp, bshort);
999 ad_setdate(adp, AD_DATE_CREATE, cdate);
1001 case FILPBIT_MDATE :
1003 case FILPBIT_BDATE :
1004 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1006 case FILPBIT_FINFO :
1007 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1009 ((em = getextmap( path->m_name )) &&
1010 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1011 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1012 || ((em = getdefextmap()) &&
1013 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1014 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1016 memcpy(finder_buf, ufinderi, 8 );
1018 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1020 case FILPBIT_UNIXPR :
1022 setfilunixmode(vol, path, upriv);
1025 case FILPBIT_PDINFO :
1026 if (afp_version < 30) { /* else it's UTF8 name */
1027 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1028 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1033 err = AFPERR_BITMAP;
1034 goto setfilparam_done;
1041 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1042 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1046 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1047 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1053 ad_close(adp, ADFLAGS_HF);
1056 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1057 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1058 bitmap = 1<<FILPBIT_MDATE;
1059 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1063 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1069 * renamefile and copyfile take the old and new unix pathnames
1070 * and the new mac name.
1072 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1073 * passing -1 means this is not used, src path is a full path
1074 * src the source path
1075 * dst the dest filename in current dir
1076 * newname the dest mac name
1077 * adp adouble struct of src file, if open, or & zeroed one
1080 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1084 LOG(log_debug, logtype_afpd,
1085 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1087 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1090 return( AFPERR_NOOBJ );
1093 return( AFPERR_ACCESS );
1095 return AFPERR_VLOCK;
1096 case EXDEV : /* Cross device move -- try copy */
1097 /* NOTE: with open file it's an error because after the copy we will
1098 * get two files, it's fixable for our process (eg reopen the new file, get the
1099 * locks, and so on. But it doesn't solve the case with a second process
1101 if (adp->ad_open_forks) {
1102 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1103 return AFPERR_OLOCK; /* little lie */
1105 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1106 /* on error copyfile delete dest */
1109 return deletefile(vol, sdir_fd, src, 0);
1111 return( AFPERR_PARAM );
1115 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1119 /* try to undo the data fork rename,
1120 * we know we are on the same device
1123 unix_rename(-1, dst, sdir_fd, src );
1124 /* return the first error */
1127 return AFPERR_NOOBJ;
1130 return AFPERR_ACCESS ;
1132 return AFPERR_VLOCK;
1134 return AFPERR_PARAM ;
1139 /* don't care if we can't open the newly renamed ressource fork */
1140 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1141 ad_setname(adp, newname);
1143 ad_close( adp, ADFLAGS_HF );
1150 convert a Mac long name to an utf8 name,
1152 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1156 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1162 /* ---------------- */
1163 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1170 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1176 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1177 if (afp_version >= 30) {
1178 /* convert it to UTF8
1180 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1184 strncpy( newname, ibuf, plen );
1185 newname[ plen ] = '\0';
1187 if (strlen(newname) != plen) {
1188 /* there's \0 in newname, e.g. it's a pathname not
1196 memcpy(&hint, ibuf, sizeof(hint));
1197 ibuf += sizeof(hint);
1199 memcpy(&len16, ibuf, sizeof(len16));
1200 ibuf += sizeof(len16);
1201 plen = ntohs(len16);
1204 if (plen > AFPOBJ_TMPSIZ) {
1207 strncpy( newname, ibuf, plen );
1208 newname[ plen ] = '\0';
1209 if (strlen(newname) != plen) {
1218 /* -----------------------------------
1220 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1222 struct vol *s_vol, *d_vol;
1224 char *newname, *p, *upath;
1225 struct path *s_path;
1226 uint32_t sdid, ddid;
1227 int err, retvalue = AFP_OK;
1228 uint16_t svid, dvid;
1230 struct adouble ad, *adp;
1236 memcpy(&svid, ibuf, sizeof( svid ));
1237 ibuf += sizeof( svid );
1238 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1239 return( AFPERR_PARAM );
1242 memcpy(&sdid, ibuf, sizeof( sdid ));
1243 ibuf += sizeof( sdid );
1244 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1248 memcpy(&dvid, ibuf, sizeof( dvid ));
1249 ibuf += sizeof( dvid );
1250 memcpy(&ddid, ibuf, sizeof( ddid ));
1251 ibuf += sizeof( ddid );
1253 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1254 return get_afp_errno(AFPERR_PARAM);
1256 if ( path_isadir(s_path) ) {
1257 return( AFPERR_BADTYPE );
1260 /* don't allow copies when the file is open.
1261 * XXX: the spec only calls for read/deny write access.
1262 * however, copyfile doesn't have any of that info,
1263 * and locks need to stay coherent. as a result,
1264 * we just balk if the file is opened already. */
1266 adp = of_ad(s_vol, s_path, &ad);
1268 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1269 return AFPERR_DENYCONF;
1271 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1272 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1275 retvalue = AFPERR_DENYCONF;
1279 newname = obj->newtmp;
1280 strcpy( newname, s_path->m_name );
1282 p = ctoupath( s_vol, curdir, newname );
1284 retvalue = AFPERR_PARAM;
1288 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1289 retvalue = AFPERR_PARAM;
1293 if (d_vol->v_flags & AFPVOL_RO) {
1294 retvalue = AFPERR_VLOCK;
1298 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1299 retvalue = afp_errno;
1303 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1304 retvalue = get_afp_errno(AFPERR_NOOBJ);
1308 if ( *s_path->m_name != '\0' ) {
1309 retvalue =path_error(s_path, AFPERR_NOOBJ);
1313 /* one of the handful of places that knows about the path type */
1314 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1315 retvalue = AFPERR_PARAM;
1318 /* newname is always only a filename so curdir *is* its
1321 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1322 retvalue =AFPERR_PARAM;
1326 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1332 setvoltime(obj, d_vol );
1335 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1339 /* ----------------------- */
1340 static int copy_all(const int dfd, const void *buf,
1346 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1349 while (buflen > 0) {
1350 if ((cc = write(dfd, buf, buflen)) < 0) {
1362 LOG(log_debug9, logtype_afpd, "end copy_all:");
1368 /* --------------------------
1369 * copy only the fork data stream
1371 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1378 if (eid == ADEID_DFORK) {
1379 sfd = ad_data_fileno(ads);
1380 dfd = ad_data_fileno(add);
1383 sfd = ad_reso_fileno(ads);
1384 dfd = ad_reso_fileno(add);
1387 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1390 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1393 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1394 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1398 #define BUF 128*1024*1024
1400 if (fstat(sfd, &st) == 0) {
1403 if ( offset >= st.st_size) {
1406 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1407 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1410 case EINVAL: /* there's no guarantee that all fs support sendfile */
1419 lseek(sfd, offset, SEEK_SET);
1423 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1430 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1437 /* ----------------------------------
1438 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1439 * because we are doing it elsewhere.
1440 * currently if newname is NULL then adp is NULL.
1442 int copyfile(const struct vol *s_vol,
1443 const struct vol *d_vol,
1448 struct adouble *adp)
1450 struct adouble ads, add;
1457 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1458 sfd, src, dst, newname);
1461 ad_init(&ads, s_vol);
1465 adflags = ADFLAGS_DF | ADFLAGS_RF | ADFLAGS_NORF;
1467 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1472 if (!AD_RSRC_OPEN(adp))
1473 /* no resource fork, don't create one for dst file */
1474 adflags &= ~ADFLAGS_RF;
1476 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1478 if (stat_result < 0) {
1479 /* unlikely but if fstat fails, the default file mode will be 0666. */
1480 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1483 ad_init(&add, d_vol);
1484 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1486 ad_close( adp, adflags );
1487 if (EEXIST != ret_err) {
1488 deletefile(d_vol, -1, dst, 0);
1491 return AFPERR_EXIST;
1494 if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1495 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1498 if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1499 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1504 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1505 /* set the new name in the resource fork */
1506 ad_copy_header(&add, adp);
1507 ad_setname(&add, newname);
1510 ad_close( adp, adflags );
1512 if (ad_close( &add, adflags ) <0) {
1517 deletefile(d_vol, -1, dst, 0);
1519 else if (stat_result == 0) {
1520 /* set dest modification date to src date */
1523 ut.actime = ut.modtime = st.st_mtime;
1525 /* FIXME netatalk doesn't use resource fork file date
1526 * but maybe we should set its modtime too.
1531 switch ( ret_err ) {
1537 return AFPERR_DFULL;
1539 return AFPERR_NOOBJ;
1541 return AFPERR_ACCESS;
1543 return AFPERR_VLOCK;
1545 return AFPERR_PARAM;
1549 /* -----------------------------------
1550 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1551 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1553 when deletefile is called we don't have lock on it, file is closed (for us)
1554 untrue if called by renamefile
1556 ad_open always try to open file RDWR first and ad_lock takes care of
1557 WRITE lock on read only file.
1560 static int check_attrib(struct adouble *adp)
1562 uint16_t bshort = 0;
1564 ad_getattr(adp, &bshort);
1566 * Does kFPDeleteInhibitBit (bit 8) set?
1568 if ((bshort & htons(ATTRBIT_NODELETE))) {
1569 return AFPERR_OLOCK;
1571 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1577 * dirfd can be used for unlinkat semantics
1579 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1582 struct adouble *adp = NULL;
1583 int adflags, err = AFP_OK;
1586 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1590 /* was EACCESS error try to get only metadata */
1591 /* we never want to create a resource fork here, we are going to delete it
1592 * moreover sometimes deletefile is called with a no existent file and
1593 * ad_open would create a 0 byte resource fork
1595 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1596 if ((err = check_attrib(&ad))) {
1597 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1604 /* try to open both forks at once */
1605 adflags = ADFLAGS_DF;
1606 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1611 case EACCES: /* maybe it's a file with no write mode for us */
1612 break; /* was return AFPERR_ACCESS;*/
1625 if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1626 adflags |= ADFLAGS_RF;
1627 /* FIXME we have a pb here because we want to know if a file is open
1628 * there's a 'priority inversion' if you can't open the ressource fork RW
1629 * you can delete it if it's open because you can't get a write lock.
1631 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1634 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1636 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1642 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1643 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1645 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1647 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1648 cnid_delete(vol->v_cdb, id);
1654 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1657 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1662 /* ------------------------------------ */
1663 /* return a file id */
1664 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1673 struct path *s_path;
1679 memcpy(&vid, ibuf, sizeof(vid));
1680 ibuf += sizeof(vid);
1682 if (NULL == ( vol = getvolbyvid( vid )) ) {
1683 return( AFPERR_PARAM);
1686 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1690 if (vol->v_flags & AFPVOL_RO)
1691 return AFPERR_VLOCK;
1693 memcpy(&did, ibuf, sizeof( did ));
1694 ibuf += sizeof(did);
1696 if (NULL == ( dir = dirlookup( vol, did )) ) {
1697 return afp_errno; /* was AFPERR_PARAM */
1700 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1701 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1704 if ( path_isadir(s_path) ) {
1705 return( AFPERR_BADTYPE );
1708 upath = s_path->u_name;
1709 switch (s_path->st_errno) {
1711 break; /* success */
1714 return AFPERR_ACCESS;
1716 return AFPERR_NOOBJ;
1718 return AFPERR_PARAM;
1721 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1722 memcpy(rbuf, &id, sizeof(id));
1723 *rbuflen = sizeof(id);
1724 return AFPERR_EXISTID;
1727 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1728 memcpy(rbuf, &id, sizeof(id));
1729 *rbuflen = sizeof(id);
1736 /* ------------------------------- */
1742 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1745 struct reenum *param = data;
1746 struct vol *vol = param->vol;
1747 cnid_t did = param->did;
1750 if ( lstat(de->d_name, &path.st) < 0 )
1753 /* update or add to cnid */
1754 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1759 /* --------------------
1760 * Ok the db is out of synch with the dir.
1761 * but if it's a deleted file we don't want to do it again and again.
1764 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1770 if (vol->v_cdb == NULL) {
1774 /* FIXME use of_statdir ? */
1775 if (lstat(name, &st)) {
1779 if (dirreenumerate(dir, &st)) {
1780 /* we already did it once and the dir haven't been modified */
1781 return dir->d_offcnt;
1785 data.did = dir->d_did;
1786 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1787 setdiroffcnt(curdir, &st, ret);
1788 dir->d_flags |= DIRF_CNID;
1794 /* ------------------------------
1795 resolve a file id */
1796 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1805 uint16_t vid, bitmap;
1807 static char buffer[12 + MAXPATHLEN + 1];
1808 int len = 12 + MAXPATHLEN + 1;
1813 memcpy(&vid, ibuf, sizeof(vid));
1814 ibuf += sizeof(vid);
1816 if (NULL == ( vol = getvolbyvid( vid )) ) {
1817 return( AFPERR_PARAM);
1820 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1824 memcpy(&id, ibuf, sizeof( id ));
1829 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1833 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1834 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1837 if (NULL == ( dir = dirlookup( vol, id )) ) {
1838 return AFPERR_NOID; /* idem AFPERR_PARAM */
1840 if (movecwd(vol, dir) < 0) {
1844 return AFPERR_ACCESS;
1848 return AFPERR_PARAM;
1852 memset(&path, 0, sizeof(path));
1853 path.u_name = upath;
1854 if ( of_stat(&path) < 0 ) {
1856 /* with nfs and our working directory is deleted */
1857 if (errno == ESTALE) {
1861 if ( errno == ENOENT && !retry) {
1862 /* cnid db is out of sync, reenumerate the directory and update ids */
1863 reenumerate_id(vol, ".", dir);
1871 return AFPERR_ACCESS;
1875 return AFPERR_PARAM;
1879 /* directories are bad */
1880 if (S_ISDIR(path.st.st_mode)) {
1881 /* OS9 and OSX don't return the same error code */
1882 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1885 memcpy(&bitmap, ibuf, sizeof(bitmap));
1886 bitmap = ntohs( bitmap );
1887 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1891 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1892 rbuf + sizeof(bitmap), &buflen))) {
1895 *rbuflen = buflen + sizeof(bitmap);
1896 memcpy(rbuf, ibuf, sizeof(bitmap));
1901 /* ------------------------------ */
1902 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1912 static char buffer[12 + MAXPATHLEN + 1];
1913 int len = 12 + MAXPATHLEN + 1;
1918 memcpy(&vid, ibuf, sizeof(vid));
1919 ibuf += sizeof(vid);
1921 if (NULL == ( vol = getvolbyvid( vid )) ) {
1922 return( AFPERR_PARAM);
1925 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1929 if (vol->v_flags & AFPVOL_RO)
1930 return AFPERR_VLOCK;
1932 memcpy(&id, ibuf, sizeof( id ));
1936 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1940 if (NULL == ( dir = dirlookup( vol, id )) ) {
1941 if (afp_errno == AFPERR_NOOBJ) {
1945 return( AFPERR_PARAM );
1949 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1953 return AFPERR_ACCESS;
1958 /* still try to delete the id */
1962 return AFPERR_PARAM;
1965 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1966 return AFPERR_BADTYPE;
1969 if (cnid_delete(vol->v_cdb, fileid)) {
1972 return AFPERR_VLOCK;
1975 return AFPERR_ACCESS;
1977 return AFPERR_PARAM;
1984 /* ------------------------------ */
1985 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
1989 if (path->st_errno) {
1990 switch (path->st_errno) {
1992 afp_errno = AFPERR_NOID;
1996 afp_errno = AFPERR_ACCESS;
1999 afp_errno = AFPERR_PARAM;
2004 /* we use file_access both for legacy Mac perm and
2005 * for unix privilege, rename will take care of folder perms
2007 if (file_access(path, OPENACC_WR ) < 0) {
2008 afp_errno = AFPERR_ACCESS;
2012 if ((*of = of_findname(path))) {
2013 /* reuse struct adouble so it won't break locks */
2017 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
2019 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2021 * The user must have the Read & Write privilege for both files in order to use this command.
2023 ad_close(adp, ADFLAGS_HF);
2024 afp_errno = AFPERR_ACCESS;
2031 #define APPLETEMP ".AppleTempXXXXXX"
2033 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2035 struct stat srcst, destst;
2037 struct dir *dir, *sdir;
2038 char *spath, temp[17], *p;
2039 char *supath, *upath;
2044 struct adouble *adsp = NULL;
2045 struct adouble *addp = NULL;
2046 struct ofork *s_of = NULL;
2047 struct ofork *d_of = NULL;
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 ;
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;
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, -1, 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, -1, 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, -1, 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);
2192 cnid_delete(vol->v_cdb, did);
2194 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2195 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2197 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2198 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2203 err = AFPERR_ACCESS;
2208 goto err_temp_to_dest;
2211 /* here we need to reopen if crossdev */
2212 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2217 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2222 /* change perms, src gets dest perm and vice versa */
2227 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2228 err = AFP_OK; /* ignore error */
2229 goto err_temp_to_dest;
2233 * we need to exchange ACL entries as well
2235 /* exchange_acls(vol, p, upath); */
2240 path->m_name = NULL;
2241 path->u_name = upath;
2243 setfilunixmode(vol, path, destst.st_mode);
2244 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2251 setfilunixmode(vol, path, srcst.st_mode);
2252 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2254 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2255 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2260 goto err_exchangefile;
2262 /* all this stuff is so that we can unwind a failed operation
2265 /* rename dest to temp */
2266 renamefile(vol, -1, upath, temp, temp, adsp);
2267 of_rename(vol, s_of, curdir, upath, curdir, temp);
2270 /* rename source back to dest */
2271 renamefile(vol, -1, p, upath, path->m_name, addp);
2272 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2275 /* rename temp back to source */
2276 renamefile(vol, -1, temp, p, spath, adsp);
2277 of_rename(vol, s_of, curdir, temp, sdir, spath);
2280 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2281 ad_close(adsp, ADFLAGS_HF);
2283 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2284 ad_close(addp, ADFLAGS_HF);
2288 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2289 (void)dir_remove(vol, cached);
2290 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2291 (void)dir_remove(vol, cached);