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
1126 if (ad_open(adp, dst, ADFLAGS_HF, O_RDWR) == 0) {
1127 ad_setname(adp, newname);
1129 ad_close( adp, ADFLAGS_HF );
1136 convert a Mac long name to an utf8 name,
1138 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1142 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1148 /* ---------------- */
1149 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1156 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1162 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1163 if (afp_version >= 30) {
1164 /* convert it to UTF8
1166 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1170 strncpy( newname, ibuf, plen );
1171 newname[ plen ] = '\0';
1173 if (strlen(newname) != plen) {
1174 /* there's \0 in newname, e.g. it's a pathname not
1182 memcpy(&hint, ibuf, sizeof(hint));
1183 ibuf += sizeof(hint);
1185 memcpy(&len16, ibuf, sizeof(len16));
1186 ibuf += sizeof(len16);
1187 plen = ntohs(len16);
1190 if (plen > AFPOBJ_TMPSIZ) {
1193 strncpy( newname, ibuf, plen );
1194 newname[ plen ] = '\0';
1195 if (strlen(newname) != plen) {
1204 /* -----------------------------------
1206 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1208 struct vol *s_vol, *d_vol;
1210 char *newname, *p, *upath;
1211 struct path *s_path;
1212 u_int32_t sdid, ddid;
1213 int err, retvalue = AFP_OK;
1214 u_int16_t svid, dvid;
1216 struct adouble ad, *adp;
1222 memcpy(&svid, ibuf, sizeof( svid ));
1223 ibuf += sizeof( svid );
1224 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1225 return( AFPERR_PARAM );
1228 memcpy(&sdid, ibuf, sizeof( sdid ));
1229 ibuf += sizeof( sdid );
1230 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1234 memcpy(&dvid, ibuf, sizeof( dvid ));
1235 ibuf += sizeof( dvid );
1236 memcpy(&ddid, ibuf, sizeof( ddid ));
1237 ibuf += sizeof( ddid );
1239 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1240 return get_afp_errno(AFPERR_PARAM);
1242 if ( path_isadir(s_path) ) {
1243 return( AFPERR_BADTYPE );
1246 /* don't allow copies when the file is open.
1247 * XXX: the spec only calls for read/deny write access.
1248 * however, copyfile doesn't have any of that info,
1249 * and locks need to stay coherent. as a result,
1250 * we just balk if the file is opened already. */
1252 adp = of_ad(s_vol, s_path, &ad);
1254 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0) {
1255 return AFPERR_DENYCONF;
1257 denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1258 getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1261 retvalue = AFPERR_DENYCONF;
1265 newname = obj->newtmp;
1266 strcpy( newname, s_path->m_name );
1268 p = ctoupath( s_vol, curdir, newname );
1270 retvalue = AFPERR_PARAM;
1275 /* FIXME svid != dvid && dvid's user can't read svid */
1277 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1278 retvalue = AFPERR_PARAM;
1282 if (d_vol->v_flags & AFPVOL_RO) {
1283 retvalue = AFPERR_VLOCK;
1287 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1288 retvalue = afp_errno;
1292 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1293 retvalue = get_afp_errno(AFPERR_NOOBJ);
1297 if ( *s_path->m_name != '\0' ) {
1298 retvalue =path_error(s_path, AFPERR_NOOBJ);
1302 /* one of the handful of places that knows about the path type */
1303 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1304 retvalue = AFPERR_PARAM;
1307 /* newname is always only a filename so curdir *is* its
1310 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1311 retvalue =AFPERR_PARAM;
1315 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1322 if (vol->v_flags & AFPVOL_DROPBOX) {
1323 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1325 #endif /* DROPKLUDGE */
1327 setvoltime(obj, d_vol );
1330 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1334 /* ----------------------- */
1335 static int copy_all(const int dfd, const void *buf,
1341 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1344 while (buflen > 0) {
1345 if ((cc = write(dfd, buf, buflen)) < 0) {
1357 LOG(log_debug9, logtype_afpd, "end copy_all:");
1363 /* --------------------------
1364 * copy only the fork data stream
1366 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1373 if (eid == ADEID_DFORK) {
1374 sfd = ad_data_fileno(ads);
1375 dfd = ad_data_fileno(add);
1378 sfd = ad_reso_fileno(ads);
1379 dfd = ad_reso_fileno(add);
1382 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1385 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1388 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1389 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1393 #define BUF 128*1024*1024
1395 if (fstat(sfd, &st) == 0) {
1398 if ( offset >= st.st_size) {
1401 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1402 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1405 case EINVAL: /* there's no guarantee that all fs support sendfile */
1414 lseek(sfd, offset, SEEK_SET);
1418 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1425 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1432 /* ----------------------------------
1433 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1434 * because we are doing it elsewhere.
1435 * currently if newname is NULL then adp is NULL.
1437 int copyfile(const struct vol *s_vol,
1438 const struct vol *d_vol,
1443 struct adouble *adp)
1445 struct adouble ads, add;
1452 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1453 sfd, src, dst, newname);
1456 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1460 adflags = ADFLAGS_DF;
1462 adflags |= ADFLAGS_HF;
1465 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0) {
1470 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1471 /* no resource fork, don't create one for dst file */
1472 adflags &= ~ADFLAGS_HF;
1475 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1477 if (stat_result < 0) {
1478 /* unlikely but if fstat fails, the default file mode will be 0666. */
1479 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1482 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1483 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) {
1485 ad_close( adp, adflags );
1486 if (EEXIST != ret_err) {
1487 deletefile(d_vol, -1, dst, 0);
1490 return AFPERR_EXIST;
1494 * XXX if the source and the dest don't use the same resource type it's broken
1496 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1497 /* copy the data fork */
1498 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1499 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1507 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1508 /* set the new name in the resource fork */
1509 ad_copy_header(&add, adp);
1510 ad_setname(&add, newname);
1513 ad_close( adp, adflags );
1515 if (ad_close( &add, adflags ) <0) {
1520 deletefile(d_vol, -1, dst, 0);
1522 else if (stat_result == 0) {
1523 /* set dest modification date to src date */
1526 ut.actime = ut.modtime = st.st_mtime;
1528 /* FIXME netatalk doesn't use resource fork file date
1529 * but maybe we should set its modtime too.
1534 switch ( ret_err ) {
1540 return AFPERR_DFULL;
1542 return AFPERR_NOOBJ;
1544 return AFPERR_ACCESS;
1546 return AFPERR_VLOCK;
1548 return AFPERR_PARAM;
1552 /* -----------------------------------
1553 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1554 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1556 when deletefile is called we don't have lock on it, file is closed (for us)
1557 untrue if called by renamefile
1559 ad_open always try to open file RDWR first and ad_lock takes care of
1560 WRITE lock on read only file.
1563 static int check_attrib(struct adouble *adp)
1565 u_int16_t bshort = 0;
1567 ad_getattr(adp, &bshort);
1569 * Does kFPDeleteInhibitBit (bit 8) set?
1571 if ((bshort & htons(ATTRBIT_NODELETE))) {
1572 return AFPERR_OLOCK;
1574 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1580 * dirfd can be used for unlinkat semantics
1582 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1585 struct adouble *adp = NULL;
1586 int adflags, err = AFP_OK;
1589 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1591 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1593 /* was EACCESS error try to get only metadata */
1594 /* we never want to create a resource fork here, we are going to delete it
1595 * moreover sometimes deletefile is called with a no existent file and
1596 * ad_open would create a 0 byte resource fork
1598 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1599 if ((err = check_attrib(&ad))) {
1600 ad_close_metadata(&ad);
1607 /* try to open both forks at once */
1608 adflags = ADFLAGS_DF;
1609 if ( ad_openat(&ad, dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0 ) {
1614 case EACCES: /* maybe it's a file with no write mode for us */
1615 break; /* was return AFPERR_ACCESS;*/
1628 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1629 adflags |= ADFLAGS_HF;
1630 /* FIXME we have a pb here because we want to know if a file is open
1631 * there's a 'priority inversion' if you can't open the ressource fork RW
1632 * you can delete it if it's open because you can't get a write lock.
1634 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1637 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1639 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1645 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1647 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1649 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1650 cnid_delete(vol->v_cdb, id);
1656 ad_close_metadata(&ad);
1659 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1664 /* ------------------------------------ */
1665 /* return a file id */
1666 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1675 struct path *s_path;
1681 memcpy(&vid, ibuf, sizeof(vid));
1682 ibuf += sizeof(vid);
1684 if (NULL == ( vol = getvolbyvid( vid )) ) {
1685 return( AFPERR_PARAM);
1688 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1692 if (vol->v_flags & AFPVOL_RO)
1693 return AFPERR_VLOCK;
1695 memcpy(&did, ibuf, sizeof( did ));
1696 ibuf += sizeof(did);
1698 if (NULL == ( dir = dirlookup( vol, did )) ) {
1699 return afp_errno; /* was AFPERR_PARAM */
1702 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1703 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1706 if ( path_isadir(s_path) ) {
1707 return( AFPERR_BADTYPE );
1710 upath = s_path->u_name;
1711 switch (s_path->st_errno) {
1713 break; /* success */
1716 return AFPERR_ACCESS;
1718 return AFPERR_NOOBJ;
1720 return AFPERR_PARAM;
1723 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1724 memcpy(rbuf, &id, sizeof(id));
1725 *rbuflen = sizeof(id);
1726 return AFPERR_EXISTID;
1729 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1730 memcpy(rbuf, &id, sizeof(id));
1731 *rbuflen = sizeof(id);
1738 /* ------------------------------- */
1744 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1747 struct reenum *param = data;
1748 struct vol *vol = param->vol;
1749 cnid_t did = param->did;
1752 if ( lstat(de->d_name, &path.st) < 0 )
1755 /* update or add to cnid */
1756 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1761 /* --------------------
1762 * Ok the db is out of synch with the dir.
1763 * but if it's a deleted file we don't want to do it again and again.
1766 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1772 if (vol->v_cdb == NULL) {
1776 /* FIXME use of_statdir ? */
1777 if (lstat(name, &st)) {
1781 if (dirreenumerate(dir, &st)) {
1782 /* we already did it once and the dir haven't been modified */
1787 data.did = dir->d_did;
1788 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1789 setdiroffcnt(curdir, &st, ret);
1790 dir->d_flags |= DIRF_CNID;
1796 /* ------------------------------
1797 resolve a file id */
1798 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1807 u_int16_t vid, bitmap;
1809 static char buffer[12 + MAXPATHLEN + 1];
1810 int len = 12 + MAXPATHLEN + 1;
1815 memcpy(&vid, ibuf, sizeof(vid));
1816 ibuf += sizeof(vid);
1818 if (NULL == ( vol = getvolbyvid( vid )) ) {
1819 return( AFPERR_PARAM);
1822 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1826 memcpy(&id, ibuf, sizeof( id ));
1831 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1835 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1836 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1839 if (NULL == ( dir = dirlookup( vol, id )) ) {
1840 return AFPERR_NOID; /* idem AFPERR_PARAM */
1842 if (movecwd(vol, dir) < 0) {
1846 return AFPERR_ACCESS;
1850 return AFPERR_PARAM;
1854 memset(&path, 0, sizeof(path));
1855 path.u_name = upath;
1856 if ( of_stat(&path) < 0 ) {
1858 /* with nfs and our working directory is deleted */
1859 if (errno == ESTALE) {
1863 if ( errno == ENOENT && !retry) {
1864 /* cnid db is out of sync, reenumerate the directory and update ids */
1865 reenumerate_id(vol, ".", dir);
1873 return AFPERR_ACCESS;
1877 return AFPERR_PARAM;
1881 /* directories are bad */
1882 if (S_ISDIR(path.st.st_mode)) {
1883 /* OS9 and OSX don't return the same error code */
1884 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1887 memcpy(&bitmap, ibuf, sizeof(bitmap));
1888 bitmap = ntohs( bitmap );
1889 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1893 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1894 rbuf + sizeof(bitmap), &buflen))) {
1897 *rbuflen = buflen + sizeof(bitmap);
1898 memcpy(rbuf, ibuf, sizeof(bitmap));
1903 /* ------------------------------ */
1904 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1914 static char buffer[12 + MAXPATHLEN + 1];
1915 int len = 12 + MAXPATHLEN + 1;
1920 memcpy(&vid, ibuf, sizeof(vid));
1921 ibuf += sizeof(vid);
1923 if (NULL == ( vol = getvolbyvid( vid )) ) {
1924 return( AFPERR_PARAM);
1927 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1931 if (vol->v_flags & AFPVOL_RO)
1932 return AFPERR_VLOCK;
1934 memcpy(&id, ibuf, sizeof( id ));
1938 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1942 if (NULL == ( dir = dirlookup( vol, id )) ) {
1943 if (afp_errno == AFPERR_NOOBJ) {
1947 return( AFPERR_PARAM );
1951 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1955 return AFPERR_ACCESS;
1960 /* still try to delete the id */
1964 return AFPERR_PARAM;
1967 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1968 return AFPERR_BADTYPE;
1971 if (cnid_delete(vol->v_cdb, fileid)) {
1974 return AFPERR_VLOCK;
1977 return AFPERR_ACCESS;
1979 return AFPERR_PARAM;
1986 /* ------------------------------ */
1987 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
1991 if (path->st_errno) {
1992 switch (path->st_errno) {
1994 afp_errno = AFPERR_NOID;
1998 afp_errno = AFPERR_ACCESS;
2001 afp_errno = AFPERR_PARAM;
2006 /* we use file_access both for legacy Mac perm and
2007 * for unix privilege, rename will take care of folder perms
2009 if (file_access(path, OPENACC_WR ) < 0) {
2010 afp_errno = AFPERR_ACCESS;
2014 if ((*of = of_findname(path))) {
2015 /* reuse struct adouble so it won't break locks */
2019 ret = ad_open(adp, path->u_name, ADFLAGS_HF, O_RDONLY);
2021 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2023 * The user must have the Read & Write privilege for both files in order to use this command.
2025 ad_close(adp, ADFLAGS_HF);
2026 afp_errno = AFPERR_ACCESS;
2033 #define APPLETEMP ".AppleTempXXXXXX"
2035 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2037 struct stat srcst, destst;
2039 struct dir *dir, *sdir;
2040 char *spath, temp[17], *p;
2041 char *supath, *upath;
2046 struct adouble *adsp = NULL;
2047 struct adouble *addp = NULL;
2048 struct ofork *s_of = NULL;
2049 struct ofork *d_of = NULL;
2062 memcpy(&vid, ibuf, sizeof(vid));
2063 ibuf += sizeof(vid);
2065 if (NULL == ( vol = getvolbyvid( vid )) ) {
2066 return( AFPERR_PARAM);
2069 if ((vol->v_flags & AFPVOL_RO))
2070 return AFPERR_VLOCK;
2072 /* source and destination dids */
2073 memcpy(&sid, ibuf, sizeof(sid));
2074 ibuf += sizeof(sid);
2075 memcpy(&did, ibuf, sizeof(did));
2076 ibuf += sizeof(did);
2079 if (NULL == (dir = dirlookup( vol, sid )) ) {
2080 return afp_errno; /* was AFPERR_PARAM */
2083 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2084 return get_afp_errno(AFPERR_NOOBJ);
2087 if ( path_isadir(path) ) {
2088 return AFPERR_BADTYPE; /* it's a dir */
2091 /* save some stuff */
2094 spath = obj->oldtmp;
2095 supath = obj->newtmp;
2096 strcpy(spath, path->m_name);
2097 strcpy(supath, path->u_name); /* this is for the cnid changing */
2098 p = absupath( vol, sdir, supath);
2100 /* pathname too long */
2101 return AFPERR_PARAM ;
2104 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2105 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2109 /* ***** from here we may have resource fork open **** */
2111 /* look for the source cnid. if it doesn't exist, don't worry about
2113 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2115 if (NULL == ( dir = dirlookup( vol, did )) ) {
2116 err = afp_errno; /* was AFPERR_PARAM */
2117 goto err_exchangefile;
2120 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2121 err = get_afp_errno(AFPERR_NOOBJ);
2122 goto err_exchangefile;
2125 if ( path_isadir(path) ) {
2126 err = AFPERR_BADTYPE;
2127 goto err_exchangefile;
2130 /* FPExchangeFiles is the only call that can return the SameObj
2132 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2133 err = AFPERR_SAMEOBJ;
2134 goto err_exchangefile;
2137 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2138 if (!(addp = find_adouble( path, &d_of, &add))) {
2140 goto err_exchangefile;
2144 /* they are not on the same device and at least one is open
2145 * FIXME broken for for crossdev and adouble v2
2148 crossdev = (srcst.st_dev != destst.st_dev);
2149 if (/* (d_of || s_of) && */ crossdev) {
2151 goto err_exchangefile;
2154 /* look for destination id. */
2155 upath = path->u_name;
2156 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2158 /* construct a temp name.
2159 * NOTE: the temp file will be in the dest file's directory. it
2160 * will also be inaccessible from AFP. */
2161 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2162 if (!mktemp(temp)) {
2164 goto err_exchangefile;
2168 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2169 ad_close(adsp, ADFLAGS_HF);
2170 ad_close(addp, ADFLAGS_HF);
2173 /* now, quickly rename the file. we error if we can't. */
2174 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2175 goto err_exchangefile;
2176 of_rename(vol, s_of, sdir, spath, curdir, temp);
2178 /* rename destination to source */
2179 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2180 goto err_src_to_tmp;
2181 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2183 /* rename temp to destination */
2184 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2185 goto err_dest_to_src;
2186 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2188 /* id's need switching. src -> dest and dest -> src.
2189 * we need to re-stat() if it was a cross device copy.
2192 cnid_delete(vol->v_cdb, sid);
2194 cnid_delete(vol->v_cdb, did);
2196 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2197 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2199 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2200 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2205 err = AFPERR_ACCESS;
2210 goto err_temp_to_dest;
2213 /* here we need to reopen if crossdev */
2214 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2219 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2224 /* change perms, src gets dest perm and vice versa */
2229 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2230 err = AFP_OK; /* ignore error */
2231 goto err_temp_to_dest;
2235 * we need to exchange ACL entries as well
2237 /* exchange_acls(vol, p, upath); */
2242 path->m_name = NULL;
2243 path->u_name = upath;
2245 setfilunixmode(vol, path, destst.st_mode);
2246 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2253 setfilunixmode(vol, path, srcst.st_mode);
2254 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2256 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2257 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2262 goto err_exchangefile;
2264 /* all this stuff is so that we can unwind a failed operation
2267 /* rename dest to temp */
2268 renamefile(vol, -1, upath, temp, temp, adsp);
2269 of_rename(vol, s_of, curdir, upath, curdir, temp);
2272 /* rename source back to dest */
2273 renamefile(vol, -1, p, upath, path->m_name, addp);
2274 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2277 /* rename temp back to source */
2278 renamefile(vol, -1, temp, p, spath, adsp);
2279 of_rename(vol, s_of, curdir, temp, sdir, spath);
2282 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2283 ad_close(adsp, ADFLAGS_HF);
2285 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2286 ad_close(addp, ADFLAGS_HF);
2290 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2291 (void)dir_remove(vol, cached);
2292 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2293 (void)dir_remove(vol, cached);