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 );
728 setvoltime(obj, vol );
733 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
739 u_int16_t vid, bitmap;
744 memcpy(&vid, ibuf, sizeof( vid ));
745 ibuf += sizeof( vid );
746 if (NULL == ( vol = getvolbyvid( vid )) ) {
747 return( AFPERR_PARAM );
750 if (vol->v_flags & AFPVOL_RO)
753 memcpy(&did, ibuf, sizeof( did ));
754 ibuf += sizeof( did );
755 if (NULL == ( dir = dirlookup( vol, did )) ) {
756 return afp_errno; /* was AFPERR_NOOBJ */
759 memcpy(&bitmap, ibuf, sizeof( bitmap ));
760 bitmap = ntohs( bitmap );
761 ibuf += sizeof( bitmap );
763 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
764 return get_afp_errno(AFPERR_PARAM);
767 if (path_isadir(s_path)) {
768 return( AFPERR_BADTYPE ); /* it's a directory */
771 if ( s_path->st_errno != 0 ) {
772 return( AFPERR_NOOBJ );
775 if ((u_long)ibuf & 1 ) {
779 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
780 setvoltime(obj, vol );
787 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
790 extern struct path Cur_Path;
792 int setfilparams(struct vol *vol,
793 struct path *path, u_int16_t f_bitmap, char *buf )
795 struct adouble ad, *adp;
797 int bit, isad = 1, err = AFP_OK;
799 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
800 u_int16_t ashort, bshort, oshort;
803 u_int16_t upriv_bit = 0;
807 int change_mdate = 0;
808 int change_parent_mdate = 0;
813 u_int16_t bitmap = f_bitmap;
814 u_int32_t cdate,bdate;
815 u_char finder_buf[32];
818 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
821 adp = of_ad(vol, path, &ad);
822 upath = path->u_name;
824 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
825 return AFPERR_ACCESS;
828 /* with unix priv maybe we have to change adouble file priv first */
830 while ( bitmap != 0 ) {
831 while (( bitmap & 1 ) == 0 ) {
838 memcpy(&ashort, buf, sizeof( ashort ));
839 buf += sizeof( ashort );
843 memcpy(&cdate, buf, sizeof(cdate));
844 buf += sizeof( cdate );
847 memcpy(&newdate, buf, sizeof( newdate ));
848 buf += sizeof( newdate );
852 memcpy(&bdate, buf, sizeof( bdate));
853 buf += sizeof( bdate );
857 memcpy(finder_buf, buf, 32 );
858 if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
863 char buf[PATH_MAX+1];
864 if ((fp=open(path->u_name,O_RDONLY))>=0){
865 if ((len=read(fp,buf,PATH_MAX+1))){
866 if (unlink(path->u_name)==0){
868 erc = symlink(buf, path->u_name);
877 goto setfilparam_done;
882 case FILPBIT_UNIXPR :
883 if (!vol_unix_priv(vol)) {
884 /* this volume doesn't use unix priv */
890 change_parent_mdate = 1;
892 memcpy( &aint, buf, sizeof( aint ));
893 f_uid = ntohl (aint);
894 buf += sizeof( aint );
895 memcpy( &aint, buf, sizeof( aint ));
896 f_gid = ntohl (aint);
897 buf += sizeof( aint );
898 setfilowner(vol, f_uid, f_gid, path);
900 memcpy( &upriv, buf, sizeof( upriv ));
901 buf += sizeof( upriv );
902 upriv = ntohl (upriv);
903 if ((upriv & S_IWUSR)) {
904 setfilunixmode(vol, path, upriv);
911 case FILPBIT_PDINFO :
912 if (afp_version < 30) { /* else it's UTF8 name */
915 /* Keep special case to support crlf translations */
916 if ((unsigned int) achar == 0x04) {
917 fdType = (u_char *)"TEXT";
920 xyy[0] = ( u_char ) 'p';
931 /* break while loop */
940 /* second try with adouble open
942 if ( ad_open(adp, upath, ADFLAGS_HF, O_RDWR | O_CREAT, 0666) < 0) {
943 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
945 * For some things, we don't need an adouble header:
946 * - change of modification date
947 * - UNIX privs (Bug-ID #2863424)
949 if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
950 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
951 return AFPERR_ACCESS;
953 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
955 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
956 ad_setname(adp, path->m_name);
961 while ( bitmap != 0 ) {
962 while (( bitmap & 1 ) == 0 ) {
969 ad_getattr(adp, &bshort);
971 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
972 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
976 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
977 change_parent_mdate = 1;
978 ad_setattr(adp, bshort);
981 ad_setdate(adp, AD_DATE_CREATE, cdate);
986 ad_setdate(adp, AD_DATE_BACKUP, bdate);
989 if (default_type( ad_entry( adp, ADEID_FINDERI ))
991 ((em = getextmap( path->m_name )) &&
992 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
993 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
994 || ((em = getdefextmap()) &&
995 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
996 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
998 memcpy(finder_buf, ufinderi, 8 );
1000 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1002 case FILPBIT_UNIXPR :
1004 setfilunixmode(vol, path, upriv);
1007 case FILPBIT_PDINFO :
1008 if (afp_version < 30) { /* else it's UTF8 name */
1009 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1010 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1015 err = AFPERR_BITMAP;
1016 goto setfilparam_done;
1023 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1024 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1028 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1029 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1035 ad_close_metadata( adp);
1038 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1039 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1040 bitmap = 1<<FILPBIT_MDATE;
1041 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1045 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1051 * renamefile and copyfile take the old and new unix pathnames
1052 * and the new mac name.
1054 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1055 * passing -1 means this is not used, src path is a full path
1056 * src the source path
1057 * dst the dest filename in current dir
1058 * newname the dest mac name
1059 * adp adouble struct of src file, if open, or & zeroed one
1062 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1066 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1069 return( AFPERR_NOOBJ );
1072 return( AFPERR_ACCESS );
1074 return AFPERR_VLOCK;
1075 case EXDEV : /* Cross device move -- try copy */
1076 /* NOTE: with open file it's an error because after the copy we will
1077 * get two files, it's fixable for our process (eg reopen the new file, get the
1078 * locks, and so on. But it doesn't solve the case with a second process
1080 if (adp->ad_open_forks) {
1081 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1082 return AFPERR_OLOCK; /* little lie */
1084 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1085 /* on error copyfile delete dest */
1088 return deletefile(vol, sdir_fd, src, 0);
1090 return( AFPERR_PARAM );
1094 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1098 /* try to undo the data fork rename,
1099 * we know we are on the same device
1102 unix_rename(-1, dst, sdir_fd, src );
1103 /* return the first error */
1106 return AFPERR_NOOBJ;
1109 return AFPERR_ACCESS ;
1111 return AFPERR_VLOCK;
1113 return AFPERR_PARAM ;
1118 /* don't care if we can't open the newly renamed ressource fork */
1119 if (ad_open(adp, dst, ADFLAGS_HF, O_RDWR) == 0) {
1120 ad_setname(adp, newname);
1122 ad_close( adp, ADFLAGS_HF );
1129 convert a Mac long name to an utf8 name,
1131 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1135 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1141 /* ---------------- */
1142 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1149 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1155 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1156 if (afp_version >= 30) {
1157 /* convert it to UTF8
1159 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1163 strncpy( newname, ibuf, plen );
1164 newname[ plen ] = '\0';
1166 if (strlen(newname) != plen) {
1167 /* there's \0 in newname, e.g. it's a pathname not
1175 memcpy(&hint, ibuf, sizeof(hint));
1176 ibuf += sizeof(hint);
1178 memcpy(&len16, ibuf, sizeof(len16));
1179 ibuf += sizeof(len16);
1180 plen = ntohs(len16);
1183 if (plen > AFPOBJ_TMPSIZ) {
1186 strncpy( newname, ibuf, plen );
1187 newname[ plen ] = '\0';
1188 if (strlen(newname) != plen) {
1197 /* -----------------------------------
1199 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1201 struct vol *s_vol, *d_vol;
1203 char *newname, *p, *upath;
1204 struct path *s_path;
1205 u_int32_t sdid, ddid;
1206 int err, retvalue = AFP_OK;
1207 u_int16_t svid, dvid;
1209 struct adouble ad, *adp;
1215 memcpy(&svid, ibuf, sizeof( svid ));
1216 ibuf += sizeof( svid );
1217 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1218 return( AFPERR_PARAM );
1221 memcpy(&sdid, ibuf, sizeof( sdid ));
1222 ibuf += sizeof( sdid );
1223 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1227 memcpy(&dvid, ibuf, sizeof( dvid ));
1228 ibuf += sizeof( dvid );
1229 memcpy(&ddid, ibuf, sizeof( ddid ));
1230 ibuf += sizeof( ddid );
1232 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1233 return get_afp_errno(AFPERR_PARAM);
1235 if ( path_isadir(s_path) ) {
1236 return( AFPERR_BADTYPE );
1239 /* don't allow copies when the file is open.
1240 * XXX: the spec only calls for read/deny write access.
1241 * however, copyfile doesn't have any of that info,
1242 * and locks need to stay coherent. as a result,
1243 * we just balk if the file is opened already. */
1245 adp = of_ad(s_vol, s_path, &ad);
1247 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0) {
1248 return AFPERR_DENYCONF;
1250 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1251 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1254 retvalue = AFPERR_DENYCONF;
1258 newname = obj->newtmp;
1259 strcpy( newname, s_path->m_name );
1261 p = ctoupath( s_vol, curdir, newname );
1263 retvalue = AFPERR_PARAM;
1267 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1268 retvalue = AFPERR_PARAM;
1272 if (d_vol->v_flags & AFPVOL_RO) {
1273 retvalue = AFPERR_VLOCK;
1277 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1278 retvalue = afp_errno;
1282 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1283 retvalue = get_afp_errno(AFPERR_NOOBJ);
1287 if ( *s_path->m_name != '\0' ) {
1288 retvalue =path_error(s_path, AFPERR_NOOBJ);
1292 /* one of the handful of places that knows about the path type */
1293 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1294 retvalue = AFPERR_PARAM;
1297 /* newname is always only a filename so curdir *is* its
1300 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1301 retvalue =AFPERR_PARAM;
1305 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1311 setvoltime(obj, d_vol );
1314 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1318 /* ----------------------- */
1319 static int copy_all(const int dfd, const void *buf,
1325 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1328 while (buflen > 0) {
1329 if ((cc = write(dfd, buf, buflen)) < 0) {
1341 LOG(log_debug9, logtype_afpd, "end copy_all:");
1347 /* --------------------------
1348 * copy only the fork data stream
1350 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1357 if (eid == ADEID_DFORK) {
1358 sfd = ad_data_fileno(ads);
1359 dfd = ad_data_fileno(add);
1362 sfd = ad_reso_fileno(ads);
1363 dfd = ad_reso_fileno(add);
1366 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1369 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1372 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1373 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1377 #define BUF 128*1024*1024
1379 if (fstat(sfd, &st) == 0) {
1382 if ( offset >= st.st_size) {
1385 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1386 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1389 case EINVAL: /* there's no guarantee that all fs support sendfile */
1398 lseek(sfd, offset, SEEK_SET);
1402 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1409 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1416 /* ----------------------------------
1417 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1418 * because we are doing it elsewhere.
1419 * currently if newname is NULL then adp is NULL.
1421 int copyfile(const struct vol *s_vol,
1422 const struct vol *d_vol,
1427 struct adouble *adp)
1429 struct adouble ads, add;
1436 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1437 sfd, src, dst, newname);
1440 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1444 adflags = ADFLAGS_DF;
1446 adflags |= ADFLAGS_HF;
1449 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0) {
1454 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1455 /* no resource fork, don't create one for dst file */
1456 adflags &= ~ADFLAGS_HF;
1459 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1461 if (stat_result < 0) {
1462 /* unlikely but if fstat fails, the default file mode will be 0666. */
1463 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1466 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1467 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) {
1469 ad_close( adp, adflags );
1470 if (EEXIST != ret_err) {
1471 deletefile(d_vol, -1, dst, 0);
1474 return AFPERR_EXIST;
1478 * XXX if the source and the dest don't use the same resource type it's broken
1480 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1481 /* copy the data fork */
1482 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1483 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1491 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1492 /* set the new name in the resource fork */
1493 ad_copy_header(&add, adp);
1494 ad_setname(&add, newname);
1497 ad_close( adp, adflags );
1499 if (ad_close( &add, adflags ) <0) {
1504 deletefile(d_vol, -1, dst, 0);
1506 else if (stat_result == 0) {
1507 /* set dest modification date to src date */
1510 ut.actime = ut.modtime = st.st_mtime;
1512 /* FIXME netatalk doesn't use resource fork file date
1513 * but maybe we should set its modtime too.
1518 switch ( ret_err ) {
1524 return AFPERR_DFULL;
1526 return AFPERR_NOOBJ;
1528 return AFPERR_ACCESS;
1530 return AFPERR_VLOCK;
1532 return AFPERR_PARAM;
1536 /* -----------------------------------
1537 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1538 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1540 when deletefile is called we don't have lock on it, file is closed (for us)
1541 untrue if called by renamefile
1543 ad_open always try to open file RDWR first and ad_lock takes care of
1544 WRITE lock on read only file.
1547 static int check_attrib(struct adouble *adp)
1549 u_int16_t bshort = 0;
1551 ad_getattr(adp, &bshort);
1553 * Does kFPDeleteInhibitBit (bit 8) set?
1555 if ((bshort & htons(ATTRBIT_NODELETE))) {
1556 return AFPERR_OLOCK;
1558 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1564 * dirfd can be used for unlinkat semantics
1566 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1569 struct adouble *adp = NULL;
1570 int adflags, err = AFP_OK;
1573 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1575 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1577 /* was EACCESS error try to get only metadata */
1578 /* we never want to create a resource fork here, we are going to delete it
1579 * moreover sometimes deletefile is called with a no existent file and
1580 * ad_open would create a 0 byte resource fork
1582 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1583 if ((err = check_attrib(&ad))) {
1584 ad_close_metadata(&ad);
1591 /* try to open both forks at once */
1592 adflags = ADFLAGS_DF;
1593 if ( ad_openat(&ad, dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0 ) {
1598 case EACCES: /* maybe it's a file with no write mode for us */
1599 break; /* was return AFPERR_ACCESS;*/
1612 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1613 adflags |= ADFLAGS_HF;
1614 /* FIXME we have a pb here because we want to know if a file is open
1615 * there's a 'priority inversion' if you can't open the ressource fork RW
1616 * you can delete it if it's open because you can't get a write lock.
1618 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1621 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1623 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1629 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1631 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1633 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1634 cnid_delete(vol->v_cdb, id);
1640 ad_close_metadata(&ad);
1643 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1648 /* ------------------------------------ */
1649 /* return a file id */
1650 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1659 struct path *s_path;
1665 memcpy(&vid, ibuf, sizeof(vid));
1666 ibuf += sizeof(vid);
1668 if (NULL == ( vol = getvolbyvid( vid )) ) {
1669 return( AFPERR_PARAM);
1672 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1676 if (vol->v_flags & AFPVOL_RO)
1677 return AFPERR_VLOCK;
1679 memcpy(&did, ibuf, sizeof( did ));
1680 ibuf += sizeof(did);
1682 if (NULL == ( dir = dirlookup( vol, did )) ) {
1683 return afp_errno; /* was AFPERR_PARAM */
1686 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1687 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1690 if ( path_isadir(s_path) ) {
1691 return( AFPERR_BADTYPE );
1694 upath = s_path->u_name;
1695 switch (s_path->st_errno) {
1697 break; /* success */
1700 return AFPERR_ACCESS;
1702 return AFPERR_NOOBJ;
1704 return AFPERR_PARAM;
1707 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1708 memcpy(rbuf, &id, sizeof(id));
1709 *rbuflen = sizeof(id);
1710 return AFPERR_EXISTID;
1713 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1714 memcpy(rbuf, &id, sizeof(id));
1715 *rbuflen = sizeof(id);
1722 /* ------------------------------- */
1728 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1731 struct reenum *param = data;
1732 struct vol *vol = param->vol;
1733 cnid_t did = param->did;
1736 if ( lstat(de->d_name, &path.st) < 0 )
1739 /* update or add to cnid */
1740 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1745 /* --------------------
1746 * Ok the db is out of synch with the dir.
1747 * but if it's a deleted file we don't want to do it again and again.
1750 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1756 if (vol->v_cdb == NULL) {
1760 /* FIXME use of_statdir ? */
1761 if (lstat(name, &st)) {
1765 if (dirreenumerate(dir, &st)) {
1766 /* we already did it once and the dir haven't been modified */
1771 data.did = dir->d_did;
1772 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1773 setdiroffcnt(curdir, &st, ret);
1774 dir->d_flags |= DIRF_CNID;
1780 /* ------------------------------
1781 resolve a file id */
1782 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1791 u_int16_t vid, bitmap;
1793 static char buffer[12 + MAXPATHLEN + 1];
1794 int len = 12 + MAXPATHLEN + 1;
1799 memcpy(&vid, ibuf, sizeof(vid));
1800 ibuf += sizeof(vid);
1802 if (NULL == ( vol = getvolbyvid( vid )) ) {
1803 return( AFPERR_PARAM);
1806 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1810 memcpy(&id, ibuf, sizeof( id ));
1815 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1819 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1820 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1823 if (NULL == ( dir = dirlookup( vol, id )) ) {
1824 return AFPERR_NOID; /* idem AFPERR_PARAM */
1826 if (movecwd(vol, dir) < 0) {
1830 return AFPERR_ACCESS;
1834 return AFPERR_PARAM;
1838 memset(&path, 0, sizeof(path));
1839 path.u_name = upath;
1840 if ( of_stat(&path) < 0 ) {
1842 /* with nfs and our working directory is deleted */
1843 if (errno == ESTALE) {
1847 if ( errno == ENOENT && !retry) {
1848 /* cnid db is out of sync, reenumerate the directory and update ids */
1849 reenumerate_id(vol, ".", dir);
1857 return AFPERR_ACCESS;
1861 return AFPERR_PARAM;
1865 /* directories are bad */
1866 if (S_ISDIR(path.st.st_mode)) {
1867 /* OS9 and OSX don't return the same error code */
1868 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1871 memcpy(&bitmap, ibuf, sizeof(bitmap));
1872 bitmap = ntohs( bitmap );
1873 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1877 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1878 rbuf + sizeof(bitmap), &buflen))) {
1881 *rbuflen = buflen + sizeof(bitmap);
1882 memcpy(rbuf, ibuf, sizeof(bitmap));
1887 /* ------------------------------ */
1888 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1898 static char buffer[12 + MAXPATHLEN + 1];
1899 int len = 12 + MAXPATHLEN + 1;
1904 memcpy(&vid, ibuf, sizeof(vid));
1905 ibuf += sizeof(vid);
1907 if (NULL == ( vol = getvolbyvid( vid )) ) {
1908 return( AFPERR_PARAM);
1911 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1915 if (vol->v_flags & AFPVOL_RO)
1916 return AFPERR_VLOCK;
1918 memcpy(&id, ibuf, sizeof( id ));
1922 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1926 if (NULL == ( dir = dirlookup( vol, id )) ) {
1927 if (afp_errno == AFPERR_NOOBJ) {
1931 return( AFPERR_PARAM );
1935 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1939 return AFPERR_ACCESS;
1944 /* still try to delete the id */
1948 return AFPERR_PARAM;
1951 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1952 return AFPERR_BADTYPE;
1955 if (cnid_delete(vol->v_cdb, fileid)) {
1958 return AFPERR_VLOCK;
1961 return AFPERR_ACCESS;
1963 return AFPERR_PARAM;
1970 /* ------------------------------ */
1971 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
1975 if (path->st_errno) {
1976 switch (path->st_errno) {
1978 afp_errno = AFPERR_NOID;
1982 afp_errno = AFPERR_ACCESS;
1985 afp_errno = AFPERR_PARAM;
1990 /* we use file_access both for legacy Mac perm and
1991 * for unix privilege, rename will take care of folder perms
1993 if (file_access(path, OPENACC_WR ) < 0) {
1994 afp_errno = AFPERR_ACCESS;
1998 if ((*of = of_findname(path))) {
1999 /* reuse struct adouble so it won't break locks */
2003 ret = ad_open(adp, path->u_name, ADFLAGS_HF, O_RDONLY);
2005 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2007 * The user must have the Read & Write privilege for both files in order to use this command.
2009 ad_close(adp, ADFLAGS_HF);
2010 afp_errno = AFPERR_ACCESS;
2017 #define APPLETEMP ".AppleTempXXXXXX"
2019 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2021 struct stat srcst, destst;
2023 struct dir *dir, *sdir;
2024 char *spath, temp[17], *p;
2025 char *supath, *upath;
2030 struct adouble *adsp = NULL;
2031 struct adouble *addp = NULL;
2032 struct ofork *s_of = NULL;
2033 struct ofork *d_of = NULL;
2046 memcpy(&vid, ibuf, sizeof(vid));
2047 ibuf += sizeof(vid);
2049 if (NULL == ( vol = getvolbyvid( vid )) ) {
2050 return( AFPERR_PARAM);
2053 if ((vol->v_flags & AFPVOL_RO))
2054 return AFPERR_VLOCK;
2056 /* source and destination dids */
2057 memcpy(&sid, ibuf, sizeof(sid));
2058 ibuf += sizeof(sid);
2059 memcpy(&did, ibuf, sizeof(did));
2060 ibuf += sizeof(did);
2063 if (NULL == (dir = dirlookup( vol, sid )) ) {
2064 return afp_errno; /* was AFPERR_PARAM */
2067 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2068 return get_afp_errno(AFPERR_NOOBJ);
2071 if ( path_isadir(path) ) {
2072 return AFPERR_BADTYPE; /* it's a dir */
2075 /* save some stuff */
2078 spath = obj->oldtmp;
2079 supath = obj->newtmp;
2080 strcpy(spath, path->m_name);
2081 strcpy(supath, path->u_name); /* this is for the cnid changing */
2082 p = absupath( vol, sdir, supath);
2084 /* pathname too long */
2085 return AFPERR_PARAM ;
2088 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2089 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2093 /* ***** from here we may have resource fork open **** */
2095 /* look for the source cnid. if it doesn't exist, don't worry about
2097 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2099 if (NULL == ( dir = dirlookup( vol, did )) ) {
2100 err = afp_errno; /* was AFPERR_PARAM */
2101 goto err_exchangefile;
2104 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2105 err = get_afp_errno(AFPERR_NOOBJ);
2106 goto err_exchangefile;
2109 if ( path_isadir(path) ) {
2110 err = AFPERR_BADTYPE;
2111 goto err_exchangefile;
2114 /* FPExchangeFiles is the only call that can return the SameObj
2116 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2117 err = AFPERR_SAMEOBJ;
2118 goto err_exchangefile;
2121 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2122 if (!(addp = find_adouble( path, &d_of, &add))) {
2124 goto err_exchangefile;
2128 /* they are not on the same device and at least one is open
2129 * FIXME broken for for crossdev and adouble v2
2132 crossdev = (srcst.st_dev != destst.st_dev);
2133 if (/* (d_of || s_of) && */ crossdev) {
2135 goto err_exchangefile;
2138 /* look for destination id. */
2139 upath = path->u_name;
2140 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2142 /* construct a temp name.
2143 * NOTE: the temp file will be in the dest file's directory. it
2144 * will also be inaccessible from AFP. */
2145 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2146 if (!mktemp(temp)) {
2148 goto err_exchangefile;
2152 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2153 ad_close(adsp, ADFLAGS_HF);
2154 ad_close(addp, ADFLAGS_HF);
2157 /* now, quickly rename the file. we error if we can't. */
2158 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2159 goto err_exchangefile;
2160 of_rename(vol, s_of, sdir, spath, curdir, temp);
2162 /* rename destination to source */
2163 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2164 goto err_src_to_tmp;
2165 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2167 /* rename temp to destination */
2168 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2169 goto err_dest_to_src;
2170 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2172 /* id's need switching. src -> dest and dest -> src.
2173 * we need to re-stat() if it was a cross device copy.
2176 cnid_delete(vol->v_cdb, sid);
2178 cnid_delete(vol->v_cdb, did);
2180 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2181 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2183 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2184 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2189 err = AFPERR_ACCESS;
2194 goto err_temp_to_dest;
2197 /* here we need to reopen if crossdev */
2198 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2203 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2208 /* change perms, src gets dest perm and vice versa */
2213 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2214 err = AFP_OK; /* ignore error */
2215 goto err_temp_to_dest;
2219 * we need to exchange ACL entries as well
2221 /* exchange_acls(vol, p, upath); */
2226 path->m_name = NULL;
2227 path->u_name = upath;
2229 setfilunixmode(vol, path, destst.st_mode);
2230 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2237 setfilunixmode(vol, path, srcst.st_mode);
2238 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2240 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2241 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2246 goto err_exchangefile;
2248 /* all this stuff is so that we can unwind a failed operation
2251 /* rename dest to temp */
2252 renamefile(vol, -1, upath, temp, temp, adsp);
2253 of_rename(vol, s_of, curdir, upath, curdir, temp);
2256 /* rename source back to dest */
2257 renamefile(vol, -1, p, upath, path->m_name, addp);
2258 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2261 /* rename temp back to source */
2262 renamefile(vol, -1, temp, p, spath, adsp);
2263 of_rename(vol, s_of, curdir, temp, sdir, spath);
2266 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2267 ad_close(adsp, ADFLAGS_HF);
2269 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2270 ad_close(addp, ADFLAGS_HF);
2274 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2275 (void)dir_remove(vol, cached);
2276 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2277 (void)dir_remove(vol, cached);