2 * Copyright (c) 1990,1993 Regents of The University of Michigan.
3 * All Rights Reserved. See COPYRIGHT.
8 #endif /* HAVE_CONFIG_H */
16 #include <sys/param.h>
18 #include <atalk/adouble.h>
19 #include <atalk/vfs.h>
20 #include <atalk/logger.h>
21 #include <atalk/afp.h>
22 #include <atalk/util.h>
23 #include <atalk/cnid.h>
24 #include <atalk/unix.h>
25 #include <atalk/globals.h>
26 #include <atalk/fce_api.h>
28 #include "directory.h"
37 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
38 * field bytes subfield bytes
41 * ioFlFndrInfo 16 -> type 4 type field
42 * creator 4 creator field
43 * flags 2 finder flags:
45 * location 4 location in window
46 * folder 2 window that contains file
48 * ioFlXFndrInfo 16 -> iconID 2 icon id
50 * script 1 script system
52 * commentID 2 comment id
53 * putawayID 4 home directory id
56 const u_char ufinderi[ADEDLEN_FINDERI] = {
57 0, 0, 0, 0, 0, 0, 0, 0,
58 1, 0, 0, 0, 0, 0, 0, 0,
59 0, 0, 0, 0, 0, 0, 0, 0,
60 0, 0, 0, 0, 0, 0, 0, 0
63 static const u_char old_ufinderi[] = {
64 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
67 /* ----------------------
69 static int default_type(void *finder)
71 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
76 /* FIXME path : unix or mac name ? (for now it's unix name ) */
77 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
80 void *ad_finder = NULL;
84 ad_finder = ad_entry(adp, ADEID_FINDERI);
87 memcpy(data, ad_finder, ADEDLEN_FINDERI);
89 if (default_type(ad_finder))
93 memcpy(data, ufinderi, ADEDLEN_FINDERI);
95 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
98 ashort = htons(FINDERINFO_INVISIBLE);
99 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
105 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
106 linkflag |= htons(FINDERINFO_ISALIAS);
107 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
108 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
109 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
113 /** Only enter if no appledouble information and no finder information found. */
114 if (chk_ext && (em = getextmap( upath ))) {
115 memcpy(data, em->em_type, sizeof( em->em_type ));
116 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
121 /* ---------------------
123 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, uint32_t utf8)
128 aint = strlen( name );
132 if (utf8_encoding()) {
133 /* but name is an utf8 mac name */
136 /* global static variable... */
138 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
147 if (aint > MACFILELEN)
154 if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
155 aint = UTF8FILELEN_EARLY;
157 utf8 = vol->v_kTextEncoding;
158 memcpy(data, &utf8, sizeof(utf8));
159 data += sizeof(utf8);
162 memcpy(data, &temp, sizeof(temp));
163 data += sizeof(temp);
166 memcpy( data, src, aint );
176 * FIXME: PDINFO is UTF8 and doesn't need adp
178 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
179 (1 << FILPBIT_CDATE) |\
180 (1 << FILPBIT_MDATE) |\
181 (1 << FILPBIT_BDATE) |\
182 (1 << FILPBIT_FINFO) |\
183 (1 << FILPBIT_RFLEN) |\
184 (1 << FILPBIT_EXTRFLEN) |\
185 (1 << FILPBIT_PDINFO) |\
186 (1 << FILPBIT_FNUM) |\
187 (1 << FILPBIT_UNIXPR)))
190 * @brief Get CNID for did/upath args both from database and adouble file
192 * 1. Get the objects CNID as stored in its adouble file
193 * 2. Get the objects CNID from the database
194 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
195 * 4. In case 2 and 3 differ, store 3 in the adouble file
197 * @param vol (rw) volume
198 * @param adp (rw) adouble struct of object upath, might be NULL
199 * @param st (r) stat of upath, must NOT be NULL
200 * @param did (r) parent CNID of upath
201 * @param upath (r) name of object
202 * @param len (r) strlen of upath
204 uint32_t get_id(struct vol *vol,
206 const struct stat *st,
211 static int first = 1; /* mark if this func is called the first time */
213 uint32_t dbcnid = CNID_INVALID;
216 if (vol->v_cdb != NULL) {
217 /* prime aint with what we think is the cnid, set did to zero for
218 catching moved files */
219 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
221 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
222 /* Throw errors if cnid_add fails. */
223 if (dbcnid == CNID_INVALID) {
225 case CNID_ERR_CLOSE: /* the db is closed */
228 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
229 afp_errno = AFPERR_PARAM;
232 afp_errno = AFPERR_PARAM;
235 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
236 /* we have to do it here for "dbd" because it uses "lazy opening" */
237 /* In order to not end in a loop somehow with goto restart below */
239 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
240 cnid_close(vol->v_cdb);
241 free(vol->v_cnidscheme);
242 vol->v_cnidscheme = strdup("tdb");
244 int flags = CNID_FLAG_MEMORY;
245 if ((vol->v_flags & AFPVOL_NODEV)) {
246 flags |= CNID_FLAG_NODEV;
248 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
250 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
252 /* deactivate cnid caching/storing in AppleDouble files and set ro mode*/
253 vol->v_flags &= ~AFPVOL_CACHE;
254 vol->v_flags |= AFPVOL_RO;
256 /* kill ourself with SIGUSR2 aka msg pending */
257 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
258 "Check server messages for details. Switching to read-only mode.");
259 kill(getpid(), SIGUSR2);
261 goto restart; /* not try again with the temp CNID db */
264 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
265 "Check server messages for details, can't recover from this state!");
269 afp_errno = AFPERR_MISC;
273 else if (adp && (adcnid != dbcnid)) { /* 4 */
274 /* Update the ressource fork. For a folder adp is always null */
275 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
276 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
277 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
288 /* -------------------------- */
289 int getmetadata(struct vol *vol,
291 struct path *path, struct dir *dir,
292 char *buf, size_t *buflen, struct adouble *adp)
294 char *data, *l_nameoff = NULL, *upath;
295 char *utf_nameoff = NULL;
300 u_char achar, fdType[4];
305 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
307 upath = path->u_name;
311 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
312 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
313 || (bitmap & (1 << FILPBIT_FNUM))) {
316 struct dir *cachedfile;
317 int len = strlen(upath);
318 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
319 id = cachedfile->d_did;
321 id = get_id(vol, adp, st, dir->d_did, upath, len);
323 /* Add it to the cache */
324 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
325 ntohl(dir->d_did), upath, ntohl(id));
327 /* Get macname from unixname first */
328 if (path->m_name == NULL) {
329 if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
330 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
336 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
337 || (bconchar(fullpath, '/') != BSTR_OK)
338 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
339 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
343 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
344 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
348 if ((dircache_add(vol, cachedfile)) != 0) {
349 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
357 if (id == CNID_INVALID)
361 path->m_name = utompath(vol, upath, id, utf8_encoding());
364 while ( bitmap != 0 ) {
365 while (( bitmap & 1 ) == 0 ) {
373 ad_getattr(adp, &ashort);
374 } else if (vol_inv_dots(vol) && *upath == '.') {
375 ashort = htons(ATTRBIT_INVISIBLE);
379 /* FIXME do we want a visual clue if the file is read only
382 accessmode(vol, ".", &ma, dir , NULL);
383 if ((ma.ma_user & AR_UWRITE)) {
384 accessmode(vol, upath, &ma, dir , st);
385 if (!(ma.ma_user & AR_UWRITE)) {
386 ashort |= htons(ATTRBIT_NOWRITE);
390 memcpy(data, &ashort, sizeof( ashort ));
391 data += sizeof( ashort );
392 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
393 path->u_name, ntohs(ashort));
397 memcpy(data, &dir->d_did, sizeof( uint32_t ));
398 data += sizeof( uint32_t );
399 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
400 path->u_name, ntohl(dir->d_did));
404 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
405 aint = AD_DATE_FROM_UNIX(st->st_mtime);
406 memcpy(data, &aint, sizeof( aint ));
407 data += sizeof( aint );
411 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
412 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
413 aint = AD_DATE_FROM_UNIX(st->st_mtime);
416 aint = AD_DATE_FROM_UNIX(st->st_mtime);
418 memcpy(data, &aint, sizeof( int ));
419 data += sizeof( int );
423 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
424 aint = AD_DATE_START;
425 memcpy(data, &aint, sizeof( int ));
426 data += sizeof( int );
430 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
431 data += ADEDLEN_FINDERI;
436 data += sizeof( uint16_t );
440 memset(data, 0, sizeof(uint16_t));
441 data += sizeof( uint16_t );
445 memcpy(data, &id, sizeof( id ));
446 data += sizeof( id );
447 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
448 path->u_name, ntohl(id));
452 if (st->st_size > 0xffffffff)
455 aint = htonl( st->st_size );
456 memcpy(data, &aint, sizeof( aint ));
457 data += sizeof( aint );
462 if (adp->ad_rlen > 0xffffffff)
465 aint = htonl( adp->ad_rlen);
469 memcpy(data, &aint, sizeof( aint ));
470 data += sizeof( aint );
473 /* Current client needs ProDOS info block for this file.
474 Use simple heuristic and let the Mac "type" string tell
475 us what the PD file code should be. Everything gets a
476 subtype of 0x0000 unless the original value was hashed
477 to "pXYZ" when we created it. See IA, Ver 2.
478 <shirsch@adelphia.net> */
479 case FILPBIT_PDINFO :
480 if (afp_version >= 30) { /* UTF8 name */
481 utf8 = kTextEncodingUTF8;
483 data += sizeof( uint16_t );
485 memcpy(data, &aint, sizeof( aint ));
486 data += sizeof( aint );
490 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
492 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
496 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
500 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
504 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
508 else if ( fdType[0] == 'p' ) {
510 ashort = (fdType[2] * 256) + fdType[3];
524 memcpy(data, &ashort, sizeof( ashort ));
525 data += sizeof( ashort );
526 memset(data, 0, sizeof( ashort ));
527 data += sizeof( ashort );
530 case FILPBIT_EXTDFLEN:
531 aint = htonl(st->st_size >> 32);
532 memcpy(data, &aint, sizeof( aint ));
533 data += sizeof( aint );
534 aint = htonl(st->st_size);
535 memcpy(data, &aint, sizeof( aint ));
536 data += sizeof( aint );
538 case FILPBIT_EXTRFLEN:
541 aint = htonl(adp->ad_rlen >> 32);
542 memcpy(data, &aint, sizeof( aint ));
543 data += sizeof( aint );
545 aint = htonl(adp->ad_rlen);
546 memcpy(data, &aint, sizeof( aint ));
547 data += sizeof( aint );
549 case FILPBIT_UNIXPR :
550 /* accessmode may change st_mode with ACLs */
551 accessmode(vol, upath, &ma, dir , st);
553 aint = htonl(st->st_uid);
554 memcpy( data, &aint, sizeof( aint ));
555 data += sizeof( aint );
556 aint = htonl(st->st_gid);
557 memcpy( data, &aint, sizeof( aint ));
558 data += sizeof( aint );
561 type == slnk indicates an OSX style symlink,
562 we have to add S_IFLNK to the mode, otherwise
563 10.3 clients freak out. */
567 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
568 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
574 memcpy( data, &aint, sizeof( aint ));
575 data += sizeof( aint );
577 *data++ = ma.ma_user;
578 *data++ = ma.ma_world;
579 *data++ = ma.ma_group;
580 *data++ = ma.ma_owner;
584 return( AFPERR_BITMAP );
590 ashort = htons( data - buf );
591 memcpy(l_nameoff, &ashort, sizeof( ashort ));
592 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
595 ashort = htons( data - buf );
596 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
597 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
599 *buflen = data - buf;
603 /* ----------------------- */
604 int getfilparams(struct vol *vol,
606 struct path *path, struct dir *dir,
607 char *buf, size_t *buflen )
609 struct adouble ad, *adp;
613 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
615 opened = PARAM_NEED_ADP(bitmap);
620 int flags = (bitmap & (1 << FILPBIT_ATTR)) ? ADFLAGS_CHECK_OF : 0;
622 adp = of_ad(vol, path, &ad);
623 upath = path->u_name;
625 if ( ad_metadata( upath, flags, adp) < 0 ) {
628 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
629 upath, strerror(errno));
630 return AFPERR_ACCESS;
632 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
641 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
642 ad_close(adp, ADFLAGS_HF);
647 /* ----------------------------- */
648 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
653 struct ofork *of = NULL;
655 int creatf, did, openf, retvalue = AFP_OK;
661 creatf = (unsigned char) *ibuf++;
663 memcpy(&vid, ibuf, sizeof( vid ));
664 ibuf += sizeof( vid );
666 if (NULL == ( vol = getvolbyvid( vid )) )
667 return( AFPERR_PARAM );
669 if (vol->v_flags & AFPVOL_RO)
672 memcpy(&did, ibuf, sizeof( did));
673 ibuf += sizeof( did );
675 if (NULL == ( dir = dirlookup( vol, did )) )
678 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
679 return get_afp_errno(AFPERR_PARAM);
680 if ( *s_path->m_name == '\0' )
681 return( AFPERR_BADTYPE );
683 upath = s_path->u_name;
686 /* if upath is deleted we already in trouble anyway */
687 if ((of = of_findname(s_path))) {
695 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
697 /* on a soft create, if the file is open then ad_open won't fail
698 because open syscall is not called */
699 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
701 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
705 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
706 return ( AFPERR_NOOBJ );
708 return( AFPERR_EXIST );
710 return( AFPERR_ACCESS );
713 return( AFPERR_DFULL );
715 return( AFPERR_PARAM );
718 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
719 /* FIXME with hard create on an existing file, we already
720 * corrupted the data file.
722 netatalk_unlink( upath );
723 ad_close( &ad, ADFLAGS_DF );
724 return AFPERR_ACCESS;
727 path = s_path->m_name;
728 ad_setname(&ad, path);
731 if (lstat(upath, &st) != 0) {
732 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
733 upath, strerror(errno));
734 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
738 (void)get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath));
741 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
742 fce_register_new_file(s_path);
747 setvoltime(obj, vol );
752 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
758 uint16_t vid, bitmap;
763 memcpy(&vid, ibuf, sizeof( vid ));
764 ibuf += sizeof( vid );
765 if (NULL == ( vol = getvolbyvid( vid )) ) {
766 return( AFPERR_PARAM );
769 if (vol->v_flags & AFPVOL_RO)
772 memcpy(&did, ibuf, sizeof( did ));
773 ibuf += sizeof( did );
774 if (NULL == ( dir = dirlookup( vol, did )) ) {
775 return afp_errno; /* was AFPERR_NOOBJ */
778 memcpy(&bitmap, ibuf, sizeof( bitmap ));
779 bitmap = ntohs( bitmap );
780 ibuf += sizeof( bitmap );
782 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
783 return get_afp_errno(AFPERR_PARAM);
786 if (path_isadir(s_path)) {
787 return( AFPERR_BADTYPE ); /* it's a directory */
790 if ( s_path->st_errno != 0 ) {
791 return( AFPERR_NOOBJ );
794 if ((u_long)ibuf & 1 ) {
798 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
799 setvoltime(obj, vol );
806 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
809 extern struct path Cur_Path;
811 int setfilparams(struct vol *vol,
812 struct path *path, uint16_t f_bitmap, char *buf )
814 struct adouble ad, *adp;
816 int bit, isad = 1, err = AFP_OK;
818 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
819 uint16_t ashort, bshort, oshort;
822 uint16_t upriv_bit = 0;
824 int change_mdate = 0;
825 int change_parent_mdate = 0;
830 uint16_t bitmap = f_bitmap;
831 uint32_t cdate,bdate;
832 u_char finder_buf[32];
836 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
839 adp = of_ad(vol, path, &ad);
840 upath = path->u_name;
842 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
843 return AFPERR_ACCESS;
846 /* with unix priv maybe we have to change adouble file priv first */
848 while ( bitmap != 0 ) {
849 while (( bitmap & 1 ) == 0 ) {
856 memcpy(&ashort, buf, sizeof( ashort ));
857 buf += sizeof( ashort );
861 memcpy(&cdate, buf, sizeof(cdate));
862 buf += sizeof( cdate );
865 memcpy(&newdate, buf, sizeof( newdate ));
866 buf += sizeof( newdate );
870 memcpy(&bdate, buf, sizeof( bdate));
871 buf += sizeof( bdate );
875 memcpy(finder_buf, buf, 32 );
876 if (memcmp(buf, "slnkrhap", 8) == 0 && !S_ISLNK(path->st.st_mode)) {
880 char buf[PATH_MAX+1];
881 if ((fp = open(path->u_name, O_RDONLY)) >= 0) {
882 if ((len = read(fp, buf, PATH_MAX+1))) {
883 if (unlink(path->u_name) == 0) {
885 erc = symlink(buf, path->u_name);
894 goto setfilparam_done;
900 case FILPBIT_UNIXPR :
901 if (!vol_unix_priv(vol)) {
902 /* this volume doesn't use unix priv */
908 change_parent_mdate = 1;
910 memcpy( &aint, buf, sizeof( aint ));
911 f_uid = ntohl (aint);
912 buf += sizeof( aint );
913 memcpy( &aint, buf, sizeof( aint ));
914 f_gid = ntohl (aint);
915 buf += sizeof( aint );
916 setfilowner(vol, f_uid, f_gid, path);
918 memcpy( &upriv, buf, sizeof( upriv ));
919 buf += sizeof( upriv );
920 upriv = ntohl (upriv);
921 if ((upriv & S_IWUSR)) {
922 setfilunixmode(vol, path, upriv);
929 case FILPBIT_PDINFO :
930 if (afp_version < 30) { /* else it's UTF8 name */
933 /* Keep special case to support crlf translations */
934 if ((unsigned int) achar == 0x04) {
935 fdType = (u_char *)"TEXT";
938 xyy[0] = ( u_char ) 'p';
949 /* break while loop */
958 /* second try with adouble open
960 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
961 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
963 * For some things, we don't need an adouble header:
964 * - change of modification date
965 * - UNIX privs (Bug-ID #2863424)
967 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
968 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
969 return AFPERR_ACCESS;
971 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
973 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
974 ad_setname(adp, path->m_name);
979 while ( bitmap != 0 ) {
980 while (( bitmap & 1 ) == 0 ) {
987 ad_getattr(adp, &bshort);
989 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
990 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
994 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
995 change_parent_mdate = 1;
996 ad_setattr(adp, bshort);
999 ad_setdate(adp, AD_DATE_CREATE, cdate);
1001 case FILPBIT_MDATE :
1003 case FILPBIT_BDATE :
1004 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1006 case FILPBIT_FINFO :
1007 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1009 ((em = getextmap( path->m_name )) &&
1010 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1011 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1012 || ((em = getdefextmap()) &&
1013 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1014 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1016 memcpy(finder_buf, ufinderi, 8 );
1018 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1020 case FILPBIT_UNIXPR :
1022 setfilunixmode(vol, path, upriv);
1025 case FILPBIT_PDINFO :
1026 if (afp_version < 30) { /* else it's UTF8 name */
1027 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1028 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1033 err = AFPERR_BITMAP;
1034 goto setfilparam_done;
1041 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1042 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1046 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1047 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1053 ad_close(adp, ADFLAGS_HF);
1056 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1057 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1058 bitmap = 1<<FILPBIT_MDATE;
1059 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1063 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1069 * renamefile and copyfile take the old and new unix pathnames
1070 * and the new mac name.
1072 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1073 * passing -1 means this is not used, src path is a full path
1074 * src the source path
1075 * dst the dest filename in current dir
1076 * newname the dest mac name
1077 * adp adouble struct of src file, if open, or & zeroed one
1080 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1084 LOG(log_debug, logtype_afpd,
1085 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1087 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1090 return( AFPERR_NOOBJ );
1093 return( AFPERR_ACCESS );
1095 return AFPERR_VLOCK;
1096 case EXDEV : /* Cross device move -- try copy */
1097 /* NOTE: with open file it's an error because after the copy we will
1098 * get two files, it's fixable for our process (eg reopen the new file, get the
1099 * locks, and so on. But it doesn't solve the case with a second process
1101 if (adp->ad_open_forks) {
1102 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1103 return AFPERR_OLOCK; /* little lie */
1105 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1106 /* on error copyfile delete dest */
1109 return deletefile(vol, sdir_fd, src, 0);
1111 return( AFPERR_PARAM );
1115 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1119 /* try to undo the data fork rename,
1120 * we know we are on the same device
1123 unix_rename(-1, dst, sdir_fd, src );
1124 /* return the first error */
1127 return AFPERR_NOOBJ;
1130 return AFPERR_ACCESS ;
1132 return AFPERR_VLOCK;
1134 return AFPERR_PARAM ;
1139 /* don't care if we can't open the newly renamed ressource fork */
1140 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1141 ad_setname(adp, newname);
1143 ad_close( adp, ADFLAGS_HF );
1150 convert a Mac long name to an utf8 name,
1152 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1156 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1162 /* ---------------- */
1163 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1170 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1176 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1177 if (afp_version >= 30) {
1178 /* convert it to UTF8
1180 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1184 strncpy( newname, ibuf, plen );
1185 newname[ plen ] = '\0';
1187 if (strlen(newname) != plen) {
1188 /* there's \0 in newname, e.g. it's a pathname not
1196 memcpy(&hint, ibuf, sizeof(hint));
1197 ibuf += sizeof(hint);
1199 memcpy(&len16, ibuf, sizeof(len16));
1200 ibuf += sizeof(len16);
1201 plen = ntohs(len16);
1204 if (plen > AFPOBJ_TMPSIZ) {
1207 strncpy( newname, ibuf, plen );
1208 newname[ plen ] = '\0';
1209 if (strlen(newname) != plen) {
1218 /* -----------------------------------
1220 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1222 struct vol *s_vol, *d_vol;
1224 char *newname, *p, *upath;
1225 struct path *s_path;
1226 uint32_t sdid, ddid;
1227 int err, retvalue = AFP_OK;
1228 uint16_t svid, dvid;
1230 struct adouble ad, *adp;
1236 memcpy(&svid, ibuf, sizeof( svid ));
1237 ibuf += sizeof( svid );
1238 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1239 return( AFPERR_PARAM );
1242 memcpy(&sdid, ibuf, sizeof( sdid ));
1243 ibuf += sizeof( sdid );
1244 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1248 memcpy(&dvid, ibuf, sizeof( dvid ));
1249 ibuf += sizeof( dvid );
1250 memcpy(&ddid, ibuf, sizeof( ddid ));
1251 ibuf += sizeof( ddid );
1253 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1254 return get_afp_errno(AFPERR_PARAM);
1256 if ( path_isadir(s_path) ) {
1257 return( AFPERR_BADTYPE );
1260 /* don't allow copies when the file is open.
1261 * XXX: the spec only calls for read/deny write access.
1262 * however, copyfile doesn't have any of that info,
1263 * and locks need to stay coherent. as a result,
1264 * we just balk if the file is opened already. */
1266 adp = of_ad(s_vol, s_path, &ad);
1268 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1269 return AFPERR_DENYCONF;
1271 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1272 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1275 retvalue = AFPERR_DENYCONF;
1279 newname = obj->newtmp;
1280 strcpy( newname, s_path->m_name );
1282 p = ctoupath( s_vol, curdir, newname );
1284 retvalue = AFPERR_PARAM;
1288 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1289 retvalue = AFPERR_PARAM;
1293 if (d_vol->v_flags & AFPVOL_RO) {
1294 retvalue = AFPERR_VLOCK;
1298 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1299 retvalue = afp_errno;
1303 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1304 retvalue = get_afp_errno(AFPERR_NOOBJ);
1308 if ( *s_path->m_name != '\0' ) {
1309 retvalue =path_error(s_path, AFPERR_NOOBJ);
1313 /* one of the handful of places that knows about the path type */
1314 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1315 retvalue = AFPERR_PARAM;
1318 /* newname is always only a filename so curdir *is* its
1321 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1322 retvalue =AFPERR_PARAM;
1326 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1332 setvoltime(obj, d_vol );
1335 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1339 /* ----------------------- */
1340 static int copy_all(const int dfd, const void *buf,
1346 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1349 while (buflen > 0) {
1350 if ((cc = write(dfd, buf, buflen)) < 0) {
1362 LOG(log_debug9, logtype_afpd, "end copy_all:");
1368 /* --------------------------
1369 * copy only the fork data stream
1371 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1379 if (eid == ADEID_DFORK) {
1380 sfd = ad_data_fileno(ads);
1381 dfd = ad_data_fileno(add);
1384 sfd = ad_reso_fileno(ads);
1385 dfd = ad_reso_fileno(add);
1388 if (add->ad_version == AD_VERSION2)
1389 soff = doff = ad_getentryoff(ads, eid);
1391 if (eid == ADEID_DFORK)
1392 soff = doff = ad_getentryoff(ads, eid);
1397 soff = doff = ADEDOFF_RFORK_OSX;
1402 if ((off_t)-1 == lseek(sfd, soff, SEEK_SET))
1405 if ((off_t)-1 == lseek(dfd, doff, SEEK_SET))
1408 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1409 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1413 #define BUF 128*1024*1024
1415 if (fstat(sfd, &st) == 0) {
1418 if ( offset >= st.st_size) {
1421 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1422 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1425 case EINVAL: /* there's no guarantee that all fs support sendfile */
1434 lseek(sfd, offset, SEEK_SET);
1438 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1445 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1452 /* ----------------------------------
1453 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1454 * because we are doing it elsewhere.
1455 * currently if newname is NULL then adp is NULL.
1457 int copyfile(const struct vol *s_vol,
1458 const struct vol *d_vol,
1463 struct adouble *adp)
1465 struct adouble ads, add;
1472 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1473 sfd, src, dst, newname);
1476 ad_init(&ads, s_vol);
1480 adflags = ADFLAGS_DF | ADFLAGS_RF | ADFLAGS_NORF;
1482 adflags |= ADFLAGS_HF;
1485 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_NOHF | ADFLAGS_RDONLY) < 0) {
1490 if (!AD_RSRC_OPEN(adp))
1491 /* no resource fork, don't create one for dst file */
1492 adflags &= ~ADFLAGS_RF;
1494 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1496 if (stat_result < 0) {
1497 /* unlikely but if fstat fails, the default file mode will be 0666. */
1498 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1501 ad_init(&add, d_vol);
1502 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1504 ad_close( adp, adflags );
1505 if (EEXIST != ret_err) {
1506 deletefile(d_vol, -1, dst, 0);
1509 return AFPERR_EXIST;
1512 if (AD_RSRC_OPEN(adp))
1513 err = copy_fork(ADEID_RFORK, &add, adp);
1516 err = copy_fork(ADEID_DFORK, &add, adp);
1518 if ((err == 0) && (ad_meta_fileno(adp) != -1))
1519 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1524 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1525 /* set the new name in the resource fork */
1526 ad_copy_header(&add, adp);
1527 ad_setname(&add, newname);
1530 ad_close( adp, adflags );
1532 if (ad_close( &add, adflags ) <0) {
1537 deletefile(d_vol, -1, dst, 0);
1539 else if (stat_result == 0) {
1540 /* set dest modification date to src date */
1543 ut.actime = ut.modtime = st.st_mtime;
1545 /* FIXME netatalk doesn't use resource fork file date
1546 * but maybe we should set its modtime too.
1551 switch ( ret_err ) {
1557 return AFPERR_DFULL;
1559 return AFPERR_NOOBJ;
1561 return AFPERR_ACCESS;
1563 return AFPERR_VLOCK;
1565 return AFPERR_PARAM;
1569 /* -----------------------------------
1570 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1571 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1573 when deletefile is called we don't have lock on it, file is closed (for us)
1574 untrue if called by renamefile
1576 ad_open always try to open file RDWR first and ad_lock takes care of
1577 WRITE lock on read only file.
1580 static int check_attrib(struct adouble *adp)
1582 uint16_t bshort = 0;
1584 ad_getattr(adp, &bshort);
1586 * Does kFPDeleteInhibitBit (bit 8) set?
1588 if ((bshort & htons(ATTRBIT_NODELETE))) {
1589 return AFPERR_OLOCK;
1591 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1597 * dirfd can be used for unlinkat semantics
1599 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1602 struct adouble *adp = NULL;
1603 int adflags, err = AFP_OK;
1606 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1610 /* was EACCESS error try to get only metadata */
1611 /* we never want to create a resource fork here, we are going to delete it
1612 * moreover sometimes deletefile is called with a no existent file and
1613 * ad_open would create a 0 byte resource fork
1615 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1616 if ((err = check_attrib(&ad))) {
1617 ad_close(&ad, ADFLAGS_HF);
1624 /* try to open both forks at once */
1625 adflags = ADFLAGS_DF;
1626 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1631 case EACCES: /* maybe it's a file with no write mode for us */
1632 break; /* was return AFPERR_ACCESS;*/
1645 if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1646 adflags |= ADFLAGS_RF;
1647 /* FIXME we have a pb here because we want to know if a file is open
1648 * there's a 'priority inversion' if you can't open the ressource fork RW
1649 * you can delete it if it's open because you can't get a write lock.
1651 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1654 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1656 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1662 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1663 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1665 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1667 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1668 cnid_delete(vol->v_cdb, id);
1674 ad_close(&ad, ADFLAGS_HF);
1677 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1682 /* ------------------------------------ */
1683 /* return a file id */
1684 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1693 struct path *s_path;
1699 memcpy(&vid, ibuf, sizeof(vid));
1700 ibuf += sizeof(vid);
1702 if (NULL == ( vol = getvolbyvid( vid )) ) {
1703 return( AFPERR_PARAM);
1706 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1710 if (vol->v_flags & AFPVOL_RO)
1711 return AFPERR_VLOCK;
1713 memcpy(&did, ibuf, sizeof( did ));
1714 ibuf += sizeof(did);
1716 if (NULL == ( dir = dirlookup( vol, did )) ) {
1717 return afp_errno; /* was AFPERR_PARAM */
1720 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1721 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1724 if ( path_isadir(s_path) ) {
1725 return( AFPERR_BADTYPE );
1728 upath = s_path->u_name;
1729 switch (s_path->st_errno) {
1731 break; /* success */
1734 return AFPERR_ACCESS;
1736 return AFPERR_NOOBJ;
1738 return AFPERR_PARAM;
1741 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1742 memcpy(rbuf, &id, sizeof(id));
1743 *rbuflen = sizeof(id);
1744 return AFPERR_EXISTID;
1747 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1748 memcpy(rbuf, &id, sizeof(id));
1749 *rbuflen = sizeof(id);
1756 /* ------------------------------- */
1762 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1765 struct reenum *param = data;
1766 struct vol *vol = param->vol;
1767 cnid_t did = param->did;
1770 if ( lstat(de->d_name, &path.st) < 0 )
1773 /* update or add to cnid */
1774 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1779 /* --------------------
1780 * Ok the db is out of synch with the dir.
1781 * but if it's a deleted file we don't want to do it again and again.
1784 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1790 if (vol->v_cdb == NULL) {
1794 /* FIXME use of_statdir ? */
1795 if (lstat(name, &st)) {
1799 if (dirreenumerate(dir, &st)) {
1800 /* we already did it once and the dir haven't been modified */
1801 return dir->d_offcnt;
1805 data.did = dir->d_did;
1806 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1807 setdiroffcnt(curdir, &st, ret);
1808 dir->d_flags |= DIRF_CNID;
1814 /* ------------------------------
1815 resolve a file id */
1816 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1825 uint16_t vid, bitmap;
1827 static char buffer[12 + MAXPATHLEN + 1];
1828 int len = 12 + MAXPATHLEN + 1;
1833 memcpy(&vid, ibuf, sizeof(vid));
1834 ibuf += sizeof(vid);
1836 if (NULL == ( vol = getvolbyvid( vid )) ) {
1837 return( AFPERR_PARAM);
1840 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1844 memcpy(&id, ibuf, sizeof( id ));
1849 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1853 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1854 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1857 if (NULL == ( dir = dirlookup( vol, id )) ) {
1858 return AFPERR_NOID; /* idem AFPERR_PARAM */
1860 if (movecwd(vol, dir) < 0) {
1864 return AFPERR_ACCESS;
1868 return AFPERR_PARAM;
1872 memset(&path, 0, sizeof(path));
1873 path.u_name = upath;
1874 if ( of_stat(&path) < 0 ) {
1876 /* with nfs and our working directory is deleted */
1877 if (errno == ESTALE) {
1881 if ( errno == ENOENT && !retry) {
1882 /* cnid db is out of sync, reenumerate the directory and update ids */
1883 reenumerate_id(vol, ".", dir);
1891 return AFPERR_ACCESS;
1895 return AFPERR_PARAM;
1899 /* directories are bad */
1900 if (S_ISDIR(path.st.st_mode)) {
1901 /* OS9 and OSX don't return the same error code */
1902 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1905 memcpy(&bitmap, ibuf, sizeof(bitmap));
1906 bitmap = ntohs( bitmap );
1907 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1911 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1912 rbuf + sizeof(bitmap), &buflen))) {
1915 *rbuflen = buflen + sizeof(bitmap);
1916 memcpy(rbuf, ibuf, sizeof(bitmap));
1921 /* ------------------------------ */
1922 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1932 static char buffer[12 + MAXPATHLEN + 1];
1933 int len = 12 + MAXPATHLEN + 1;
1938 memcpy(&vid, ibuf, sizeof(vid));
1939 ibuf += sizeof(vid);
1941 if (NULL == ( vol = getvolbyvid( vid )) ) {
1942 return( AFPERR_PARAM);
1945 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1949 if (vol->v_flags & AFPVOL_RO)
1950 return AFPERR_VLOCK;
1952 memcpy(&id, ibuf, sizeof( id ));
1956 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1960 if (NULL == ( dir = dirlookup( vol, id )) ) {
1961 if (afp_errno == AFPERR_NOOBJ) {
1965 return( AFPERR_PARAM );
1969 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1973 return AFPERR_ACCESS;
1978 /* still try to delete the id */
1982 return AFPERR_PARAM;
1985 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1986 return AFPERR_BADTYPE;
1989 if (cnid_delete(vol->v_cdb, fileid)) {
1992 return AFPERR_VLOCK;
1995 return AFPERR_ACCESS;
1997 return AFPERR_PARAM;
2004 /* ------------------------------ */
2005 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
2009 if (path->st_errno) {
2010 switch (path->st_errno) {
2012 afp_errno = AFPERR_NOID;
2016 afp_errno = AFPERR_ACCESS;
2019 afp_errno = AFPERR_PARAM;
2024 /* we use file_access both for legacy Mac perm and
2025 * for unix privilege, rename will take care of folder perms
2027 if (file_access(path, OPENACC_WR ) < 0) {
2028 afp_errno = AFPERR_ACCESS;
2032 if ((*of = of_findname(path))) {
2033 /* reuse struct adouble so it won't break locks */
2037 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
2039 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2041 * The user must have the Read & Write privilege for both files in order to use this command.
2043 ad_close(adp, ADFLAGS_HF);
2044 afp_errno = AFPERR_ACCESS;
2051 #define APPLETEMP ".AppleTempXXXXXX"
2053 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2055 struct stat srcst, destst;
2057 struct dir *dir, *sdir;
2058 char *spath, temp[17], *p;
2059 char *supath, *upath;
2064 struct adouble *adsp = NULL;
2065 struct adouble *addp = NULL;
2066 struct ofork *s_of = NULL;
2067 struct ofork *d_of = NULL;
2080 memcpy(&vid, ibuf, sizeof(vid));
2081 ibuf += sizeof(vid);
2083 if (NULL == ( vol = getvolbyvid( vid )) ) {
2084 return( AFPERR_PARAM);
2087 if ((vol->v_flags & AFPVOL_RO))
2088 return AFPERR_VLOCK;
2090 /* source and destination dids */
2091 memcpy(&sid, ibuf, sizeof(sid));
2092 ibuf += sizeof(sid);
2093 memcpy(&did, ibuf, sizeof(did));
2094 ibuf += sizeof(did);
2097 if (NULL == (dir = dirlookup( vol, sid )) ) {
2098 return afp_errno; /* was AFPERR_PARAM */
2101 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2102 return get_afp_errno(AFPERR_NOOBJ);
2105 if ( path_isadir(path) ) {
2106 return AFPERR_BADTYPE; /* it's a dir */
2109 /* save some stuff */
2112 spath = obj->oldtmp;
2113 supath = obj->newtmp;
2114 strcpy(spath, path->m_name);
2115 strcpy(supath, path->u_name); /* this is for the cnid changing */
2116 p = absupath( vol, sdir, supath);
2118 /* pathname too long */
2119 return AFPERR_PARAM ;
2123 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2127 /* ***** from here we may have resource fork open **** */
2129 /* look for the source cnid. if it doesn't exist, don't worry about
2131 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2133 if (NULL == ( dir = dirlookup( vol, did )) ) {
2134 err = afp_errno; /* was AFPERR_PARAM */
2135 goto err_exchangefile;
2138 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2139 err = get_afp_errno(AFPERR_NOOBJ);
2140 goto err_exchangefile;
2143 if ( path_isadir(path) ) {
2144 err = AFPERR_BADTYPE;
2145 goto err_exchangefile;
2148 /* FPExchangeFiles is the only call that can return the SameObj
2150 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2151 err = AFPERR_SAMEOBJ;
2152 goto err_exchangefile;
2156 if (!(addp = find_adouble( path, &d_of, &add))) {
2158 goto err_exchangefile;
2162 /* they are not on the same device and at least one is open
2163 * FIXME broken for for crossdev and adouble v2
2166 crossdev = (srcst.st_dev != destst.st_dev);
2167 if (/* (d_of || s_of) && */ crossdev) {
2169 goto err_exchangefile;
2172 /* look for destination id. */
2173 upath = path->u_name;
2174 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2176 /* construct a temp name.
2177 * NOTE: the temp file will be in the dest file's directory. it
2178 * will also be inaccessible from AFP. */
2179 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2180 if (!mktemp(temp)) {
2182 goto err_exchangefile;
2186 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2187 ad_close(adsp, ADFLAGS_HF);
2188 ad_close(addp, ADFLAGS_HF);
2191 /* now, quickly rename the file. we error if we can't. */
2192 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2193 goto err_exchangefile;
2194 of_rename(vol, s_of, sdir, spath, curdir, temp);
2196 /* rename destination to source */
2197 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2198 goto err_src_to_tmp;
2199 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2201 /* rename temp to destination */
2202 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2203 goto err_dest_to_src;
2204 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2206 /* id's need switching. src -> dest and dest -> src.
2207 * we need to re-stat() if it was a cross device copy.
2210 cnid_delete(vol->v_cdb, sid);
2212 cnid_delete(vol->v_cdb, did);
2214 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2215 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2217 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2218 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2223 err = AFPERR_ACCESS;
2228 goto err_temp_to_dest;
2231 /* here we need to reopen if crossdev */
2232 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2237 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2242 /* change perms, src gets dest perm and vice versa */
2247 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2248 err = AFP_OK; /* ignore error */
2249 goto err_temp_to_dest;
2253 * we need to exchange ACL entries as well
2255 /* exchange_acls(vol, p, upath); */
2260 path->m_name = NULL;
2261 path->u_name = upath;
2263 setfilunixmode(vol, path, destst.st_mode);
2264 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2271 setfilunixmode(vol, path, srcst.st_mode);
2272 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2274 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2275 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2280 goto err_exchangefile;
2282 /* all this stuff is so that we can unwind a failed operation
2285 /* rename dest to temp */
2286 renamefile(vol, -1, upath, temp, temp, adsp);
2287 of_rename(vol, s_of, curdir, upath, curdir, temp);
2290 /* rename source back to dest */
2291 renamefile(vol, -1, p, upath, path->m_name, addp);
2292 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2295 /* rename temp back to source */
2296 renamefile(vol, -1, temp, p, spath, adsp);
2297 of_rename(vol, s_of, curdir, temp, sdir, spath);
2300 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2301 ad_close(adsp, ADFLAGS_HF);
2303 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2304 ad_close(addp, ADFLAGS_HF);
2308 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2309 (void)dir_remove(vol, cached);
2310 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2311 (void)dir_remove(vol, cached);