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 > 255) /* FIXME safeguard, anyway if no ascii char it's game over*/
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 upath = path->u_name;
308 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
309 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
310 || (bitmap & (1 << FILPBIT_FNUM))) {
312 struct dir *cachedfile;
313 int len = strlen(upath);
314 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len, st->st_ctime)) != NULL)
315 id = cachedfile->d_did;
317 id = get_id(vol, adp, st, dir->d_did, upath, len);
319 /* Add it to the cache */
320 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
321 ntohl(dir->d_did), upath, ntohl(id));
323 /* Get macname from unixname first */
324 if (path->m_name == NULL) {
325 if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
326 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
331 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, NULL, st->st_ctime)) == NULL) {
332 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
336 if ((dircache_add(cachedfile)) != 0) {
337 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
345 if (id == CNID_INVALID)
349 path->m_name = utompath(vol, upath, id, utf8_encoding());
352 while ( bitmap != 0 ) {
353 while (( bitmap & 1 ) == 0 ) {
361 ad_getattr(adp, &ashort);
362 } else if (vol_inv_dots(vol) && *upath == '.') {
363 ashort = htons(ATTRBIT_INVISIBLE);
367 /* FIXME do we want a visual clue if the file is read only
370 accessmode( ".", &ma, dir , NULL);
371 if ((ma.ma_user & AR_UWRITE)) {
372 accessmode( upath, &ma, dir , st);
373 if (!(ma.ma_user & AR_UWRITE)) {
374 ashort |= htons(ATTRBIT_NOWRITE);
378 memcpy(data, &ashort, sizeof( ashort ));
379 data += sizeof( ashort );
380 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
381 path->u_name, ntohs(ashort));
385 memcpy(data, &dir->d_did, sizeof( u_int32_t ));
386 data += sizeof( u_int32_t );
387 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
388 path->u_name, ntohl(dir->d_did));
392 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
393 aint = AD_DATE_FROM_UNIX(st->st_mtime);
394 memcpy(data, &aint, sizeof( aint ));
395 data += sizeof( aint );
399 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
400 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
401 aint = AD_DATE_FROM_UNIX(st->st_mtime);
404 aint = AD_DATE_FROM_UNIX(st->st_mtime);
406 memcpy(data, &aint, sizeof( int ));
407 data += sizeof( int );
411 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
412 aint = AD_DATE_START;
413 memcpy(data, &aint, sizeof( int ));
414 data += sizeof( int );
418 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
419 data += ADEDLEN_FINDERI;
424 data += sizeof( u_int16_t );
428 memset(data, 0, sizeof(u_int16_t));
429 data += sizeof( u_int16_t );
433 memcpy(data, &id, sizeof( id ));
434 data += sizeof( id );
435 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
436 path->u_name, ntohl(id));
440 if (st->st_size > 0xffffffff)
443 aint = htonl( st->st_size );
444 memcpy(data, &aint, sizeof( aint ));
445 data += sizeof( aint );
450 if (adp->ad_rlen > 0xffffffff)
453 aint = htonl( adp->ad_rlen);
457 memcpy(data, &aint, sizeof( aint ));
458 data += sizeof( aint );
461 /* Current client needs ProDOS info block for this file.
462 Use simple heuristic and let the Mac "type" string tell
463 us what the PD file code should be. Everything gets a
464 subtype of 0x0000 unless the original value was hashed
465 to "pXYZ" when we created it. See IA, Ver 2.
466 <shirsch@adelphia.net> */
467 case FILPBIT_PDINFO :
468 if (afp_version >= 30) { /* UTF8 name */
469 utf8 = kTextEncodingUTF8;
471 data += sizeof( u_int16_t );
473 memcpy(data, &aint, sizeof( aint ));
474 data += sizeof( aint );
478 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
480 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
484 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
488 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
492 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
496 else if ( fdType[0] == 'p' ) {
498 ashort = (fdType[2] * 256) + fdType[3];
512 memcpy(data, &ashort, sizeof( ashort ));
513 data += sizeof( ashort );
514 memset(data, 0, sizeof( ashort ));
515 data += sizeof( ashort );
518 case FILPBIT_EXTDFLEN:
519 aint = htonl(st->st_size >> 32);
520 memcpy(data, &aint, sizeof( aint ));
521 data += sizeof( aint );
522 aint = htonl(st->st_size);
523 memcpy(data, &aint, sizeof( aint ));
524 data += sizeof( aint );
526 case FILPBIT_EXTRFLEN:
529 aint = htonl(adp->ad_rlen >> 32);
530 memcpy(data, &aint, sizeof( aint ));
531 data += sizeof( aint );
533 aint = htonl(adp->ad_rlen);
534 memcpy(data, &aint, sizeof( aint ));
535 data += sizeof( aint );
537 case FILPBIT_UNIXPR :
538 /* accessmode may change st_mode with ACLs */
539 accessmode( upath, &ma, dir , st);
541 aint = htonl(st->st_uid);
542 memcpy( data, &aint, sizeof( aint ));
543 data += sizeof( aint );
544 aint = htonl(st->st_gid);
545 memcpy( data, &aint, sizeof( aint ));
546 data += sizeof( aint );
549 type == slnk indicates an OSX style symlink,
550 we have to add S_IFLNK to the mode, otherwise
551 10.3 clients freak out. */
555 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
556 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
562 memcpy( data, &aint, sizeof( aint ));
563 data += sizeof( aint );
565 *data++ = ma.ma_user;
566 *data++ = ma.ma_world;
567 *data++ = ma.ma_group;
568 *data++ = ma.ma_owner;
572 return( AFPERR_BITMAP );
578 ashort = htons( data - buf );
579 memcpy(l_nameoff, &ashort, sizeof( ashort ));
580 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
583 ashort = htons( data - buf );
584 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
585 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
587 *buflen = data - buf;
591 /* ----------------------- */
592 int getfilparams(struct vol *vol,
594 struct path *path, struct dir *dir,
595 char *buf, size_t *buflen )
597 struct adouble ad, *adp;
601 opened = PARAM_NEED_ADP(bitmap);
606 int flags = (bitmap & (1 << FILPBIT_ATTR)) ? ADFLAGS_CHECK_OF : 0;
608 adp = of_ad(vol, path, &ad);
609 upath = path->u_name;
611 if ( ad_metadata( upath, flags, adp) < 0 ) {
614 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
615 upath, strerror(errno));
616 return AFPERR_ACCESS;
618 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
627 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
628 ad_close_metadata( adp);
633 /* ----------------------------- */
634 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
639 struct ofork *of = NULL;
641 int creatf, did, openf, retvalue = AFP_OK;
647 creatf = (unsigned char) *ibuf++;
649 memcpy(&vid, ibuf, sizeof( vid ));
650 ibuf += sizeof( vid );
652 if (NULL == ( vol = getvolbyvid( vid )) )
653 return( AFPERR_PARAM );
655 if (vol->v_flags & AFPVOL_RO)
658 memcpy(&did, ibuf, sizeof( did));
659 ibuf += sizeof( did );
661 if (NULL == ( dir = dirlookup( vol, did )) )
664 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
665 return get_afp_errno(AFPERR_PARAM);
667 if ( *s_path->m_name == '\0' )
668 return( AFPERR_BADTYPE );
670 upath = s_path->u_name;
671 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
673 /* if upath is deleted we already in trouble anyway */
674 if ((of = of_findname(s_path))) {
682 openf = O_RDWR|O_CREAT|O_TRUNC;
684 /* on a soft create, if the file is open then ad_open won't fail
685 because open syscall is not called */
686 openf = O_RDWR|O_CREAT|O_EXCL;
688 if ( ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF,
689 openf, 0666, openf, 0666) < 0 ) {
693 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
694 return ( AFPERR_NOOBJ );
696 return( AFPERR_EXIST );
698 return( AFPERR_ACCESS );
701 return( AFPERR_DFULL );
703 return( AFPERR_PARAM );
706 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
707 /* on noadouble volumes, just creating the data fork is ok */
708 if (vol_noadouble(vol)) {
709 ad_close( &ad, ADFLAGS_DF );
710 goto createfile_done;
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);
723 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
729 if (vol->v_flags & AFPVOL_DROPBOX) {
730 retvalue = matchfile2dirperms(upath, vol, did);
732 #endif /* DROPKLUDGE */
734 setvoltime(obj, vol );
739 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
745 u_int16_t vid, bitmap;
750 memcpy(&vid, ibuf, sizeof( vid ));
751 ibuf += sizeof( vid );
752 if (NULL == ( vol = getvolbyvid( vid )) ) {
753 return( AFPERR_PARAM );
756 if (vol->v_flags & AFPVOL_RO)
759 memcpy(&did, ibuf, sizeof( did ));
760 ibuf += sizeof( did );
761 if (NULL == ( dir = dirlookup( vol, did )) ) {
762 return afp_errno; /* was AFPERR_NOOBJ */
765 memcpy(&bitmap, ibuf, sizeof( bitmap ));
766 bitmap = ntohs( bitmap );
767 ibuf += sizeof( bitmap );
769 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
770 return get_afp_errno(AFPERR_PARAM);
773 if (path_isadir(s_path)) {
774 return( AFPERR_BADTYPE ); /* it's a directory */
777 if ( s_path->st_errno != 0 ) {
778 return( AFPERR_NOOBJ );
781 if ((u_long)ibuf & 1 ) {
785 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
786 setvoltime(obj, vol );
793 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
796 extern struct path Cur_Path;
798 int setfilparams(struct vol *vol,
799 struct path *path, u_int16_t f_bitmap, char *buf )
801 struct adouble ad, *adp;
803 int bit, isad = 1, err = AFP_OK;
805 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
806 u_int16_t ashort, bshort, oshort;
809 u_int16_t upriv_bit = 0;
813 int change_mdate = 0;
814 int change_parent_mdate = 0;
819 u_int16_t bitmap = f_bitmap;
820 u_int32_t cdate,bdate;
821 u_char finder_buf[32];
824 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
827 adp = of_ad(vol, path, &ad);
828 upath = path->u_name;
830 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
831 return AFPERR_ACCESS;
834 /* with unix priv maybe we have to change adouble file priv first */
836 while ( bitmap != 0 ) {
837 while (( bitmap & 1 ) == 0 ) {
844 memcpy(&ashort, buf, sizeof( ashort ));
845 buf += sizeof( ashort );
849 memcpy(&cdate, buf, sizeof(cdate));
850 buf += sizeof( cdate );
853 memcpy(&newdate, buf, sizeof( newdate ));
854 buf += sizeof( newdate );
858 memcpy(&bdate, buf, sizeof( bdate));
859 buf += sizeof( bdate );
863 memcpy(finder_buf, buf, 32 );
864 if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
869 char buf[PATH_MAX+1];
870 if ((fp=open(path->u_name,O_RDONLY))>=0){
871 if ((len=read(fp,buf,PATH_MAX+1))){
872 if (unlink(path->u_name)==0){
874 erc = symlink(buf, path->u_name);
883 goto setfilparam_done;
888 case FILPBIT_UNIXPR :
889 if (!vol_unix_priv(vol)) {
890 /* this volume doesn't use unix priv */
896 change_parent_mdate = 1;
898 memcpy( &aint, buf, sizeof( aint ));
899 f_uid = ntohl (aint);
900 buf += sizeof( aint );
901 memcpy( &aint, buf, sizeof( aint ));
902 f_gid = ntohl (aint);
903 buf += sizeof( aint );
904 setfilowner(vol, f_uid, f_gid, path);
906 memcpy( &upriv, buf, sizeof( upriv ));
907 buf += sizeof( upriv );
908 upriv = ntohl (upriv);
909 if ((upriv & S_IWUSR)) {
910 setfilunixmode(vol, path, upriv);
917 case FILPBIT_PDINFO :
918 if (afp_version < 30) { /* else it's UTF8 name */
921 /* Keep special case to support crlf translations */
922 if ((unsigned int) achar == 0x04) {
923 fdType = (u_char *)"TEXT";
926 xyy[0] = ( u_char ) 'p';
937 /* break while loop */
946 /* second try with adouble open
948 if ( ad_open(adp, upath, ADFLAGS_HF, O_RDWR | O_CREAT, 0666) < 0) {
949 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
951 * For some things, we don't need an adouble header:
952 * - change of modification date
953 * - UNIX privs (Bug-ID #2863424)
955 if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
956 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
957 return AFPERR_ACCESS;
959 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
961 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
962 ad_setname(adp, path->m_name);
967 while ( bitmap != 0 ) {
968 while (( bitmap & 1 ) == 0 ) {
975 ad_getattr(adp, &bshort);
977 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
978 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
982 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
983 change_parent_mdate = 1;
984 ad_setattr(adp, bshort);
987 ad_setdate(adp, AD_DATE_CREATE, cdate);
992 ad_setdate(adp, AD_DATE_BACKUP, bdate);
995 if (default_type( ad_entry( adp, ADEID_FINDERI ))
997 ((em = getextmap( path->m_name )) &&
998 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
999 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1000 || ((em = getdefextmap()) &&
1001 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1002 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1004 memcpy(finder_buf, ufinderi, 8 );
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 (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_metadata( adp);
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 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1075 return( AFPERR_NOOBJ );
1078 return( AFPERR_ACCESS );
1080 return AFPERR_VLOCK;
1081 case EXDEV : /* Cross device move -- try copy */
1082 /* NOTE: with open file it's an error because after the copy we will
1083 * get two files, it's fixable for our process (eg reopen the new file, get the
1084 * locks, and so on. But it doesn't solve the case with a second process
1086 if (adp->ad_open_forks) {
1087 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1088 return AFPERR_OLOCK; /* little lie */
1090 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1091 /* on error copyfile delete dest */
1094 return deletefile(vol, sdir_fd, src, 0);
1096 return( AFPERR_PARAM );
1100 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1104 /* try to undo the data fork rename,
1105 * we know we are on the same device
1108 unix_rename(-1, dst, sdir_fd, src );
1109 /* return the first error */
1112 return AFPERR_NOOBJ;
1115 return AFPERR_ACCESS ;
1117 return AFPERR_VLOCK;
1119 return AFPERR_PARAM ;
1124 /* don't care if we can't open the newly renamed ressource fork */
1125 if (ad_open(adp, dst, ADFLAGS_HF, O_RDWR) == 0) {
1126 ad_setname(adp, newname);
1128 ad_close( adp, ADFLAGS_HF );
1135 convert a Mac long name to an utf8 name,
1137 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1141 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1147 /* ---------------- */
1148 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1155 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1161 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1162 if (afp_version >= 30) {
1163 /* convert it to UTF8
1165 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1169 strncpy( newname, ibuf, plen );
1170 newname[ plen ] = '\0';
1172 if (strlen(newname) != plen) {
1173 /* there's \0 in newname, e.g. it's a pathname not
1181 memcpy(&hint, ibuf, sizeof(hint));
1182 ibuf += sizeof(hint);
1184 memcpy(&len16, ibuf, sizeof(len16));
1185 ibuf += sizeof(len16);
1186 plen = ntohs(len16);
1189 if (plen > AFPOBJ_TMPSIZ) {
1192 strncpy( newname, ibuf, plen );
1193 newname[ plen ] = '\0';
1194 if (strlen(newname) != plen) {
1203 /* -----------------------------------
1205 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1207 struct vol *s_vol, *d_vol;
1209 char *newname, *p, *upath;
1210 struct path *s_path;
1211 u_int32_t sdid, ddid;
1212 int err, retvalue = AFP_OK;
1213 u_int16_t svid, dvid;
1215 struct adouble ad, *adp;
1221 memcpy(&svid, ibuf, sizeof( svid ));
1222 ibuf += sizeof( svid );
1223 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1224 return( AFPERR_PARAM );
1227 memcpy(&sdid, ibuf, sizeof( sdid ));
1228 ibuf += sizeof( sdid );
1229 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1233 memcpy(&dvid, ibuf, sizeof( dvid ));
1234 ibuf += sizeof( dvid );
1235 memcpy(&ddid, ibuf, sizeof( ddid ));
1236 ibuf += sizeof( ddid );
1238 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1239 return get_afp_errno(AFPERR_PARAM);
1241 if ( path_isadir(s_path) ) {
1242 return( AFPERR_BADTYPE );
1245 /* don't allow copies when the file is open.
1246 * XXX: the spec only calls for read/deny write access.
1247 * however, copyfile doesn't have any of that info,
1248 * and locks need to stay coherent. as a result,
1249 * we just balk if the file is opened already. */
1251 adp = of_ad(s_vol, s_path, &ad);
1253 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0) {
1254 return AFPERR_DENYCONF;
1256 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1257 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1260 retvalue = AFPERR_DENYCONF;
1264 newname = obj->newtmp;
1265 strcpy( newname, s_path->m_name );
1267 p = ctoupath( s_vol, curdir, newname );
1269 retvalue = AFPERR_PARAM;
1274 /* FIXME svid != dvid && dvid's user can't read svid */
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()))) {
1310 retvalue =AFPERR_PARAM;
1314 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1321 if (vol->v_flags & AFPVOL_DROPBOX) {
1322 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1324 #endif /* DROPKLUDGE */
1326 setvoltime(obj, d_vol );
1329 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1333 /* ----------------------- */
1334 static int copy_all(const int dfd, const void *buf,
1340 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1343 while (buflen > 0) {
1344 if ((cc = write(dfd, buf, buflen)) < 0) {
1356 LOG(log_debug9, logtype_afpd, "end copy_all:");
1362 /* --------------------------
1363 * copy only the fork data stream
1365 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1372 if (eid == ADEID_DFORK) {
1373 sfd = ad_data_fileno(ads);
1374 dfd = ad_data_fileno(add);
1377 sfd = ad_reso_fileno(ads);
1378 dfd = ad_reso_fileno(add);
1381 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1384 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1387 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1388 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1392 #define BUF 128*1024*1024
1394 if (fstat(sfd, &st) == 0) {
1397 if ( offset >= st.st_size) {
1400 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1401 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1404 case EINVAL: /* there's no guarantee that all fs support sendfile */
1413 lseek(sfd, offset, SEEK_SET);
1417 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1424 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1431 /* ----------------------------------
1432 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1433 * because we are doing it elsewhere.
1434 * currently if newname is NULL then adp is NULL.
1436 int copyfile(const struct vol *s_vol,
1437 const struct vol *d_vol,
1442 struct adouble *adp)
1444 struct adouble ads, add;
1451 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1452 sfd, src, dst, newname);
1455 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1459 adflags = ADFLAGS_DF;
1461 adflags |= ADFLAGS_HF;
1464 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0) {
1469 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1470 /* no resource fork, don't create one for dst file */
1471 adflags &= ~ADFLAGS_HF;
1474 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1476 if (stat_result < 0) {
1477 /* unlikely but if fstat fails, the default file mode will be 0666. */
1478 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1481 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1482 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) {
1484 ad_close( adp, adflags );
1485 if (EEXIST != ret_err) {
1486 deletefile(d_vol, -1, dst, 0);
1489 return AFPERR_EXIST;
1493 * XXX if the source and the dest don't use the same resource type it's broken
1495 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1496 /* copy the data fork */
1497 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1498 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1506 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1507 /* set the new name in the resource fork */
1508 ad_copy_header(&add, adp);
1509 ad_setname(&add, newname);
1512 ad_close( adp, adflags );
1514 if (ad_close( &add, adflags ) <0) {
1519 deletefile(d_vol, -1, dst, 0);
1521 else if (stat_result == 0) {
1522 /* set dest modification date to src date */
1525 ut.actime = ut.modtime = st.st_mtime;
1527 /* FIXME netatalk doesn't use resource fork file date
1528 * but maybe we should set its modtime too.
1533 switch ( ret_err ) {
1539 return AFPERR_DFULL;
1541 return AFPERR_NOOBJ;
1543 return AFPERR_ACCESS;
1545 return AFPERR_VLOCK;
1547 return AFPERR_PARAM;
1551 /* -----------------------------------
1552 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1553 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1555 when deletefile is called we don't have lock on it, file is closed (for us)
1556 untrue if called by renamefile
1558 ad_open always try to open file RDWR first and ad_lock takes care of
1559 WRITE lock on read only file.
1562 static int check_attrib(struct adouble *adp)
1564 u_int16_t bshort = 0;
1566 ad_getattr(adp, &bshort);
1568 * Does kFPDeleteInhibitBit (bit 8) set?
1570 if ((bshort & htons(ATTRBIT_NODELETE))) {
1571 return AFPERR_OLOCK;
1573 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1579 * dirfd can be used for unlinkat semantics
1581 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1584 struct adouble *adp = NULL;
1585 int adflags, err = AFP_OK;
1588 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1590 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1592 /* was EACCESS error try to get only metadata */
1593 /* we never want to create a resource fork here, we are going to delete it
1594 * moreover sometimes deletefile is called with a no existent file and
1595 * ad_open would create a 0 byte resource fork
1597 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1598 if ((err = check_attrib(&ad))) {
1599 ad_close_metadata(&ad);
1606 /* try to open both forks at once */
1607 adflags = ADFLAGS_DF;
1608 if ( ad_openat(&ad, dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0 ) {
1613 case EACCES: /* maybe it's a file with no write mode for us */
1614 break; /* was return AFPERR_ACCESS;*/
1627 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1628 adflags |= ADFLAGS_HF;
1629 /* FIXME we have a pb here because we want to know if a file is open
1630 * there's a 'priority inversion' if you can't open the ressource fork RW
1631 * you can delete it if it's open because you can't get a write lock.
1633 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1636 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1638 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1644 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1646 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1648 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1649 cnid_delete(vol->v_cdb, id);
1655 ad_close_metadata(&ad);
1658 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1663 /* ------------------------------------ */
1664 /* return a file id */
1665 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1674 struct path *s_path;
1680 memcpy(&vid, ibuf, sizeof(vid));
1681 ibuf += sizeof(vid);
1683 if (NULL == ( vol = getvolbyvid( vid )) ) {
1684 return( AFPERR_PARAM);
1687 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1691 if (vol->v_flags & AFPVOL_RO)
1692 return AFPERR_VLOCK;
1694 memcpy(&did, ibuf, sizeof( did ));
1695 ibuf += sizeof(did);
1697 if (NULL == ( dir = dirlookup( vol, did )) ) {
1698 return afp_errno; /* was AFPERR_PARAM */
1701 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1702 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1705 if ( path_isadir(s_path) ) {
1706 return( AFPERR_BADTYPE );
1709 upath = s_path->u_name;
1710 switch (s_path->st_errno) {
1712 break; /* success */
1715 return AFPERR_ACCESS;
1717 return AFPERR_NOOBJ;
1719 return AFPERR_PARAM;
1722 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1723 memcpy(rbuf, &id, sizeof(id));
1724 *rbuflen = sizeof(id);
1725 return AFPERR_EXISTID;
1728 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1729 memcpy(rbuf, &id, sizeof(id));
1730 *rbuflen = sizeof(id);
1737 /* ------------------------------- */
1743 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1746 struct reenum *param = data;
1747 struct vol *vol = param->vol;
1748 cnid_t did = param->did;
1751 if ( lstat(de->d_name, &path.st) < 0 )
1754 /* update or add to cnid */
1755 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1760 /* --------------------
1761 * Ok the db is out of synch with the dir.
1762 * but if it's a deleted file we don't want to do it again and again.
1765 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1771 if (vol->v_cdb == NULL) {
1775 /* FIXME use of_statdir ? */
1776 if (lstat(name, &st)) {
1780 if (dirreenumerate(dir, &st)) {
1781 /* we already did it once and the dir haven't been modified */
1786 data.did = dir->d_did;
1787 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1788 setdiroffcnt(curdir, &st, ret);
1789 dir->d_flags |= DIRF_CNID;
1795 /* ------------------------------
1796 resolve a file id */
1797 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1806 u_int16_t vid, bitmap;
1808 static char buffer[12 + MAXPATHLEN + 1];
1809 int len = 12 + MAXPATHLEN + 1;
1814 memcpy(&vid, ibuf, sizeof(vid));
1815 ibuf += sizeof(vid);
1817 if (NULL == ( vol = getvolbyvid( vid )) ) {
1818 return( AFPERR_PARAM);
1821 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1825 memcpy(&id, ibuf, sizeof( id ));
1830 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1834 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1835 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1838 if (NULL == ( dir = dirlookup( vol, id )) ) {
1839 return AFPERR_NOID; /* idem AFPERR_PARAM */
1841 if (movecwd(vol, dir) < 0) {
1845 return AFPERR_ACCESS;
1849 return AFPERR_PARAM;
1853 memset(&path, 0, sizeof(path));
1854 path.u_name = upath;
1855 if ( of_stat(&path) < 0 ) {
1857 /* with nfs and our working directory is deleted */
1858 if (errno == ESTALE) {
1862 if ( errno == ENOENT && !retry) {
1863 /* cnid db is out of sync, reenumerate the directory and update ids */
1864 reenumerate_id(vol, ".", dir);
1872 return AFPERR_ACCESS;
1876 return AFPERR_PARAM;
1880 /* directories are bad */
1881 if (S_ISDIR(path.st.st_mode)) {
1882 /* OS9 and OSX don't return the same error code */
1883 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1886 memcpy(&bitmap, ibuf, sizeof(bitmap));
1887 bitmap = ntohs( bitmap );
1888 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1892 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1893 rbuf + sizeof(bitmap), &buflen))) {
1896 *rbuflen = buflen + sizeof(bitmap);
1897 memcpy(rbuf, ibuf, sizeof(bitmap));
1902 /* ------------------------------ */
1903 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1913 static char buffer[12 + MAXPATHLEN + 1];
1914 int len = 12 + MAXPATHLEN + 1;
1919 memcpy(&vid, ibuf, sizeof(vid));
1920 ibuf += sizeof(vid);
1922 if (NULL == ( vol = getvolbyvid( vid )) ) {
1923 return( AFPERR_PARAM);
1926 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1930 if (vol->v_flags & AFPVOL_RO)
1931 return AFPERR_VLOCK;
1933 memcpy(&id, ibuf, sizeof( id ));
1937 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1941 if (NULL == ( dir = dirlookup( vol, id )) ) {
1942 if (afp_errno == AFPERR_NOOBJ) {
1946 return( AFPERR_PARAM );
1950 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1954 return AFPERR_ACCESS;
1959 /* still try to delete the id */
1963 return AFPERR_PARAM;
1966 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1967 return AFPERR_BADTYPE;
1970 if (cnid_delete(vol->v_cdb, fileid)) {
1973 return AFPERR_VLOCK;
1976 return AFPERR_ACCESS;
1978 return AFPERR_PARAM;
1985 /* ------------------------------ */
1986 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
1990 if (path->st_errno) {
1991 switch (path->st_errno) {
1993 afp_errno = AFPERR_NOID;
1997 afp_errno = AFPERR_ACCESS;
2000 afp_errno = AFPERR_PARAM;
2005 /* we use file_access both for legacy Mac perm and
2006 * for unix privilege, rename will take care of folder perms
2008 if (file_access(path, OPENACC_WR ) < 0) {
2009 afp_errno = AFPERR_ACCESS;
2013 if ((*of = of_findname(path))) {
2014 /* reuse struct adouble so it won't break locks */
2018 ret = ad_open(adp, path->u_name, ADFLAGS_HF, O_RDONLY);
2020 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2022 * The user must have the Read & Write privilege for both files in order to use this command.
2024 ad_close(adp, ADFLAGS_HF);
2025 afp_errno = AFPERR_ACCESS;
2032 #define APPLETEMP ".AppleTempXXXXXX"
2034 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2036 struct stat srcst, destst;
2038 struct dir *dir, *sdir;
2039 char *spath, temp[17], *p;
2040 char *supath, *upath;
2045 struct adouble *adsp = NULL;
2046 struct adouble *addp = NULL;
2047 struct ofork *s_of = NULL;
2048 struct ofork *d_of = NULL;
2061 memcpy(&vid, ibuf, sizeof(vid));
2062 ibuf += sizeof(vid);
2064 if (NULL == ( vol = getvolbyvid( vid )) ) {
2065 return( AFPERR_PARAM);
2068 if ((vol->v_flags & AFPVOL_RO))
2069 return AFPERR_VLOCK;
2071 /* source and destination dids */
2072 memcpy(&sid, ibuf, sizeof(sid));
2073 ibuf += sizeof(sid);
2074 memcpy(&did, ibuf, sizeof(did));
2075 ibuf += sizeof(did);
2078 if (NULL == (dir = dirlookup( vol, sid )) ) {
2079 return afp_errno; /* was AFPERR_PARAM */
2082 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2083 return get_afp_errno(AFPERR_NOOBJ);
2086 if ( path_isadir(path) ) {
2087 return AFPERR_BADTYPE; /* it's a dir */
2090 /* save some stuff */
2093 spath = obj->oldtmp;
2094 supath = obj->newtmp;
2095 strcpy(spath, path->m_name);
2096 strcpy(supath, path->u_name); /* this is for the cnid changing */
2097 p = absupath( vol, sdir, supath);
2099 /* pathname too long */
2100 return AFPERR_PARAM ;
2103 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2104 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2108 /* ***** from here we may have resource fork open **** */
2110 /* look for the source cnid. if it doesn't exist, don't worry about
2112 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2114 if (NULL == ( dir = dirlookup( vol, did )) ) {
2115 err = afp_errno; /* was AFPERR_PARAM */
2116 goto err_exchangefile;
2119 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2120 err = get_afp_errno(AFPERR_NOOBJ);
2121 goto err_exchangefile;
2124 if ( path_isadir(path) ) {
2125 err = AFPERR_BADTYPE;
2126 goto err_exchangefile;
2129 /* FPExchangeFiles is the only call that can return the SameObj
2131 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2132 err = AFPERR_SAMEOBJ;
2133 goto err_exchangefile;
2136 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2137 if (!(addp = find_adouble( path, &d_of, &add))) {
2139 goto err_exchangefile;
2143 /* they are not on the same device and at least one is open
2144 * FIXME broken for for crossdev and adouble v2
2147 crossdev = (srcst.st_dev != destst.st_dev);
2148 if (/* (d_of || s_of) && */ crossdev) {
2150 goto err_exchangefile;
2153 /* look for destination id. */
2154 upath = path->u_name;
2155 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2157 /* construct a temp name.
2158 * NOTE: the temp file will be in the dest file's directory. it
2159 * will also be inaccessible from AFP. */
2160 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2161 if (!mktemp(temp)) {
2163 goto err_exchangefile;
2167 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2168 ad_close(adsp, ADFLAGS_HF);
2169 ad_close(addp, ADFLAGS_HF);
2172 /* now, quickly rename the file. we error if we can't. */
2173 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2174 goto err_exchangefile;
2175 of_rename(vol, s_of, sdir, spath, curdir, temp);
2177 /* rename destination to source */
2178 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2179 goto err_src_to_tmp;
2180 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2182 /* rename temp to destination */
2183 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2184 goto err_dest_to_src;
2185 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2187 /* id's need switching. src -> dest and dest -> src.
2188 * we need to re-stat() if it was a cross device copy.
2191 cnid_delete(vol->v_cdb, sid);
2193 cnid_delete(vol->v_cdb, did);
2195 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2196 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2198 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2199 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2204 err = AFPERR_ACCESS;
2209 goto err_temp_to_dest;
2212 /* here we need to reopen if crossdev */
2213 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2218 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2223 /* change perms, src gets dest perm and vice versa */
2228 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2229 err = AFP_OK; /* ignore error */
2230 goto err_temp_to_dest;
2234 * we need to exchange ACL entries as well
2236 /* exchange_acls(vol, p, upath); */
2241 path->m_name = NULL;
2242 path->u_name = upath;
2244 setfilunixmode(vol, path, destst.st_mode);
2245 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2252 setfilunixmode(vol, path, srcst.st_mode);
2253 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2255 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2256 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2261 goto err_exchangefile;
2263 /* all this stuff is so that we can unwind a failed operation
2266 /* rename dest to temp */
2267 renamefile(vol, -1, upath, temp, temp, adsp);
2268 of_rename(vol, s_of, curdir, upath, curdir, temp);
2271 /* rename source back to dest */
2272 renamefile(vol, -1, p, upath, path->m_name, addp);
2273 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2276 /* rename temp back to source */
2277 renamefile(vol, -1, temp, p, spath, adsp);
2278 of_rename(vol, s_of, curdir, temp, sdir, spath);
2281 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2282 ad_close(adsp, ADFLAGS_HF);
2284 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2285 ad_close(addp, ADFLAGS_HF);
2289 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2290 (void)dir_remove(vol, cached);
2291 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2292 (void)dir_remove(vol, cached);