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>
26 #include "directory.h"
36 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
37 * field bytes subfield bytes
40 * ioFlFndrInfo 16 -> type 4 type field
41 * creator 4 creator field
42 * flags 2 finder flags:
44 * location 4 location in window
45 * folder 2 window that contains file
47 * ioFlXFndrInfo 16 -> iconID 2 icon id
49 * script 1 script system
51 * commentID 2 comment id
52 * putawayID 4 home directory id
55 const u_char ufinderi[ADEDLEN_FINDERI] = {
56 0, 0, 0, 0, 0, 0, 0, 0,
57 1, 0, 0, 0, 0, 0, 0, 0,
58 0, 0, 0, 0, 0, 0, 0, 0,
59 0, 0, 0, 0, 0, 0, 0, 0
62 static const u_char old_ufinderi[] = {
63 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
66 /* ----------------------
68 static int default_type(void *finder)
70 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
75 /* FIXME path : unix or mac name ? (for now it's unix name ) */
76 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);
88 if (default_type(ad_finder))
92 memcpy(data, ufinderi, ADEDLEN_FINDERI);
94 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
97 ashort = htons(FINDERINFO_INVISIBLE);
98 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
104 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
105 linkflag |= htons(FINDERINFO_ISALIAS);
106 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
107 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
108 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
112 /** Only enter if no appledouble information and no finder information found. */
113 if (chk_ext && (em = getextmap( upath ))) {
114 memcpy(data, em->em_type, sizeof( em->em_type ));
115 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
120 /* ---------------------
122 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, u_int32_t utf8)
127 aint = strlen( name );
131 if (utf8_encoding()) {
132 /* but name is an utf8 mac name */
135 /* global static variable... */
137 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
146 if (aint > MACFILELEN)
153 if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
154 aint = UTF8FILELEN_EARLY;
156 utf8 = vol->v_kTextEncoding;
157 memcpy(data, &utf8, sizeof(utf8));
158 data += sizeof(utf8);
161 memcpy(data, &temp, sizeof(temp));
162 data += sizeof(temp);
165 memcpy( data, src, aint );
175 * FIXME: PDINFO is UTF8 and doesn't need adp
177 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
178 (1 << FILPBIT_CDATE) |\
179 (1 << FILPBIT_MDATE) |\
180 (1 << FILPBIT_BDATE) |\
181 (1 << FILPBIT_FINFO) |\
182 (1 << FILPBIT_RFLEN) |\
183 (1 << FILPBIT_EXTRFLEN) |\
184 (1 << FILPBIT_PDINFO) |\
185 (1 << FILPBIT_FNUM) |\
186 (1 << FILPBIT_UNIXPR)))
189 * @brief Get CNID for did/upath args both from database and adouble file
191 * 1. Get the objects CNID as stored in its adouble file
192 * 2. Get the objects CNID from the database
193 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
194 * 4. In case 2 and 3 differ, store 3 in the adouble file
196 * @param vol (rw) volume
197 * @param adp (rw) adouble struct of object upath, might be NULL
198 * @param st (r) stat of upath, must NOT be NULL
199 * @param did (r) parent CNID of upath
200 * @param upath (r) name of object
201 * @param len (r) strlen of upath
203 uint32_t get_id(struct vol *vol,
205 const struct stat *st,
210 static int first = 1; /* mark if this func is called the first time */
212 u_int32_t dbcnid = CNID_INVALID;
215 if (vol->v_cdb != NULL) {
216 /* prime aint with what we think is the cnid, set did to zero for
217 catching moved files */
218 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
220 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
221 /* Throw errors if cnid_add fails. */
222 if (dbcnid == CNID_INVALID) {
224 case CNID_ERR_CLOSE: /* the db is closed */
227 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
228 afp_errno = AFPERR_PARAM;
231 afp_errno = AFPERR_PARAM;
234 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
235 /* we have to do it here for "dbd" because it uses "lazy opening" */
236 /* In order to not end in a loop somehow with goto restart below */
238 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
239 cnid_close(vol->v_cdb);
240 free(vol->v_cnidscheme);
241 vol->v_cnidscheme = strdup("tdb");
243 int flags = CNID_FLAG_MEMORY;
244 if ((vol->v_flags & AFPVOL_NODEV)) {
245 flags |= CNID_FLAG_NODEV;
247 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
249 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
251 /* deactivate cnid caching/storing in AppleDouble files and set ro mode*/
252 vol->v_flags &= ~AFPVOL_CACHE;
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))) {
314 struct dir *cachedfile;
315 int len = strlen(upath);
316 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len, st->st_ctime)) != NULL)
317 id = cachedfile->d_did;
319 id = get_id(vol, adp, st, dir->d_did, upath, len);
321 /* Add it to the cache */
322 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
323 ntohl(dir->d_did), upath, ntohl(id));
325 /* Get macname from unixname first */
326 if (path->m_name == NULL) {
327 if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
328 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
333 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, NULL, st->st_ctime)) == NULL) {
334 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
338 if ((dircache_add(vol, cachedfile)) != 0) {
339 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
347 if (id == CNID_INVALID)
351 path->m_name = utompath(vol, upath, id, utf8_encoding());
354 while ( bitmap != 0 ) {
355 while (( bitmap & 1 ) == 0 ) {
363 ad_getattr(adp, &ashort);
364 } else if (vol_inv_dots(vol) && *upath == '.') {
365 ashort = htons(ATTRBIT_INVISIBLE);
369 /* FIXME do we want a visual clue if the file is read only
372 accessmode( ".", &ma, dir , NULL);
373 if ((ma.ma_user & AR_UWRITE)) {
374 accessmode( upath, &ma, dir , st);
375 if (!(ma.ma_user & AR_UWRITE)) {
376 ashort |= htons(ATTRBIT_NOWRITE);
380 memcpy(data, &ashort, sizeof( ashort ));
381 data += sizeof( ashort );
382 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
383 path->u_name, ntohs(ashort));
387 memcpy(data, &dir->d_did, sizeof( u_int32_t ));
388 data += sizeof( u_int32_t );
389 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
390 path->u_name, ntohl(dir->d_did));
394 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
395 aint = AD_DATE_FROM_UNIX(st->st_mtime);
396 memcpy(data, &aint, sizeof( aint ));
397 data += sizeof( aint );
401 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
402 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
403 aint = AD_DATE_FROM_UNIX(st->st_mtime);
406 aint = AD_DATE_FROM_UNIX(st->st_mtime);
408 memcpy(data, &aint, sizeof( int ));
409 data += sizeof( int );
413 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
414 aint = AD_DATE_START;
415 memcpy(data, &aint, sizeof( int ));
416 data += sizeof( int );
420 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
421 data += ADEDLEN_FINDERI;
426 data += sizeof( u_int16_t );
430 memset(data, 0, sizeof(u_int16_t));
431 data += sizeof( u_int16_t );
435 memcpy(data, &id, sizeof( id ));
436 data += sizeof( id );
437 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
438 path->u_name, ntohl(id));
442 if (st->st_size > 0xffffffff)
445 aint = htonl( st->st_size );
446 memcpy(data, &aint, sizeof( aint ));
447 data += sizeof( aint );
452 if (adp->ad_rlen > 0xffffffff)
455 aint = htonl( adp->ad_rlen);
459 memcpy(data, &aint, sizeof( aint ));
460 data += sizeof( aint );
463 /* Current client needs ProDOS info block for this file.
464 Use simple heuristic and let the Mac "type" string tell
465 us what the PD file code should be. Everything gets a
466 subtype of 0x0000 unless the original value was hashed
467 to "pXYZ" when we created it. See IA, Ver 2.
468 <shirsch@adelphia.net> */
469 case FILPBIT_PDINFO :
470 if (afp_version >= 30) { /* UTF8 name */
471 utf8 = kTextEncodingUTF8;
473 data += sizeof( u_int16_t );
475 memcpy(data, &aint, sizeof( aint ));
476 data += sizeof( aint );
480 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
482 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
486 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
490 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
494 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
498 else if ( fdType[0] == 'p' ) {
500 ashort = (fdType[2] * 256) + fdType[3];
514 memcpy(data, &ashort, sizeof( ashort ));
515 data += sizeof( ashort );
516 memset(data, 0, sizeof( ashort ));
517 data += sizeof( ashort );
520 case FILPBIT_EXTDFLEN:
521 aint = htonl(st->st_size >> 32);
522 memcpy(data, &aint, sizeof( aint ));
523 data += sizeof( aint );
524 aint = htonl(st->st_size);
525 memcpy(data, &aint, sizeof( aint ));
526 data += sizeof( aint );
528 case FILPBIT_EXTRFLEN:
531 aint = htonl(adp->ad_rlen >> 32);
532 memcpy(data, &aint, sizeof( aint ));
533 data += sizeof( aint );
535 aint = htonl(adp->ad_rlen);
536 memcpy(data, &aint, sizeof( aint ));
537 data += sizeof( aint );
539 case FILPBIT_UNIXPR :
540 /* accessmode may change st_mode with ACLs */
541 accessmode( upath, &ma, dir , st);
543 aint = htonl(st->st_uid);
544 memcpy( data, &aint, sizeof( aint ));
545 data += sizeof( aint );
546 aint = htonl(st->st_gid);
547 memcpy( data, &aint, sizeof( aint ));
548 data += sizeof( aint );
551 type == slnk indicates an OSX style symlink,
552 we have to add S_IFLNK to the mode, otherwise
553 10.3 clients freak out. */
557 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
558 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
564 memcpy( data, &aint, sizeof( aint ));
565 data += sizeof( aint );
567 *data++ = ma.ma_user;
568 *data++ = ma.ma_world;
569 *data++ = ma.ma_group;
570 *data++ = ma.ma_owner;
574 return( AFPERR_BITMAP );
580 ashort = htons( data - buf );
581 memcpy(l_nameoff, &ashort, sizeof( ashort ));
582 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
585 ashort = htons( data - buf );
586 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
587 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
589 *buflen = data - buf;
593 /* ----------------------- */
594 int getfilparams(struct vol *vol,
596 struct path *path, struct dir *dir,
597 char *buf, size_t *buflen )
599 struct adouble ad, *adp;
603 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
605 opened = PARAM_NEED_ADP(bitmap);
610 int flags = (bitmap & (1 << FILPBIT_ATTR)) ? ADFLAGS_CHECK_OF : 0;
612 adp = of_ad(vol, path, &ad);
613 upath = path->u_name;
615 if ( ad_metadata( upath, flags, adp) < 0 ) {
618 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
619 upath, strerror(errno));
620 return AFPERR_ACCESS;
622 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
631 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
632 ad_close_metadata( adp);
637 /* ----------------------------- */
638 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
643 struct ofork *of = NULL;
645 int creatf, did, openf, retvalue = AFP_OK;
651 creatf = (unsigned char) *ibuf++;
653 memcpy(&vid, ibuf, sizeof( vid ));
654 ibuf += sizeof( vid );
656 if (NULL == ( vol = getvolbyvid( vid )) )
657 return( AFPERR_PARAM );
659 if (vol->v_flags & AFPVOL_RO)
662 memcpy(&did, ibuf, sizeof( did));
663 ibuf += sizeof( did );
665 if (NULL == ( dir = dirlookup( vol, did )) )
668 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
669 return get_afp_errno(AFPERR_PARAM);
671 if ( *s_path->m_name == '\0' )
672 return( AFPERR_BADTYPE );
674 upath = s_path->u_name;
675 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
677 /* if upath is deleted we already in trouble anyway */
678 if ((of = of_findname(s_path))) {
686 openf = O_RDWR|O_CREAT|O_TRUNC;
688 /* on a soft create, if the file is open then ad_open won't fail
689 because open syscall is not called */
690 openf = O_RDWR|O_CREAT|O_EXCL;
692 if ( ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF,
693 openf, 0666, openf, 0666) < 0 ) {
697 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
698 return ( AFPERR_NOOBJ );
700 return( AFPERR_EXIST );
702 return( AFPERR_ACCESS );
705 return( AFPERR_DFULL );
707 return( AFPERR_PARAM );
710 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
711 /* on noadouble volumes, just creating the data fork is ok */
712 if (vol_noadouble(vol)) {
713 ad_close( &ad, ADFLAGS_DF );
714 goto createfile_done;
716 /* FIXME with hard create on an existing file, we already
717 * corrupted the data file.
719 netatalk_unlink( upath );
720 ad_close( &ad, ADFLAGS_DF );
721 return AFPERR_ACCESS;
724 path = s_path->m_name;
725 ad_setname(&ad, path);
728 if (lstat(upath, &st) != 0) {
729 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
730 upath, strerror(errno));
731 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF);
735 (void)get_id(vol, adp, &st, dir->d_did, upath, strlen(upath));
738 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
743 setvoltime(obj, vol );
748 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
754 u_int16_t vid, bitmap;
759 memcpy(&vid, ibuf, sizeof( vid ));
760 ibuf += sizeof( vid );
761 if (NULL == ( vol = getvolbyvid( vid )) ) {
762 return( AFPERR_PARAM );
765 if (vol->v_flags & AFPVOL_RO)
768 memcpy(&did, ibuf, sizeof( did ));
769 ibuf += sizeof( did );
770 if (NULL == ( dir = dirlookup( vol, did )) ) {
771 return afp_errno; /* was AFPERR_NOOBJ */
774 memcpy(&bitmap, ibuf, sizeof( bitmap ));
775 bitmap = ntohs( bitmap );
776 ibuf += sizeof( bitmap );
778 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
779 return get_afp_errno(AFPERR_PARAM);
782 if (path_isadir(s_path)) {
783 return( AFPERR_BADTYPE ); /* it's a directory */
786 if ( s_path->st_errno != 0 ) {
787 return( AFPERR_NOOBJ );
790 if ((u_long)ibuf & 1 ) {
794 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
795 setvoltime(obj, vol );
802 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
805 extern struct path Cur_Path;
807 int setfilparams(struct vol *vol,
808 struct path *path, u_int16_t f_bitmap, char *buf )
810 struct adouble ad, *adp;
812 int bit, isad = 1, err = AFP_OK;
814 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
815 u_int16_t ashort, bshort, oshort;
818 u_int16_t upriv_bit = 0;
822 int change_mdate = 0;
823 int change_parent_mdate = 0;
828 u_int16_t bitmap = f_bitmap;
829 u_int32_t cdate,bdate;
830 u_char finder_buf[32];
833 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
836 adp = of_ad(vol, path, &ad);
837 upath = path->u_name;
839 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
840 return AFPERR_ACCESS;
843 /* with unix priv maybe we have to change adouble file priv first */
845 while ( bitmap != 0 ) {
846 while (( bitmap & 1 ) == 0 ) {
853 memcpy(&ashort, buf, sizeof( ashort ));
854 buf += sizeof( ashort );
858 memcpy(&cdate, buf, sizeof(cdate));
859 buf += sizeof( cdate );
862 memcpy(&newdate, buf, sizeof( newdate ));
863 buf += sizeof( newdate );
867 memcpy(&bdate, buf, sizeof( bdate));
868 buf += sizeof( bdate );
872 memcpy(finder_buf, buf, 32 );
873 if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
878 char buf[PATH_MAX+1];
879 if ((fp=open(path->u_name,O_RDONLY))>=0){
880 if ((len=read(fp,buf,PATH_MAX+1))){
881 if (unlink(path->u_name)==0){
883 erc = symlink(buf, path->u_name);
892 goto setfilparam_done;
897 case FILPBIT_UNIXPR :
898 if (!vol_unix_priv(vol)) {
899 /* this volume doesn't use unix priv */
905 change_parent_mdate = 1;
907 memcpy( &aint, buf, sizeof( aint ));
908 f_uid = ntohl (aint);
909 buf += sizeof( aint );
910 memcpy( &aint, buf, sizeof( aint ));
911 f_gid = ntohl (aint);
912 buf += sizeof( aint );
913 setfilowner(vol, f_uid, f_gid, path);
915 memcpy( &upriv, buf, sizeof( upriv ));
916 buf += sizeof( upriv );
917 upriv = ntohl (upriv);
918 if ((upriv & S_IWUSR)) {
919 setfilunixmode(vol, path, upriv);
926 case FILPBIT_PDINFO :
927 if (afp_version < 30) { /* else it's UTF8 name */
930 /* Keep special case to support crlf translations */
931 if ((unsigned int) achar == 0x04) {
932 fdType = (u_char *)"TEXT";
935 xyy[0] = ( u_char ) 'p';
946 /* break while loop */
955 /* second try with adouble open
957 if ( ad_open(adp, upath, ADFLAGS_HF, O_RDWR | O_CREAT, 0666) < 0) {
958 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
960 * For some things, we don't need an adouble header:
961 * - change of modification date
962 * - UNIX privs (Bug-ID #2863424)
964 if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
965 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
966 return AFPERR_ACCESS;
968 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
970 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
971 ad_setname(adp, path->m_name);
976 while ( bitmap != 0 ) {
977 while (( bitmap & 1 ) == 0 ) {
984 ad_getattr(adp, &bshort);
986 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
987 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
991 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
992 change_parent_mdate = 1;
993 ad_setattr(adp, bshort);
996 ad_setdate(adp, AD_DATE_CREATE, cdate);
1000 case FILPBIT_BDATE :
1001 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1003 case FILPBIT_FINFO :
1004 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1006 ((em = getextmap( path->m_name )) &&
1007 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1008 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1009 || ((em = getdefextmap()) &&
1010 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1011 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1013 memcpy(finder_buf, ufinderi, 8 );
1015 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1017 case FILPBIT_UNIXPR :
1019 setfilunixmode(vol, path, upriv);
1022 case FILPBIT_PDINFO :
1023 if (afp_version < 30) { /* else it's UTF8 name */
1024 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1025 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1030 err = AFPERR_BITMAP;
1031 goto setfilparam_done;
1038 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1039 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1043 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1044 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1050 ad_close_metadata( adp);
1053 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1054 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1055 bitmap = 1<<FILPBIT_MDATE;
1056 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1060 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1066 * renamefile and copyfile take the old and new unix pathnames
1067 * and the new mac name.
1069 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1070 * passing -1 means this is not used, src path is a full path
1071 * src the source path
1072 * dst the dest filename in current dir
1073 * newname the dest mac name
1074 * adp adouble struct of src file, if open, or & zeroed one
1077 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1081 LOG(log_debug, logtype_afpd,
1082 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1084 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1087 return( AFPERR_NOOBJ );
1090 return( AFPERR_ACCESS );
1092 return AFPERR_VLOCK;
1093 case EXDEV : /* Cross device move -- try copy */
1094 /* NOTE: with open file it's an error because after the copy we will
1095 * get two files, it's fixable for our process (eg reopen the new file, get the
1096 * locks, and so on. But it doesn't solve the case with a second process
1098 if (adp->ad_open_forks) {
1099 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1100 return AFPERR_OLOCK; /* little lie */
1102 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1103 /* on error copyfile delete dest */
1106 return deletefile(vol, sdir_fd, src, 0);
1108 return( AFPERR_PARAM );
1112 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1116 /* try to undo the data fork rename,
1117 * we know we are on the same device
1120 unix_rename(-1, dst, sdir_fd, src );
1121 /* return the first error */
1124 return AFPERR_NOOBJ;
1127 return AFPERR_ACCESS ;
1129 return AFPERR_VLOCK;
1131 return AFPERR_PARAM ;
1136 /* don't care if we can't open the newly renamed ressource fork */
1137 if (ad_open(adp, dst, ADFLAGS_HF, O_RDWR) == 0) {
1138 ad_setname(adp, newname);
1140 ad_close( adp, ADFLAGS_HF );
1147 convert a Mac long name to an utf8 name,
1149 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1153 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1159 /* ---------------- */
1160 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1167 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1173 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1174 if (afp_version >= 30) {
1175 /* convert it to UTF8
1177 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1181 strncpy( newname, ibuf, plen );
1182 newname[ plen ] = '\0';
1184 if (strlen(newname) != plen) {
1185 /* there's \0 in newname, e.g. it's a pathname not
1193 memcpy(&hint, ibuf, sizeof(hint));
1194 ibuf += sizeof(hint);
1196 memcpy(&len16, ibuf, sizeof(len16));
1197 ibuf += sizeof(len16);
1198 plen = ntohs(len16);
1201 if (plen > AFPOBJ_TMPSIZ) {
1204 strncpy( newname, ibuf, plen );
1205 newname[ plen ] = '\0';
1206 if (strlen(newname) != plen) {
1215 /* -----------------------------------
1217 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1219 struct vol *s_vol, *d_vol;
1221 char *newname, *p, *upath;
1222 struct path *s_path;
1223 u_int32_t sdid, ddid;
1224 int err, retvalue = AFP_OK;
1225 u_int16_t svid, dvid;
1227 struct adouble ad, *adp;
1233 memcpy(&svid, ibuf, sizeof( svid ));
1234 ibuf += sizeof( svid );
1235 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1236 return( AFPERR_PARAM );
1239 memcpy(&sdid, ibuf, sizeof( sdid ));
1240 ibuf += sizeof( sdid );
1241 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1245 memcpy(&dvid, ibuf, sizeof( dvid ));
1246 ibuf += sizeof( dvid );
1247 memcpy(&ddid, ibuf, sizeof( ddid ));
1248 ibuf += sizeof( ddid );
1250 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1251 return get_afp_errno(AFPERR_PARAM);
1253 if ( path_isadir(s_path) ) {
1254 return( AFPERR_BADTYPE );
1257 /* don't allow copies when the file is open.
1258 * XXX: the spec only calls for read/deny write access.
1259 * however, copyfile doesn't have any of that info,
1260 * and locks need to stay coherent. as a result,
1261 * we just balk if the file is opened already. */
1263 adp = of_ad(s_vol, s_path, &ad);
1265 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0) {
1266 return AFPERR_DENYCONF;
1268 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1269 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1272 retvalue = AFPERR_DENYCONF;
1276 newname = obj->newtmp;
1277 strcpy( newname, s_path->m_name );
1279 p = ctoupath( s_vol, curdir, newname );
1281 retvalue = AFPERR_PARAM;
1285 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1286 retvalue = AFPERR_PARAM;
1290 if (d_vol->v_flags & AFPVOL_RO) {
1291 retvalue = AFPERR_VLOCK;
1295 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1296 retvalue = afp_errno;
1300 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1301 retvalue = get_afp_errno(AFPERR_NOOBJ);
1305 if ( *s_path->m_name != '\0' ) {
1306 retvalue =path_error(s_path, AFPERR_NOOBJ);
1310 /* one of the handful of places that knows about the path type */
1311 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1312 retvalue = AFPERR_PARAM;
1315 /* newname is always only a filename so curdir *is* its
1318 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1319 retvalue =AFPERR_PARAM;
1323 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1329 setvoltime(obj, d_vol );
1332 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1336 /* ----------------------- */
1337 static int copy_all(const int dfd, const void *buf,
1343 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1346 while (buflen > 0) {
1347 if ((cc = write(dfd, buf, buflen)) < 0) {
1359 LOG(log_debug9, logtype_afpd, "end copy_all:");
1365 /* --------------------------
1366 * copy only the fork data stream
1368 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1375 if (eid == ADEID_DFORK) {
1376 sfd = ad_data_fileno(ads);
1377 dfd = ad_data_fileno(add);
1380 sfd = ad_reso_fileno(ads);
1381 dfd = ad_reso_fileno(add);
1384 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1387 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1390 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1391 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1395 #define BUF 128*1024*1024
1397 if (fstat(sfd, &st) == 0) {
1400 if ( offset >= st.st_size) {
1403 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1404 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1407 case EINVAL: /* there's no guarantee that all fs support sendfile */
1416 lseek(sfd, offset, SEEK_SET);
1420 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1427 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1434 /* ----------------------------------
1435 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1436 * because we are doing it elsewhere.
1437 * currently if newname is NULL then adp is NULL.
1439 int copyfile(const struct vol *s_vol,
1440 const struct vol *d_vol,
1445 struct adouble *adp)
1447 struct adouble ads, add;
1454 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1455 sfd, src, dst, newname);
1458 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1462 adflags = ADFLAGS_DF;
1464 adflags |= ADFLAGS_HF;
1467 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0) {
1472 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1473 /* no resource fork, don't create one for dst file */
1474 adflags &= ~ADFLAGS_HF;
1477 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1479 if (stat_result < 0) {
1480 /* unlikely but if fstat fails, the default file mode will be 0666. */
1481 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1484 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1485 if (ad_open(&add, dst, adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, O_RDWR|O_CREAT|O_EXCL, st.st_mode) < 0) {
1487 ad_close( adp, adflags );
1488 if (EEXIST != ret_err) {
1489 deletefile(d_vol, -1, dst, 0);
1492 return AFPERR_EXIST;
1496 * XXX if the source and the dest don't use the same resource type it's broken
1498 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1499 /* copy the data fork */
1500 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1501 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1509 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1510 /* set the new name in the resource fork */
1511 ad_copy_header(&add, adp);
1512 ad_setname(&add, newname);
1515 ad_close( adp, adflags );
1517 if (ad_close( &add, adflags ) <0) {
1522 deletefile(d_vol, -1, dst, 0);
1524 else if (stat_result == 0) {
1525 /* set dest modification date to src date */
1528 ut.actime = ut.modtime = st.st_mtime;
1530 /* FIXME netatalk doesn't use resource fork file date
1531 * but maybe we should set its modtime too.
1536 switch ( ret_err ) {
1542 return AFPERR_DFULL;
1544 return AFPERR_NOOBJ;
1546 return AFPERR_ACCESS;
1548 return AFPERR_VLOCK;
1550 return AFPERR_PARAM;
1554 /* -----------------------------------
1555 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1556 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1558 when deletefile is called we don't have lock on it, file is closed (for us)
1559 untrue if called by renamefile
1561 ad_open always try to open file RDWR first and ad_lock takes care of
1562 WRITE lock on read only file.
1565 static int check_attrib(struct adouble *adp)
1567 u_int16_t bshort = 0;
1569 ad_getattr(adp, &bshort);
1571 * Does kFPDeleteInhibitBit (bit 8) set?
1573 if ((bshort & htons(ATTRBIT_NODELETE))) {
1574 return AFPERR_OLOCK;
1576 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1582 * dirfd can be used for unlinkat semantics
1584 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1587 struct adouble *adp = NULL;
1588 int adflags, err = AFP_OK;
1591 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1593 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1595 /* was EACCESS error try to get only metadata */
1596 /* we never want to create a resource fork here, we are going to delete it
1597 * moreover sometimes deletefile is called with a no existent file and
1598 * ad_open would create a 0 byte resource fork
1600 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1601 if ((err = check_attrib(&ad))) {
1602 ad_close_metadata(&ad);
1609 /* try to open both forks at once */
1610 adflags = ADFLAGS_DF;
1611 if ( ad_openat(&ad, dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0 ) {
1616 case EACCES: /* maybe it's a file with no write mode for us */
1617 break; /* was return AFPERR_ACCESS;*/
1630 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1631 adflags |= ADFLAGS_HF;
1632 /* FIXME we have a pb here because we want to know if a file is open
1633 * there's a 'priority inversion' if you can't open the ressource fork RW
1634 * you can delete it if it's open because you can't get a write lock.
1636 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1639 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1641 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1647 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1649 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1651 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1652 cnid_delete(vol->v_cdb, id);
1658 ad_close_metadata(&ad);
1661 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1666 /* ------------------------------------ */
1667 /* return a file id */
1668 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1677 struct path *s_path;
1683 memcpy(&vid, ibuf, sizeof(vid));
1684 ibuf += sizeof(vid);
1686 if (NULL == ( vol = getvolbyvid( vid )) ) {
1687 return( AFPERR_PARAM);
1690 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1694 if (vol->v_flags & AFPVOL_RO)
1695 return AFPERR_VLOCK;
1697 memcpy(&did, ibuf, sizeof( did ));
1698 ibuf += sizeof(did);
1700 if (NULL == ( dir = dirlookup( vol, did )) ) {
1701 return afp_errno; /* was AFPERR_PARAM */
1704 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1705 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1708 if ( path_isadir(s_path) ) {
1709 return( AFPERR_BADTYPE );
1712 upath = s_path->u_name;
1713 switch (s_path->st_errno) {
1715 break; /* success */
1718 return AFPERR_ACCESS;
1720 return AFPERR_NOOBJ;
1722 return AFPERR_PARAM;
1725 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1726 memcpy(rbuf, &id, sizeof(id));
1727 *rbuflen = sizeof(id);
1728 return AFPERR_EXISTID;
1731 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1732 memcpy(rbuf, &id, sizeof(id));
1733 *rbuflen = sizeof(id);
1740 /* ------------------------------- */
1746 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1749 struct reenum *param = data;
1750 struct vol *vol = param->vol;
1751 cnid_t did = param->did;
1754 if ( lstat(de->d_name, &path.st) < 0 )
1757 /* update or add to cnid */
1758 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1763 /* --------------------
1764 * Ok the db is out of synch with the dir.
1765 * but if it's a deleted file we don't want to do it again and again.
1768 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1774 if (vol->v_cdb == NULL) {
1778 /* FIXME use of_statdir ? */
1779 if (lstat(name, &st)) {
1783 if (dirreenumerate(dir, &st)) {
1784 /* we already did it once and the dir haven't been modified */
1789 data.did = dir->d_did;
1790 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1791 setdiroffcnt(curdir, &st, ret);
1792 dir->d_flags |= DIRF_CNID;
1798 /* ------------------------------
1799 resolve a file id */
1800 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1809 u_int16_t vid, bitmap;
1811 static char buffer[12 + MAXPATHLEN + 1];
1812 int len = 12 + MAXPATHLEN + 1;
1817 memcpy(&vid, ibuf, sizeof(vid));
1818 ibuf += sizeof(vid);
1820 if (NULL == ( vol = getvolbyvid( vid )) ) {
1821 return( AFPERR_PARAM);
1824 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1828 memcpy(&id, ibuf, sizeof( id ));
1833 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1837 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1838 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1841 if (NULL == ( dir = dirlookup( vol, id )) ) {
1842 return AFPERR_NOID; /* idem AFPERR_PARAM */
1844 if (movecwd(vol, dir) < 0) {
1848 return AFPERR_ACCESS;
1852 return AFPERR_PARAM;
1856 memset(&path, 0, sizeof(path));
1857 path.u_name = upath;
1858 if ( of_stat(&path) < 0 ) {
1860 /* with nfs and our working directory is deleted */
1861 if (errno == ESTALE) {
1865 if ( errno == ENOENT && !retry) {
1866 /* cnid db is out of sync, reenumerate the directory and update ids */
1867 reenumerate_id(vol, ".", dir);
1875 return AFPERR_ACCESS;
1879 return AFPERR_PARAM;
1883 /* directories are bad */
1884 if (S_ISDIR(path.st.st_mode)) {
1885 /* OS9 and OSX don't return the same error code */
1886 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1889 memcpy(&bitmap, ibuf, sizeof(bitmap));
1890 bitmap = ntohs( bitmap );
1891 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1895 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1896 rbuf + sizeof(bitmap), &buflen))) {
1899 *rbuflen = buflen + sizeof(bitmap);
1900 memcpy(rbuf, ibuf, sizeof(bitmap));
1905 /* ------------------------------ */
1906 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1916 static char buffer[12 + MAXPATHLEN + 1];
1917 int len = 12 + MAXPATHLEN + 1;
1922 memcpy(&vid, ibuf, sizeof(vid));
1923 ibuf += sizeof(vid);
1925 if (NULL == ( vol = getvolbyvid( vid )) ) {
1926 return( AFPERR_PARAM);
1929 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1933 if (vol->v_flags & AFPVOL_RO)
1934 return AFPERR_VLOCK;
1936 memcpy(&id, ibuf, sizeof( id ));
1940 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1944 if (NULL == ( dir = dirlookup( vol, id )) ) {
1945 if (afp_errno == AFPERR_NOOBJ) {
1949 return( AFPERR_PARAM );
1953 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1957 return AFPERR_ACCESS;
1962 /* still try to delete the id */
1966 return AFPERR_PARAM;
1969 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1970 return AFPERR_BADTYPE;
1973 if (cnid_delete(vol->v_cdb, fileid)) {
1976 return AFPERR_VLOCK;
1979 return AFPERR_ACCESS;
1981 return AFPERR_PARAM;
1988 /* ------------------------------ */
1989 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
1993 if (path->st_errno) {
1994 switch (path->st_errno) {
1996 afp_errno = AFPERR_NOID;
2000 afp_errno = AFPERR_ACCESS;
2003 afp_errno = AFPERR_PARAM;
2008 /* we use file_access both for legacy Mac perm and
2009 * for unix privilege, rename will take care of folder perms
2011 if (file_access(path, OPENACC_WR ) < 0) {
2012 afp_errno = AFPERR_ACCESS;
2016 if ((*of = of_findname(path))) {
2017 /* reuse struct adouble so it won't break locks */
2021 ret = ad_open(adp, path->u_name, ADFLAGS_HF, O_RDONLY);
2023 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2025 * The user must have the Read & Write privilege for both files in order to use this command.
2027 ad_close(adp, ADFLAGS_HF);
2028 afp_errno = AFPERR_ACCESS;
2035 #define APPLETEMP ".AppleTempXXXXXX"
2037 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2039 struct stat srcst, destst;
2041 struct dir *dir, *sdir;
2042 char *spath, temp[17], *p;
2043 char *supath, *upath;
2048 struct adouble *adsp = NULL;
2049 struct adouble *addp = NULL;
2050 struct ofork *s_of = NULL;
2051 struct ofork *d_of = NULL;
2064 memcpy(&vid, ibuf, sizeof(vid));
2065 ibuf += sizeof(vid);
2067 if (NULL == ( vol = getvolbyvid( vid )) ) {
2068 return( AFPERR_PARAM);
2071 if ((vol->v_flags & AFPVOL_RO))
2072 return AFPERR_VLOCK;
2074 /* source and destination dids */
2075 memcpy(&sid, ibuf, sizeof(sid));
2076 ibuf += sizeof(sid);
2077 memcpy(&did, ibuf, sizeof(did));
2078 ibuf += sizeof(did);
2081 if (NULL == (dir = dirlookup( vol, sid )) ) {
2082 return afp_errno; /* was AFPERR_PARAM */
2085 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2086 return get_afp_errno(AFPERR_NOOBJ);
2089 if ( path_isadir(path) ) {
2090 return AFPERR_BADTYPE; /* it's a dir */
2093 /* save some stuff */
2096 spath = obj->oldtmp;
2097 supath = obj->newtmp;
2098 strcpy(spath, path->m_name);
2099 strcpy(supath, path->u_name); /* this is for the cnid changing */
2100 p = absupath( vol, sdir, supath);
2102 /* pathname too long */
2103 return AFPERR_PARAM ;
2106 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2107 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2111 /* ***** from here we may have resource fork open **** */
2113 /* look for the source cnid. if it doesn't exist, don't worry about
2115 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2117 if (NULL == ( dir = dirlookup( vol, did )) ) {
2118 err = afp_errno; /* was AFPERR_PARAM */
2119 goto err_exchangefile;
2122 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2123 err = get_afp_errno(AFPERR_NOOBJ);
2124 goto err_exchangefile;
2127 if ( path_isadir(path) ) {
2128 err = AFPERR_BADTYPE;
2129 goto err_exchangefile;
2132 /* FPExchangeFiles is the only call that can return the SameObj
2134 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2135 err = AFPERR_SAMEOBJ;
2136 goto err_exchangefile;
2139 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2140 if (!(addp = find_adouble( path, &d_of, &add))) {
2142 goto err_exchangefile;
2146 /* they are not on the same device and at least one is open
2147 * FIXME broken for for crossdev and adouble v2
2150 crossdev = (srcst.st_dev != destst.st_dev);
2151 if (/* (d_of || s_of) && */ crossdev) {
2153 goto err_exchangefile;
2156 /* look for destination id. */
2157 upath = path->u_name;
2158 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2160 /* construct a temp name.
2161 * NOTE: the temp file will be in the dest file's directory. it
2162 * will also be inaccessible from AFP. */
2163 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2164 if (!mktemp(temp)) {
2166 goto err_exchangefile;
2170 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2171 ad_close(adsp, ADFLAGS_HF);
2172 ad_close(addp, ADFLAGS_HF);
2175 /* now, quickly rename the file. we error if we can't. */
2176 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2177 goto err_exchangefile;
2178 of_rename(vol, s_of, sdir, spath, curdir, temp);
2180 /* rename destination to source */
2181 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2182 goto err_src_to_tmp;
2183 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2185 /* rename temp to destination */
2186 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2187 goto err_dest_to_src;
2188 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2190 /* id's need switching. src -> dest and dest -> src.
2191 * we need to re-stat() if it was a cross device copy.
2194 cnid_delete(vol->v_cdb, sid);
2196 cnid_delete(vol->v_cdb, did);
2198 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2199 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2201 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2202 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2207 err = AFPERR_ACCESS;
2212 goto err_temp_to_dest;
2215 /* here we need to reopen if crossdev */
2216 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2221 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2226 /* change perms, src gets dest perm and vice versa */
2231 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2232 err = AFP_OK; /* ignore error */
2233 goto err_temp_to_dest;
2237 * we need to exchange ACL entries as well
2239 /* exchange_acls(vol, p, upath); */
2244 path->m_name = NULL;
2245 path->u_name = upath;
2247 setfilunixmode(vol, path, destst.st_mode);
2248 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2255 setfilunixmode(vol, path, srcst.st_mode);
2256 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2258 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2259 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2264 goto err_exchangefile;
2266 /* all this stuff is so that we can unwind a failed operation
2269 /* rename dest to temp */
2270 renamefile(vol, -1, upath, temp, temp, adsp);
2271 of_rename(vol, s_of, curdir, upath, curdir, temp);
2274 /* rename source back to dest */
2275 renamefile(vol, -1, p, upath, path->m_name, addp);
2276 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2279 /* rename temp back to source */
2280 renamefile(vol, -1, temp, p, spath, adsp);
2281 of_rename(vol, s_of, curdir, temp, sdir, spath);
2284 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2285 ad_close(adsp, ADFLAGS_HF);
2287 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2288 ad_close(addp, ADFLAGS_HF);
2292 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2293 (void)dir_remove(vol, cached);
2294 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2295 (void)dir_remove(vol, cached);