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>
27 #include <atalk/netatalk_conf.h>
29 #include "directory.h"
38 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
39 * field bytes subfield bytes
42 * ioFlFndrInfo 16 -> type 4 type field
43 * creator 4 creator field
44 * flags 2 finder flags:
46 * location 4 location in window
47 * folder 2 window that contains file
49 * ioFlXFndrInfo 16 -> iconID 2 icon id
51 * script 1 script system
53 * commentID 2 comment id
54 * putawayID 4 home directory id
57 const u_char ufinderi[ADEDLEN_FINDERI] = {
58 0, 0, 0, 0, 0, 0, 0, 0,
59 1, 0, 0, 0, 0, 0, 0, 0,
60 0, 0, 0, 0, 0, 0, 0, 0,
61 0, 0, 0, 0, 0, 0, 0, 0
64 static const u_char old_ufinderi[] = {
65 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
68 /* ----------------------
70 static int default_type(void *finder)
72 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
77 /* FIXME path : unix or mac name ? (for now it's unix name ) */
78 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);
90 memcpy(data, ufinderi, ADEDLEN_FINDERI);
91 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
94 ashort = htons(FINDERINFO_INVISIBLE);
95 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
101 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
102 linkflag |= htons(FINDERINFO_ISALIAS);
103 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
104 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
105 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
111 /* ---------------------
113 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, uint32_t utf8)
118 aint = strlen( name );
122 if (utf8_encoding(vol->v_obj)) {
123 /* but name is an utf8 mac name */
126 /* global static variable... */
128 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
137 if (aint > MACFILELEN)
144 if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
145 aint = UTF8FILELEN_EARLY;
147 utf8 = vol->v_kTextEncoding;
148 memcpy(data, &utf8, sizeof(utf8));
149 data += sizeof(utf8);
152 memcpy(data, &temp, sizeof(temp));
153 data += sizeof(temp);
156 memcpy( data, src, aint );
166 * FIXME: PDINFO is UTF8 and doesn't need adp
168 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
169 (1 << FILPBIT_CDATE) |\
170 (1 << FILPBIT_MDATE) |\
171 (1 << FILPBIT_BDATE) |\
172 (1 << FILPBIT_FINFO) |\
173 (1 << FILPBIT_RFLEN) |\
174 (1 << FILPBIT_EXTRFLEN) |\
175 (1 << FILPBIT_PDINFO) |\
176 (1 << FILPBIT_FNUM) |\
177 (1 << FILPBIT_UNIXPR)))
180 * @brief Get CNID for did/upath args both from database and adouble file
182 * 1. Get the objects CNID as stored in its adouble file
183 * 2. Get the objects CNID from the database
184 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
185 * 4. In case 2 and 3 differ, store 3 in the adouble file
187 * @param vol (rw) volume
188 * @param adp (rw) adouble struct of object upath, might be NULL
189 * @param st (r) stat of upath, must NOT be NULL
190 * @param did (r) parent CNID of upath
191 * @param upath (r) name of object
192 * @param len (r) strlen of upath
194 uint32_t get_id(struct vol *vol,
196 const struct stat *st,
201 static int first = 1; /* mark if this func is called the first time */
203 uint32_t dbcnid = CNID_INVALID;
206 if (vol->v_cdb != NULL) {
207 /* prime aint with what we think is the cnid, set did to zero for
208 catching moved files */
209 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
211 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
212 /* Throw errors if cnid_add fails. */
213 if (dbcnid == CNID_INVALID) {
215 case CNID_ERR_CLOSE: /* the db is closed */
218 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
219 afp_errno = AFPERR_PARAM;
222 afp_errno = AFPERR_PARAM;
225 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
226 /* we have to do it here for "dbd" because it uses "lazy opening" */
227 /* In order to not end in a loop somehow with goto restart below */
229 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
230 cnid_close(vol->v_cdb);
231 free(vol->v_cnidscheme);
232 vol->v_cnidscheme = strdup("tdb");
234 int flags = CNID_FLAG_MEMORY;
235 if ((vol->v_flags & AFPVOL_NODEV)) {
236 flags |= CNID_FLAG_NODEV;
238 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
240 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
243 vol->v_flags |= AFPVOL_RO;
245 /* kill ourself with SIGUSR2 aka msg pending */
246 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
247 "Check server messages for details. Switching to read-only mode.");
248 kill(getpid(), SIGUSR2);
250 goto restart; /* not try again with the temp CNID db */
253 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
254 "Check server messages for details, can't recover from this state!");
258 afp_errno = AFPERR_MISC;
262 else if (adp && adcnid && (adcnid != dbcnid)) { /* 4 */
263 /* Update the ressource fork. For a folder adp is always null */
264 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
265 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
266 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
267 if (ad_flush(adp) != 0)
268 LOG(log_error, logtype_afpd, "get_id(\"%s\"): can't flush", fullpathname(upath));
278 /* -------------------------- */
279 int getmetadata(const AFPObj *obj,
282 struct path *path, struct dir *dir,
283 char *buf, size_t *buflen, struct adouble *adp)
285 char *data, *l_nameoff = NULL, *upath;
286 char *utf_nameoff = NULL;
291 u_char achar, fdType[4];
296 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
298 upath = path->u_name;
302 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
303 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding(obj)) /* FIXME should be m_name utf8 filename */
304 || (bitmap & (1 << FILPBIT_FNUM))) {
307 struct dir *cachedfile;
308 int len = strlen(upath);
309 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
310 id = cachedfile->d_did;
312 id = get_id(vol, adp, st, dir->d_did, upath, len);
314 /* Add it to the cache */
315 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
316 ntohl(dir->d_did), upath, ntohl(id));
318 /* Get macname from unixname first */
319 if (path->m_name == NULL) {
320 if ((path->m_name = utompath(vol, upath, id, utf8_encoding(obj))) == NULL) {
321 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
327 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
328 || (bconchar(fullpath, '/') != BSTR_OK)
329 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
330 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
334 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
335 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
339 if ((dircache_add(vol, cachedfile)) != 0) {
340 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
348 if (id == CNID_INVALID)
352 path->m_name = utompath(vol, upath, id, utf8_encoding(vol->v_obj));
355 while ( bitmap != 0 ) {
356 while (( bitmap & 1 ) == 0 ) {
364 ad_getattr(adp, &ashort);
365 } else if (vol_inv_dots(vol) && *upath == '.') {
366 ashort = htons(ATTRBIT_INVISIBLE);
370 /* FIXME do we want a visual clue if the file is read only
373 accessmode(vol, ".", &ma, dir , NULL);
374 if ((ma.ma_user & AR_UWRITE)) {
375 accessmode(vol, upath, &ma, dir , st);
376 if (!(ma.ma_user & AR_UWRITE)) {
377 ashort |= htons(ATTRBIT_NOWRITE);
381 memcpy(data, &ashort, sizeof( ashort ));
382 data += sizeof( ashort );
383 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
384 path->u_name, ntohs(ashort));
388 memcpy(data, &dir->d_did, sizeof( uint32_t ));
389 data += sizeof( uint32_t );
390 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
391 path->u_name, ntohl(dir->d_did));
395 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
396 aint = AD_DATE_FROM_UNIX(st->st_mtime);
397 memcpy(data, &aint, sizeof( aint ));
398 data += sizeof( aint );
402 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
403 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
404 aint = AD_DATE_FROM_UNIX(st->st_mtime);
407 aint = AD_DATE_FROM_UNIX(st->st_mtime);
409 memcpy(data, &aint, sizeof( int ));
410 data += sizeof( int );
414 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
415 aint = AD_DATE_START;
416 memcpy(data, &aint, sizeof( int ));
417 data += sizeof( int );
421 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
422 data += ADEDLEN_FINDERI;
427 data += sizeof( uint16_t );
431 memset(data, 0, sizeof(uint16_t));
432 data += sizeof( uint16_t );
436 memcpy(data, &id, sizeof( id ));
437 data += sizeof( id );
438 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
439 path->u_name, ntohl(id));
443 if (st->st_size > 0xffffffff)
446 aint = htonl( st->st_size );
447 memcpy(data, &aint, sizeof( aint ));
448 data += sizeof( aint );
453 if (adp->ad_rlen > 0xffffffff)
456 aint = htonl( adp->ad_rlen);
460 memcpy(data, &aint, sizeof( aint ));
461 data += sizeof( aint );
464 /* Current client needs ProDOS info block for this file.
465 Use simple heuristic and let the Mac "type" string tell
466 us what the PD file code should be. Everything gets a
467 subtype of 0x0000 unless the original value was hashed
468 to "pXYZ" when we created it. See IA, Ver 2.
469 <shirsch@adelphia.net> */
470 case FILPBIT_PDINFO :
471 if (obj->afp_version >= 30) { /* UTF8 name */
472 utf8 = kTextEncodingUTF8;
474 data += sizeof( uint16_t );
476 memcpy(data, &aint, sizeof( aint ));
477 data += sizeof( aint );
481 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
483 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
487 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
491 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
495 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
499 else if ( fdType[0] == 'p' ) {
501 ashort = (fdType[2] * 256) + fdType[3];
515 memcpy(data, &ashort, sizeof( ashort ));
516 data += sizeof( ashort );
517 memset(data, 0, sizeof( ashort ));
518 data += sizeof( ashort );
521 case FILPBIT_EXTDFLEN:
522 aint = htonl(st->st_size >> 32);
523 memcpy(data, &aint, sizeof( aint ));
524 data += sizeof( aint );
525 aint = htonl(st->st_size);
526 memcpy(data, &aint, sizeof( aint ));
527 data += sizeof( aint );
529 case FILPBIT_EXTRFLEN:
532 aint = htonl(adp->ad_rlen >> 32);
533 memcpy(data, &aint, sizeof( aint ));
534 data += sizeof( aint );
536 aint = htonl(adp->ad_rlen);
537 memcpy(data, &aint, sizeof( aint ));
538 data += sizeof( aint );
540 case FILPBIT_UNIXPR :
541 /* accessmode may change st_mode with ACLs */
542 accessmode(obj, vol, upath, &ma, dir , st);
544 aint = htonl(st->st_uid);
545 memcpy( data, &aint, sizeof( aint ));
546 data += sizeof( aint );
547 aint = htonl(st->st_gid);
548 memcpy( data, &aint, sizeof( aint ));
549 data += sizeof( aint );
552 type == slnk indicates an OSX style symlink,
553 we have to add S_IFLNK to the mode, otherwise
554 10.3 clients freak out. */
558 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
559 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
565 memcpy( data, &aint, sizeof( aint ));
566 data += sizeof( aint );
568 *data++ = ma.ma_user;
569 *data++ = ma.ma_world;
570 *data++ = ma.ma_group;
571 *data++ = ma.ma_owner;
575 return( AFPERR_BITMAP );
581 ashort = htons( data - buf );
582 memcpy(l_nameoff, &ashort, sizeof( ashort ));
583 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
586 ashort = htons( data - buf );
587 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
588 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
590 *buflen = data - buf;
594 /* ----------------------- */
595 int getfilparams(const AFPObj *obj,
598 struct path *path, struct dir *dir,
599 char *buf, size_t *buflen )
601 struct adouble ad, *adp;
606 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
608 opened = PARAM_NEED_ADP(bitmap);
613 flags = (bitmap & (1 << FILPBIT_ATTR)) ? ADFLAGS_CHECK_OF : 0;
615 adp = of_ad(vol, path, &ad);
616 upath = path->u_name;
618 if ( ad_metadata( upath, flags, adp) < 0 ) {
621 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
622 upath, strerror(errno));
623 return AFPERR_ACCESS;
625 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
634 rc = getmetadata(obj, vol, bitmap, path, dir, buf, buflen, adp);
635 ad_close(adp, ADFLAGS_HF | flags);
640 /* ----------------------------- */
641 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
646 struct ofork *of = NULL;
648 int creatf, did, openf, retvalue = AFP_OK;
654 creatf = (unsigned char) *ibuf++;
656 memcpy(&vid, ibuf, sizeof( vid ));
657 ibuf += sizeof( vid );
659 if (NULL == ( vol = getvolbyvid( vid )) )
660 return( AFPERR_PARAM );
662 if (vol->v_flags & AFPVOL_RO)
665 memcpy(&did, ibuf, sizeof( did));
666 ibuf += sizeof( did );
668 if (NULL == ( dir = dirlookup( vol, did )) )
671 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
672 return get_afp_errno(AFPERR_PARAM);
673 if ( *s_path->m_name == '\0' )
674 return( AFPERR_BADTYPE );
676 upath = s_path->u_name;
679 /* if upath is deleted we already in trouble anyway */
680 if ((of = of_findname(s_path))) {
688 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
690 /* on a soft create, if the file is open then ad_open won't fail
691 because open syscall is not called */
692 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
694 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
698 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
699 return ( AFPERR_NOOBJ );
701 return( AFPERR_EXIST );
703 return( AFPERR_ACCESS );
706 return( AFPERR_DFULL );
708 return( AFPERR_PARAM );
711 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
712 /* FIXME with hard create on an existing file, we already
713 * corrupted the data file.
715 netatalk_unlink( upath );
716 ad_close( &ad, ADFLAGS_DF );
717 return AFPERR_ACCESS;
720 path = s_path->m_name;
721 ad_setname(&ad, path);
724 if (lstat(upath, &st) != 0) {
725 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
726 upath, strerror(errno));
727 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
732 if ((id = get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath))) == CNID_INVALID) {
733 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
734 goto createfile_iderr;
736 (void)ad_setid(&ad, st.st_dev, st.st_ino, id, dir->d_did, vol->v_stamp);
740 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
741 fce_register_new_file(s_path);
746 setvoltime(obj, vol );
751 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
757 uint16_t vid, bitmap;
762 memcpy(&vid, ibuf, sizeof( vid ));
763 ibuf += sizeof( vid );
764 if (NULL == ( vol = getvolbyvid( vid )) ) {
765 return( AFPERR_PARAM );
768 if (vol->v_flags & AFPVOL_RO)
771 memcpy(&did, ibuf, sizeof( did ));
772 ibuf += sizeof( did );
773 if (NULL == ( dir = dirlookup( vol, did )) ) {
774 return afp_errno; /* was AFPERR_NOOBJ */
777 memcpy(&bitmap, ibuf, sizeof( bitmap ));
778 bitmap = ntohs( bitmap );
779 ibuf += sizeof( bitmap );
781 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
782 return get_afp_errno(AFPERR_PARAM);
785 if (path_isadir(s_path)) {
786 return( AFPERR_BADTYPE ); /* it's a directory */
789 if ( s_path->st_errno != 0 ) {
790 return( AFPERR_NOOBJ );
793 if ((u_long)ibuf & 1 ) {
797 if (AFP_OK == ( rc = setfilparams(obj, vol, s_path, bitmap, ibuf )) ) {
798 setvoltime(obj, vol );
805 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
808 extern struct path Cur_Path;
810 int setfilparams(const AFPObj *obj, struct vol *vol,
811 struct path *path, uint16_t f_bitmap, char *buf )
813 struct adouble ad, *adp;
815 int bit, isad = 1, err = AFP_OK;
817 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
818 uint16_t ashort, bshort, oshort;
821 uint16_t upriv_bit = 0;
823 int change_mdate = 0;
824 int change_parent_mdate = 0;
829 uint16_t bitmap = f_bitmap;
830 uint32_t cdate,bdate;
831 u_char finder_buf[32];
835 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
838 adp = of_ad(vol, path, &ad);
839 upath = path->u_name;
841 if (!vol_unix_priv(vol) && check_access(obj, vol, upath, OPENACC_WR ) < 0) {
842 return AFPERR_ACCESS;
845 /* with unix priv maybe we have to change adouble file priv first */
847 while ( bitmap != 0 ) {
848 while (( bitmap & 1 ) == 0 ) {
855 memcpy(&ashort, buf, sizeof( ashort ));
856 buf += sizeof( ashort );
860 memcpy(&cdate, buf, sizeof(cdate));
861 buf += sizeof( cdate );
864 memcpy(&newdate, buf, sizeof( newdate ));
865 buf += sizeof( newdate );
869 memcpy(&bdate, buf, sizeof( bdate));
870 buf += sizeof( bdate );
874 memcpy(finder_buf, buf, 32 );
875 if (memcmp(buf, "slnkrhap", 8) == 0 && !S_ISLNK(path->st.st_mode)) {
879 char buf[PATH_MAX+1];
880 if ((fp = open(path->u_name, O_RDONLY)) >= 0) {
881 if ((len = read(fp, buf, PATH_MAX+1))) {
882 if (unlink(path->u_name) == 0) {
884 erc = symlink(buf, path->u_name);
893 goto setfilparam_done;
899 case FILPBIT_UNIXPR :
900 if (!vol_unix_priv(vol)) {
901 /* this volume doesn't use unix priv */
907 change_parent_mdate = 1;
909 memcpy( &aint, buf, sizeof( aint ));
910 f_uid = ntohl (aint);
911 buf += sizeof( aint );
912 memcpy( &aint, buf, sizeof( aint ));
913 f_gid = ntohl (aint);
914 buf += sizeof( aint );
915 setfilowner(vol, f_uid, f_gid, path);
917 memcpy( &upriv, buf, sizeof( upriv ));
918 buf += sizeof( upriv );
919 upriv = ntohl (upriv);
920 if ((upriv & S_IWUSR)) {
921 setfilunixmode(vol, path, upriv);
928 case FILPBIT_PDINFO :
929 if (obj->afp_version < 30) { /* else it's UTF8 name */
932 /* Keep special case to support crlf translations */
933 if ((unsigned int) achar == 0x04) {
934 fdType = (u_char *)"TEXT";
937 xyy[0] = ( u_char ) 'p';
948 /* break while loop */
957 /* second try with adouble open
959 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
960 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
962 * For some things, we don't need an adouble header:
963 * - change of modification date
964 * - UNIX privs (Bug-ID #2863424)
966 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
967 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
968 return AFPERR_ACCESS;
970 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
972 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
973 ad_setname(adp, path->m_name);
978 while ( bitmap != 0 ) {
979 while (( bitmap & 1 ) == 0 ) {
986 ad_getattr(adp, &bshort);
988 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
989 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
993 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
994 change_parent_mdate = 1;
995 ad_setattr(adp, bshort);
998 ad_setdate(adp, AD_DATE_CREATE, cdate);
1000 case FILPBIT_MDATE :
1002 case FILPBIT_BDATE :
1003 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1005 case FILPBIT_FINFO :
1006 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1008 case FILPBIT_UNIXPR :
1010 setfilunixmode(vol, path, upriv);
1013 case FILPBIT_PDINFO :
1014 if (obj->afp_version < 30) { /* else it's UTF8 name */
1015 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1016 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1021 err = AFPERR_BITMAP;
1022 goto setfilparam_done;
1029 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1030 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1034 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1035 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1041 ad_close(adp, ADFLAGS_HF);
1044 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1045 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1046 bitmap = 1<<FILPBIT_MDATE;
1047 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1051 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1057 * renamefile and copyfile take the old and new unix pathnames
1058 * and the new mac name.
1060 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1061 * passing -1 means this is not used, src path is a full path
1062 * src the source path
1063 * dst the dest filename in current dir
1064 * newname the dest mac name
1065 * adp adouble struct of src file, if open, or & zeroed one
1068 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1072 LOG(log_debug, logtype_afpd,
1073 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1075 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1078 return( AFPERR_NOOBJ );
1081 return( AFPERR_ACCESS );
1083 return AFPERR_VLOCK;
1084 case EXDEV : /* Cross device move -- try copy */
1085 /* NOTE: with open file it's an error because after the copy we will
1086 * get two files, it's fixable for our process (eg reopen the new file, get the
1087 * locks, and so on. But it doesn't solve the case with a second process
1089 if (adp->ad_open_forks) {
1090 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1091 return AFPERR_OLOCK; /* little lie */
1093 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1094 /* on error copyfile delete dest */
1097 return deletefile(vol, sdir_fd, src, 0);
1099 return( AFPERR_PARAM );
1103 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1107 /* try to undo the data fork rename,
1108 * we know we are on the same device
1111 unix_rename(-1, dst, sdir_fd, src );
1112 /* return the first error */
1115 return AFPERR_NOOBJ;
1118 return AFPERR_ACCESS ;
1120 return AFPERR_VLOCK;
1122 return AFPERR_PARAM ;
1127 /* don't care if we can't open the newly renamed ressource fork */
1128 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1129 ad_setname(adp, newname);
1131 ad_close( adp, ADFLAGS_HF );
1138 convert a Mac long name to an utf8 name,
1140 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1144 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1150 /* ---------------- */
1151 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1158 if ( type != 2 && !(vol->v_obj->afp_version >= 30 && type == 3) ) {
1164 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1165 if (vol->v_obj->afp_version >= 30) {
1166 /* convert it to UTF8
1168 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1172 strncpy( newname, ibuf, plen );
1173 newname[ plen ] = '\0';
1175 if (strlen(newname) != plen) {
1176 /* there's \0 in newname, e.g. it's a pathname not
1184 memcpy(&hint, ibuf, sizeof(hint));
1185 ibuf += sizeof(hint);
1187 memcpy(&len16, ibuf, sizeof(len16));
1188 ibuf += sizeof(len16);
1189 plen = ntohs(len16);
1192 if (plen > AFPOBJ_TMPSIZ) {
1195 strncpy( newname, ibuf, plen );
1196 newname[ plen ] = '\0';
1197 if (strlen(newname) != plen) {
1206 /* -----------------------------------
1208 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1210 struct vol *s_vol, *d_vol;
1212 char *newname, *p, *upath;
1213 struct path *s_path;
1214 uint32_t sdid, ddid;
1215 int err, retvalue = AFP_OK;
1216 uint16_t svid, dvid;
1218 struct adouble ad, *adp;
1224 memcpy(&svid, ibuf, sizeof( svid ));
1225 ibuf += sizeof( svid );
1226 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1227 return( AFPERR_PARAM );
1230 memcpy(&sdid, ibuf, sizeof( sdid ));
1231 ibuf += sizeof( sdid );
1232 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1236 memcpy(&dvid, ibuf, sizeof( dvid ));
1237 ibuf += sizeof( dvid );
1238 memcpy(&ddid, ibuf, sizeof( ddid ));
1239 ibuf += sizeof( ddid );
1241 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1242 return get_afp_errno(AFPERR_PARAM);
1244 if ( path_isadir(s_path) ) {
1245 return( AFPERR_BADTYPE );
1248 /* don't allow copies when the file is open.
1249 * XXX: the spec only calls for read/deny write access.
1250 * however, copyfile doesn't have any of that info,
1251 * and locks need to stay coherent. as a result,
1252 * we just balk if the file is opened already. */
1254 adp = of_ad(s_vol, s_path, &ad);
1256 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1257 return AFPERR_DENYCONF;
1259 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1260 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1263 retvalue = AFPERR_DENYCONF;
1267 newname = obj->newtmp;
1268 strcpy( newname, s_path->m_name );
1270 p = ctoupath( s_vol, curdir, newname );
1272 retvalue = AFPERR_PARAM;
1276 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1277 retvalue = AFPERR_PARAM;
1281 if (d_vol->v_flags & AFPVOL_RO) {
1282 retvalue = AFPERR_VLOCK;
1286 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1287 retvalue = afp_errno;
1291 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1292 retvalue = get_afp_errno(AFPERR_NOOBJ);
1296 if ( *s_path->m_name != '\0' ) {
1297 retvalue =path_error(s_path, AFPERR_NOOBJ);
1301 /* one of the handful of places that knows about the path type */
1302 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1303 retvalue = AFPERR_PARAM;
1306 /* newname is always only a filename so curdir *is* its
1309 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding(d_vol->v_obj)))) {
1310 retvalue =AFPERR_PARAM;
1314 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1320 setvoltime(obj, d_vol );
1323 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1327 /* ----------------------------------
1328 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1329 * because we are doing it elsewhere.
1330 * currently if newname is NULL then adp is NULL.
1332 int copyfile(const struct vol *s_vol,
1333 const struct vol *d_vol,
1338 struct adouble *adp)
1340 struct adouble ads, add;
1347 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1348 sfd, src, dst, newname);
1351 ad_init(&ads, s_vol);
1355 adflags = ADFLAGS_DF | ADFLAGS_RF | ADFLAGS_NORF;
1357 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1362 if (!AD_RSRC_OPEN(adp))
1363 /* no resource fork, don't create one for dst file */
1364 adflags &= ~ADFLAGS_RF;
1366 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1368 if (stat_result < 0) {
1369 /* unlikely but if fstat fails, the default file mode will be 0666. */
1370 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1373 ad_init(&add, d_vol);
1374 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1376 ad_close( adp, adflags );
1377 if (EEXIST != ret_err) {
1378 deletefile(d_vol, -1, dst, 0);
1381 return AFPERR_EXIST;
1384 if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1385 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1388 if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1389 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1394 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1395 /* set the new name in the resource fork */
1396 ad_copy_header(&add, adp);
1397 ad_setname(&add, newname);
1400 ad_close( adp, adflags );
1402 if (ad_close( &add, adflags ) <0) {
1407 deletefile(d_vol, -1, dst, 0);
1409 else if (stat_result == 0) {
1410 /* set dest modification date to src date */
1413 ut.actime = ut.modtime = st.st_mtime;
1415 /* FIXME netatalk doesn't use resource fork file date
1416 * but maybe we should set its modtime too.
1421 switch ( ret_err ) {
1427 return AFPERR_DFULL;
1429 return AFPERR_NOOBJ;
1431 return AFPERR_ACCESS;
1433 return AFPERR_VLOCK;
1435 return AFPERR_PARAM;
1439 /* -----------------------------------
1440 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1441 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1443 when deletefile is called we don't have lock on it, file is closed (for us)
1444 untrue if called by renamefile
1446 ad_open always try to open file RDWR first and ad_lock takes care of
1447 WRITE lock on read only file.
1450 static int check_attrib(struct adouble *adp)
1452 uint16_t bshort = 0;
1454 ad_getattr(adp, &bshort);
1456 * Does kFPDeleteInhibitBit (bit 8) set?
1458 if ((bshort & htons(ATTRBIT_NODELETE))) {
1459 return AFPERR_OLOCK;
1461 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1467 * dirfd can be used for unlinkat semantics
1469 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1472 struct adouble *adp = NULL;
1473 int adflags, err = AFP_OK;
1476 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1480 /* was EACCESS error try to get only metadata */
1481 /* we never want to create a resource fork here, we are going to delete it
1482 * moreover sometimes deletefile is called with a no existent file and
1483 * ad_open would create a 0 byte resource fork
1485 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1486 if ((err = check_attrib(&ad))) {
1487 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1494 /* try to open both forks at once */
1495 adflags = ADFLAGS_DF;
1496 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1501 case EACCES: /* maybe it's a file with no write mode for us */
1502 break; /* was return AFPERR_ACCESS;*/
1515 if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1516 adflags |= ADFLAGS_RF;
1517 /* FIXME we have a pb here because we want to know if a file is open
1518 * there's a 'priority inversion' if you can't open the ressource fork RW
1519 * you can delete it if it's open because you can't get a write lock.
1521 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1524 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1526 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1532 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1533 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1535 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1537 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1538 cnid_delete(vol->v_cdb, id);
1544 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1547 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1552 /* ------------------------------------ */
1553 /* return a file id */
1554 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1563 struct path *s_path;
1569 memcpy(&vid, ibuf, sizeof(vid));
1570 ibuf += sizeof(vid);
1572 if (NULL == ( vol = getvolbyvid( vid )) ) {
1573 return( AFPERR_PARAM);
1576 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1580 if (vol->v_flags & AFPVOL_RO)
1581 return AFPERR_VLOCK;
1583 memcpy(&did, ibuf, sizeof( did ));
1584 ibuf += sizeof(did);
1586 if (NULL == ( dir = dirlookup( vol, did )) ) {
1587 return afp_errno; /* was AFPERR_PARAM */
1590 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1591 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1594 if ( path_isadir(s_path) ) {
1595 return( AFPERR_BADTYPE );
1598 upath = s_path->u_name;
1599 switch (s_path->st_errno) {
1601 break; /* success */
1604 return AFPERR_ACCESS;
1606 return AFPERR_NOOBJ;
1608 return AFPERR_PARAM;
1611 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1612 memcpy(rbuf, &id, sizeof(id));
1613 *rbuflen = sizeof(id);
1614 return AFPERR_EXISTID;
1617 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1618 memcpy(rbuf, &id, sizeof(id));
1619 *rbuflen = sizeof(id);
1626 /* ------------------------------- */
1632 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1635 struct reenum *param = data;
1636 struct vol *vol = param->vol;
1637 cnid_t did = param->did;
1640 if ( lstat(de->d_name, &path.st) < 0 )
1643 /* update or add to cnid */
1644 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1649 /* --------------------
1650 * Ok the db is out of synch with the dir.
1651 * but if it's a deleted file we don't want to do it again and again.
1654 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1660 if (vol->v_cdb == NULL) {
1664 /* FIXME use of_statdir ? */
1665 if (lstat(name, &st)) {
1669 if (dirreenumerate(dir, &st)) {
1670 /* we already did it once and the dir haven't been modified */
1671 return dir->d_offcnt;
1675 data.did = dir->d_did;
1676 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1677 setdiroffcnt(curdir, &st, ret);
1678 dir->d_flags |= DIRF_CNID;
1684 /* ------------------------------
1685 resolve a file id */
1686 int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1695 uint16_t vid, bitmap;
1697 static char buffer[12 + MAXPATHLEN + 1];
1698 int len = 12 + MAXPATHLEN + 1;
1703 memcpy(&vid, ibuf, sizeof(vid));
1704 ibuf += sizeof(vid);
1706 if (NULL == ( vol = getvolbyvid( vid )) ) {
1707 return( AFPERR_PARAM);
1710 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1714 memcpy(&id, ibuf, sizeof( id ));
1719 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1723 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1724 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1727 if (NULL == ( dir = dirlookup( vol, id )) ) {
1728 return AFPERR_NOID; /* idem AFPERR_PARAM */
1730 if (movecwd(vol, dir) < 0) {
1734 return AFPERR_ACCESS;
1738 return AFPERR_PARAM;
1742 memset(&path, 0, sizeof(path));
1743 path.u_name = upath;
1744 if ( of_stat(&path) < 0 ) {
1746 /* with nfs and our working directory is deleted */
1747 if (errno == ESTALE) {
1751 if ( errno == ENOENT && !retry) {
1752 /* cnid db is out of sync, reenumerate the directory and update ids */
1753 reenumerate_id(vol, ".", dir);
1761 return AFPERR_ACCESS;
1765 return AFPERR_PARAM;
1769 /* directories are bad */
1770 if (S_ISDIR(path.st.st_mode)) {
1771 /* OS9 and OSX don't return the same error code */
1772 return (obj->afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1775 memcpy(&bitmap, ibuf, sizeof(bitmap));
1776 bitmap = ntohs( bitmap );
1777 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding(obj)))) {
1781 if (AFP_OK != (err = getfilparams(obj, vol, bitmap, &path , curdir,
1782 rbuf + sizeof(bitmap), &buflen))) {
1785 *rbuflen = buflen + sizeof(bitmap);
1786 memcpy(rbuf, ibuf, sizeof(bitmap));
1791 /* ------------------------------ */
1792 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1802 static char buffer[12 + MAXPATHLEN + 1];
1803 int len = 12 + MAXPATHLEN + 1;
1808 memcpy(&vid, ibuf, sizeof(vid));
1809 ibuf += sizeof(vid);
1811 if (NULL == ( vol = getvolbyvid( vid )) ) {
1812 return( AFPERR_PARAM);
1815 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1819 if (vol->v_flags & AFPVOL_RO)
1820 return AFPERR_VLOCK;
1822 memcpy(&id, ibuf, sizeof( id ));
1826 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1830 if (NULL == ( dir = dirlookup( vol, id )) ) {
1831 if (afp_errno == AFPERR_NOOBJ) {
1835 return( AFPERR_PARAM );
1839 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1843 return AFPERR_ACCESS;
1848 /* still try to delete the id */
1852 return AFPERR_PARAM;
1855 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1856 return AFPERR_BADTYPE;
1859 if (cnid_delete(vol->v_cdb, fileid)) {
1862 return AFPERR_VLOCK;
1865 return AFPERR_ACCESS;
1867 return AFPERR_PARAM;
1874 /* ------------------------------ */
1875 static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
1879 if (path->st_errno) {
1880 switch (path->st_errno) {
1882 afp_errno = AFPERR_NOID;
1886 afp_errno = AFPERR_ACCESS;
1889 afp_errno = AFPERR_PARAM;
1894 /* we use file_access both for legacy Mac perm and
1895 * for unix privilege, rename will take care of folder perms
1897 if (file_access(obj, vol, path, OPENACC_WR ) < 0) {
1898 afp_errno = AFPERR_ACCESS;
1902 if ((*of = of_findname(path))) {
1903 /* reuse struct adouble so it won't break locks */
1907 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
1909 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1911 * The user must have the Read & Write privilege for both files in order to use this command.
1913 ad_close(adp, ADFLAGS_HF);
1914 afp_errno = AFPERR_ACCESS;
1921 #define APPLETEMP ".AppleTempXXXXXX"
1923 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1925 struct stat srcst, destst;
1927 struct dir *dir, *sdir;
1928 char *spath, temp[17], *p;
1929 char *supath, *upath;
1934 struct adouble *adsp = NULL;
1935 struct adouble *addp = NULL;
1936 struct ofork *s_of = NULL;
1937 struct ofork *d_of = NULL;
1950 memcpy(&vid, ibuf, sizeof(vid));
1951 ibuf += sizeof(vid);
1953 if (NULL == ( vol = getvolbyvid( vid )) ) {
1954 return( AFPERR_PARAM);
1957 if ((vol->v_flags & AFPVOL_RO))
1958 return AFPERR_VLOCK;
1960 /* source and destination dids */
1961 memcpy(&sid, ibuf, sizeof(sid));
1962 ibuf += sizeof(sid);
1963 memcpy(&did, ibuf, sizeof(did));
1964 ibuf += sizeof(did);
1967 if (NULL == (dir = dirlookup( vol, sid )) ) {
1968 return afp_errno; /* was AFPERR_PARAM */
1971 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1972 return get_afp_errno(AFPERR_NOOBJ);
1975 if ( path_isadir(path) ) {
1976 return AFPERR_BADTYPE; /* it's a dir */
1979 /* save some stuff */
1982 spath = obj->oldtmp;
1983 supath = obj->newtmp;
1984 strcpy(spath, path->m_name);
1985 strcpy(supath, path->u_name); /* this is for the cnid changing */
1986 p = absupath( vol, sdir, supath);
1988 /* pathname too long */
1989 return AFPERR_PARAM ;
1993 if (!(adsp = find_adouble(obj, vol, path, &s_of, &ads))) {
1997 /* ***** from here we may have resource fork open **** */
1999 /* look for the source cnid. if it doesn't exist, don't worry about
2001 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2003 if (NULL == ( dir = dirlookup( vol, did )) ) {
2004 err = afp_errno; /* was AFPERR_PARAM */
2005 goto err_exchangefile;
2008 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2009 err = get_afp_errno(AFPERR_NOOBJ);
2010 goto err_exchangefile;
2013 if ( path_isadir(path) ) {
2014 err = AFPERR_BADTYPE;
2015 goto err_exchangefile;
2018 /* FPExchangeFiles is the only call that can return the SameObj
2020 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2021 err = AFPERR_SAMEOBJ;
2022 goto err_exchangefile;
2026 if (!(addp = find_adouble(obj, vol, path, &d_of, &add))) {
2028 goto err_exchangefile;
2032 /* they are not on the same device and at least one is open
2033 * FIXME broken for for crossdev and adouble v2
2036 crossdev = (srcst.st_dev != destst.st_dev);
2037 if (/* (d_of || s_of) && */ crossdev) {
2039 goto err_exchangefile;
2042 /* look for destination id. */
2043 upath = path->u_name;
2044 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2046 /* construct a temp name.
2047 * NOTE: the temp file will be in the dest file's directory. it
2048 * will also be inaccessible from AFP. */
2049 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2050 if (!mktemp(temp)) {
2052 goto err_exchangefile;
2056 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2057 ad_close(adsp, ADFLAGS_HF);
2058 ad_close(addp, ADFLAGS_HF);
2061 /* now, quickly rename the file. we error if we can't. */
2062 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2063 goto err_exchangefile;
2064 of_rename(vol, s_of, sdir, spath, curdir, temp);
2066 /* rename destination to source */
2067 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2068 goto err_src_to_tmp;
2069 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2071 /* rename temp to destination */
2072 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2073 goto err_dest_to_src;
2074 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2076 /* id's need switching. src -> dest and dest -> src.
2077 * we need to re-stat() if it was a cross device copy.
2080 cnid_delete(vol->v_cdb, sid);
2082 cnid_delete(vol->v_cdb, did);
2084 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2085 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2087 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2088 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2093 err = AFPERR_ACCESS;
2098 goto err_temp_to_dest;
2101 /* here we need to reopen if crossdev */
2102 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2107 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2112 /* change perms, src gets dest perm and vice versa */
2117 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2118 err = AFP_OK; /* ignore error */
2119 goto err_temp_to_dest;
2123 * we need to exchange ACL entries as well
2125 /* exchange_acls(vol, p, upath); */
2130 path->m_name = NULL;
2131 path->u_name = upath;
2133 setfilunixmode(vol, path, destst.st_mode);
2134 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2141 setfilunixmode(vol, path, srcst.st_mode);
2142 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2144 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2145 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2150 goto err_exchangefile;
2152 /* all this stuff is so that we can unwind a failed operation
2155 /* rename dest to temp */
2156 renamefile(vol, -1, upath, temp, temp, adsp);
2157 of_rename(vol, s_of, curdir, upath, curdir, temp);
2160 /* rename source back to dest */
2161 renamefile(vol, -1, p, upath, path->m_name, addp);
2162 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2165 /* rename temp back to source */
2166 renamefile(vol, -1, temp, p, spath, adsp);
2167 of_rename(vol, s_of, curdir, temp, sdir, spath);
2170 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2171 ad_close(adsp, ADFLAGS_HF);
2173 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2174 ad_close(addp, ADFLAGS_HF);
2178 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2179 (void)dir_remove(vol, cached);
2180 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2181 (void)dir_remove(vol, cached);