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>
27 #include <atalk/netatalk_conf.h>
28 #include <atalk/spotlight.h>
30 #include "directory.h"
39 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
40 * field bytes subfield bytes
43 * ioFlFndrInfo 16 -> type 4 type field
44 * creator 4 creator field
45 * flags 2 finder flags:
47 * location 4 location in window
48 * folder 2 window that contains file
50 * ioFlXFndrInfo 16 -> iconID 2 icon id
52 * script 1 script system
54 * commentID 2 comment id
55 * putawayID 4 home directory id
58 const u_char ufinderi[ADEDLEN_FINDERI] = {
59 0, 0, 0, 0, 0, 0, 0, 0,
60 1, 0, 0, 0, 0, 0, 0, 0,
61 0, 0, 0, 0, 0, 0, 0, 0,
62 0, 0, 0, 0, 0, 0, 0, 0
65 static const u_char old_ufinderi[] = {
66 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
69 /* ----------------------
71 static int default_type(void *finder)
73 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
78 /* FIXME path : unix or mac name ? (for now it's unix name ) */
79 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
82 void *ad_finder = NULL;
86 ad_finder = ad_entry(adp, ADEID_FINDERI);
89 memcpy(data, ad_finder, ADEDLEN_FINDERI);
91 if (default_type(ad_finder))
95 memcpy(data, ufinderi, ADEDLEN_FINDERI);
96 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
99 ashort = htons(FINDERINFO_INVISIBLE);
100 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
104 if (islink && !vol_syml_opt(vol)) {
106 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
107 linkflag |= htons(FINDERINFO_ISALIAS);
108 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
109 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
110 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));
122 /* ---------------------
124 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, uint32_t utf8)
129 aint = strlen( name );
133 if (utf8_encoding(vol->v_obj)) {
134 /* but name is an utf8 mac name */
137 /* global static variable... */
139 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
148 if (aint > MACFILELEN)
155 if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
156 aint = UTF8FILELEN_EARLY;
158 utf8 = vol->v_kTextEncoding;
159 memcpy(data, &utf8, sizeof(utf8));
160 data += sizeof(utf8);
163 memcpy(data, &temp, sizeof(temp));
164 data += sizeof(temp);
167 memcpy( data, src, aint );
177 * FIXME: PDINFO is UTF8 and doesn't need adp
179 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
180 (1 << FILPBIT_CDATE) |\
181 (1 << FILPBIT_MDATE) |\
182 (1 << FILPBIT_BDATE) |\
183 (1 << FILPBIT_FINFO) |\
184 (1 << FILPBIT_RFLEN) |\
185 (1 << FILPBIT_EXTRFLEN) |\
186 (1 << FILPBIT_PDINFO) |\
187 (1 << FILPBIT_FNUM) |\
188 (1 << FILPBIT_UNIXPR)))
191 * @brief Get CNID for did/upath args both from database and adouble file
193 * 1. Get the objects CNID as stored in its adouble file
194 * 2. Get the objects CNID from the database
195 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
196 * 4. In case 2 and 3 differ, store 3 in the adouble file
198 * @param vol (rw) volume
199 * @param adp (rw) adouble struct of object upath, might be NULL
200 * @param st (r) stat of upath, must NOT be NULL
201 * @param did (r) parent CNID of upath
202 * @param upath (r) name of object
203 * @param len (r) strlen of upath
205 uint32_t get_id(struct vol *vol,
207 const struct stat *st,
212 static int first = 1; /* mark if this func is called the first time */
214 uint32_t dbcnid = CNID_INVALID;
217 if (vol->v_cdb != NULL) {
218 /* prime aint with what we think is the cnid, set did to zero for
219 catching moved files */
220 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
222 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
223 /* Throw errors if cnid_add fails. */
224 if (dbcnid == CNID_INVALID) {
226 case CNID_ERR_CLOSE: /* the db is closed */
229 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
230 afp_errno = AFPERR_PARAM;
233 afp_errno = AFPERR_PARAM;
236 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
237 /* we have to do it here for "dbd" because it uses "lazy opening" */
238 /* In order to not end in a loop somehow with goto restart below */
240 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
241 cnid_close(vol->v_cdb);
242 free(vol->v_cnidscheme);
243 vol->v_cnidscheme = strdup("tdb");
245 int flags = CNID_FLAG_MEMORY;
246 if ((vol->v_flags & AFPVOL_NODEV)) {
247 flags |= CNID_FLAG_NODEV;
249 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
251 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
253 if (!(vol->v_flags & AFPVOL_TM)) {
254 vol->v_flags |= AFPVOL_RO;
255 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
256 "Check server messages for details. Switching to read-only mode.");
257 kill(getpid(), SIGUSR2);
259 goto restart; /* now try again with the temp CNID db */
261 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
262 "Check server messages for details, can't recover from this state!");
265 afp_errno = AFPERR_MISC;
269 else if (adp && adcnid && (adcnid != dbcnid)) { /* 4 */
270 /* Update the ressource fork. For a folder adp is always null */
271 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
272 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
273 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
274 if (ad_flush(adp) != 0)
275 LOG(log_error, logtype_afpd, "get_id(\"%s\"): can't flush", fullpathname(upath));
285 /* -------------------------- */
286 int getmetadata(const AFPObj *obj,
289 struct path *path, struct dir *dir,
290 char *buf, size_t *buflen, struct adouble *adp)
292 char *data, *l_nameoff = NULL, *upath;
293 char *utf_nameoff = NULL;
298 u_char achar, fdType[4];
303 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
305 upath = path->u_name;
309 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
310 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding(obj)) /* FIXME should be m_name utf8 filename */
311 || (bitmap & (1 << FILPBIT_FNUM))) {
314 struct dir *cachedfile;
315 int len = strlen(upath);
316 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
317 id = cachedfile->d_did;
319 id = get_id(vol, adp, st, dir->d_did, upath, len);
321 /* Add it to the cache */
322 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
323 ntohl(dir->d_did), upath, ntohl(id));
325 /* Get macname from unixname first */
326 if (path->m_name == NULL) {
327 if ((path->m_name = utompath(vol, upath, id, utf8_encoding(obj))) == NULL) {
328 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
334 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
335 || (bconchar(fullpath, '/') != BSTR_OK)
336 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
337 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
341 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
342 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
346 if ((dircache_add(vol, cachedfile)) != 0) {
347 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
355 if (id == CNID_INVALID)
359 path->m_name = utompath(vol, upath, id, utf8_encoding(vol->v_obj));
362 while ( bitmap != 0 ) {
363 while (( bitmap & 1 ) == 0 ) {
371 ad_getattr(adp, &ashort);
372 } else if (vol_inv_dots(vol) && *upath == '.') {
373 ashort = htons(ATTRBIT_INVISIBLE);
377 /* FIXME do we want a visual clue if the file is read only
380 accessmode(vol, ".", &ma, dir , NULL);
381 if ((ma.ma_user & AR_UWRITE)) {
382 accessmode(vol, upath, &ma, dir , st);
383 if (!(ma.ma_user & AR_UWRITE)) {
384 ashort |= htons(ATTRBIT_NOWRITE);
388 memcpy(data, &ashort, sizeof( ashort ));
389 data += sizeof( ashort );
390 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
391 path->u_name, ntohs(ashort));
395 memcpy(data, &dir->d_did, sizeof( uint32_t ));
396 data += sizeof( uint32_t );
397 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
398 path->u_name, ntohl(dir->d_did));
402 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
403 aint = AD_DATE_FROM_UNIX(st->st_mtime);
404 memcpy(data, &aint, sizeof( aint ));
405 data += sizeof( aint );
409 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
410 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
411 aint = AD_DATE_FROM_UNIX(st->st_mtime);
414 aint = AD_DATE_FROM_UNIX(st->st_mtime);
416 memcpy(data, &aint, sizeof( int ));
417 data += sizeof( int );
421 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
422 aint = AD_DATE_START;
423 memcpy(data, &aint, sizeof( int ));
424 data += sizeof( int );
428 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
429 data += ADEDLEN_FINDERI;
434 data += sizeof( uint16_t );
438 memset(data, 0, sizeof(uint16_t));
439 data += sizeof( uint16_t );
443 memcpy(data, &id, sizeof( id ));
444 data += sizeof( id );
445 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
446 path->u_name, ntohl(id));
450 if (st->st_size > 0xffffffff)
453 aint = htonl( st->st_size );
454 memcpy(data, &aint, sizeof( aint ));
455 data += sizeof( aint );
460 if (adp->ad_rlen > 0xffffffff)
463 aint = htonl( adp->ad_rlen);
467 memcpy(data, &aint, sizeof( aint ));
468 data += sizeof( aint );
471 /* Current client needs ProDOS info block for this file.
472 Use simple heuristic and let the Mac "type" string tell
473 us what the PD file code should be. Everything gets a
474 subtype of 0x0000 unless the original value was hashed
475 to "pXYZ" when we created it. See IA, Ver 2.
476 <shirsch@adelphia.net> */
477 case FILPBIT_PDINFO :
478 if (obj->afp_version >= 30) { /* UTF8 name */
479 utf8 = kTextEncodingUTF8;
481 data += sizeof( uint16_t );
483 memcpy(data, &aint, sizeof( aint ));
484 data += sizeof( aint );
488 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
490 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
494 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
498 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
502 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
506 else if ( fdType[0] == 'p' ) {
508 ashort = (fdType[2] * 256) + fdType[3];
522 memcpy(data, &ashort, sizeof( ashort ));
523 data += sizeof( ashort );
524 memset(data, 0, sizeof( ashort ));
525 data += sizeof( ashort );
528 case FILPBIT_EXTDFLEN:
529 aint = htonl(st->st_size >> 32);
530 memcpy(data, &aint, sizeof( aint ));
531 data += sizeof( aint );
532 aint = htonl(st->st_size);
533 memcpy(data, &aint, sizeof( aint ));
534 data += sizeof( aint );
536 case FILPBIT_EXTRFLEN:
539 aint = htonl(adp->ad_rlen >> 32);
540 memcpy(data, &aint, sizeof( aint ));
541 data += sizeof( aint );
543 aint = htonl(adp->ad_rlen);
544 memcpy(data, &aint, sizeof( aint ));
545 data += sizeof( aint );
547 case FILPBIT_UNIXPR :
548 /* accessmode may change st_mode with ACLs */
549 accessmode(obj, vol, upath, &ma, dir , st);
551 aint = htonl(st->st_uid);
552 memcpy( data, &aint, sizeof( aint ));
553 data += sizeof( aint );
554 aint = htonl(st->st_gid);
555 memcpy( data, &aint, sizeof( aint ));
556 data += sizeof( aint );
559 type == slnk indicates an OSX style symlink,
560 we have to add S_IFLNK to the mode, otherwise
561 10.3 clients freak out. */
565 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
566 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
572 memcpy( data, &aint, sizeof( aint ));
573 data += sizeof( aint );
575 *data++ = ma.ma_user;
576 *data++ = ma.ma_world;
577 *data++ = ma.ma_group;
578 *data++ = ma.ma_owner;
582 return( AFPERR_BITMAP );
588 ashort = htons( data - buf );
589 memcpy(l_nameoff, &ashort, sizeof( ashort ));
590 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
593 ashort = htons( data - buf );
594 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
595 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
597 *buflen = data - buf;
601 /* ----------------------- */
602 int getfilparams(const AFPObj *obj, struct vol *vol, uint16_t bitmap, struct path *path,
603 struct dir *dir, char *buf, size_t *buflen, int in_enumerate)
605 struct adouble ad, *adp;
608 int flags; /* uninitialized ok */
610 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
612 opened = PARAM_NEED_ADP(bitmap);
618 * Dont check for and resturn open fork attributes when enumerating
619 * This saves a lot of syscalls, the client will hopefully only use the result
620 * in FPGetFileParms where we return the correct value
622 flags = (!in_enumerate &&(bitmap & (1 << FILPBIT_ATTR))) ? ADFLAGS_CHECK_OF : 0;
624 adp = of_ad(vol, path, &ad);
625 upath = path->u_name;
627 if ( ad_metadata( upath, flags, adp) < 0 ) {
630 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
631 upath, strerror(errno));
632 return AFPERR_ACCESS;
634 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
643 rc = getmetadata(obj, vol, bitmap, path, dir, buf, buflen, adp);
646 ad_close(adp, ADFLAGS_HF | flags);
651 /* ----------------------------- */
652 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
657 struct ofork *of = NULL;
659 int creatf, did, openf, retvalue = AFP_OK;
665 creatf = (unsigned char) *ibuf++;
667 memcpy(&vid, ibuf, sizeof( vid ));
668 ibuf += sizeof( vid );
670 if (NULL == ( vol = getvolbyvid( vid )) )
671 return( AFPERR_PARAM );
673 if (vol->v_flags & AFPVOL_RO)
676 memcpy(&did, ibuf, sizeof( did));
677 ibuf += sizeof( did );
679 if (NULL == ( dir = dirlookup( vol, did )) )
682 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
683 return get_afp_errno(AFPERR_PARAM);
684 if ( *s_path->m_name == '\0' )
685 return( AFPERR_BADTYPE );
687 upath = s_path->u_name;
690 /* if upath is deleted we already in trouble anyway */
691 if ((of = of_findname(vol, s_path))) {
699 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
701 /* on a soft create, if the file is open then ad_open won't fail
702 because open syscall is not called */
703 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
705 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
709 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
710 return ( AFPERR_NOOBJ );
712 return( AFPERR_EXIST );
714 return( AFPERR_ACCESS );
717 LOG(log_info, logtype_afpd, "afp_createfile: DISK FULL");
718 return( AFPERR_DFULL );
720 return( AFPERR_PARAM );
723 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
724 /* FIXME with hard create on an existing file, we already
725 * corrupted the data file.
727 netatalk_unlink( upath );
728 ad_close( &ad, ADFLAGS_DF );
729 return AFPERR_ACCESS;
732 path = s_path->m_name;
733 ad_setname(&ad, path);
736 if (lstat(upath, &st) != 0) {
737 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
738 upath, strerror(errno));
739 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
744 if ((id = get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath))) == CNID_INVALID) {
745 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
746 goto createfile_iderr;
748 (void)ad_setid(&ad, st.st_dev, st.st_ino, id, dir->d_did, vol->v_stamp);
752 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
753 fce_register(FCE_FILE_CREATE, fullpathname(upath), NULL, fce_file);
757 setvoltime(obj, vol );
762 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
768 uint16_t vid, bitmap;
773 memcpy(&vid, ibuf, sizeof( vid ));
774 ibuf += sizeof( vid );
775 if (NULL == ( vol = getvolbyvid( vid )) ) {
776 return( AFPERR_PARAM );
779 if (vol->v_flags & AFPVOL_RO)
782 memcpy(&did, ibuf, sizeof( did ));
783 ibuf += sizeof( did );
784 if (NULL == ( dir = dirlookup( vol, did )) ) {
785 return afp_errno; /* was AFPERR_NOOBJ */
788 memcpy(&bitmap, ibuf, sizeof( bitmap ));
789 bitmap = ntohs( bitmap );
790 ibuf += sizeof( bitmap );
792 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
793 return get_afp_errno(AFPERR_PARAM);
796 if (path_isadir(s_path)) {
797 return( AFPERR_BADTYPE ); /* it's a directory */
800 if ( s_path->st_errno != 0 ) {
801 return( AFPERR_NOOBJ );
804 if ((u_long)ibuf & 1 ) {
808 if (AFP_OK == ( rc = setfilparams(obj, vol, s_path, bitmap, ibuf )) ) {
809 setvoltime(obj, vol );
816 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
819 extern struct path Cur_Path;
821 int setfilparams(const AFPObj *obj, struct vol *vol,
822 struct path *path, uint16_t f_bitmap, char *buf )
824 struct adouble ad, *adp;
826 int bit, isad = 1, err = AFP_OK;
828 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
829 uint16_t ashort, bshort, oshort;
832 uint16_t upriv_bit = 0;
834 int change_mdate = 0;
835 int change_parent_mdate = 0;
840 uint16_t bitmap = f_bitmap;
841 uint32_t cdate,bdate;
842 u_char finder_buf[32];
843 int symlinked = S_ISLNK(path->st.st_mode);
846 char symbuf[MAXPATHLEN+1];
849 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
852 adp = of_ad(vol, path, &ad);
853 upath = path->u_name;
855 if (!vol_unix_priv(vol) && check_access(obj, vol, upath, OPENACC_WR ) < 0) {
856 return AFPERR_ACCESS;
859 /* with unix priv maybe we have to change adouble file priv first */
861 while ( bitmap != 0 ) {
862 while (( bitmap & 1 ) == 0 ) {
869 memcpy(&ashort, buf, sizeof( ashort ));
870 buf += sizeof( ashort );
874 memcpy(&cdate, buf, sizeof(cdate));
875 buf += sizeof( cdate );
878 memcpy(&newdate, buf, sizeof( newdate ));
879 buf += sizeof( newdate );
883 memcpy(&bdate, buf, sizeof( bdate));
884 buf += sizeof( bdate );
888 if (memcmp(buf,"slnkrhap",8) == 0
889 && !(S_ISLNK(path->st.st_mode))
890 && !(vol->v_flags & AFPVOL_FOLLOWSYM)) {
891 /* request to turn this into a symlink */
892 if ((fp = open(path->u_name, O_RDONLY)) == -1) {
894 goto setfilparam_done;
896 len = read(fp, symbuf, MAXPATHLEN);
900 goto setfilparam_done;
902 if (unlink(path->u_name) != 0) {
904 goto setfilparam_done;
907 if (symlink(symbuf, path->u_name) != 0) {
909 goto setfilparam_done;
914 memcpy(finder_buf, buf, 32 );
917 case FILPBIT_UNIXPR :
918 if (!vol_unix_priv(vol)) {
919 /* this volume doesn't use unix priv */
925 change_parent_mdate = 1;
927 memcpy( &aint, buf, sizeof( aint ));
928 f_uid = ntohl (aint);
929 buf += sizeof( aint );
930 memcpy( &aint, buf, sizeof( aint ));
931 f_gid = ntohl (aint);
932 buf += sizeof( aint );
933 setfilowner(vol, f_uid, f_gid, path);
935 memcpy( &upriv, buf, sizeof( upriv ));
936 buf += sizeof( upriv );
937 upriv = ntohl (upriv);
938 if ((upriv & S_IWUSR)) {
939 setfilunixmode(vol, path, upriv);
946 case FILPBIT_PDINFO :
947 if (obj->afp_version < 30) { /* else it's UTF8 name */
950 /* Keep special case to support crlf translations */
951 if ((unsigned int) achar == 0x04) {
952 fdType = (u_char *)"TEXT";
955 xyy[0] = ( u_char ) 'p';
966 /* break while loop */
975 /* second try with adouble open
977 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
978 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
980 * For some things, we don't need an adouble header:
981 * - change of modification date
982 * - UNIX privs (Bug-ID #2863424)
984 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
985 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
986 return AFPERR_ACCESS;
988 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
990 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
991 ad_setname(adp, path->m_name);
993 if ((id = get_id(vol, adp, &path->st, curdir->d_did, upath, strlen(upath))) == CNID_INVALID) {
994 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
997 (void)ad_setid(adp, path->st.st_dev, path->st.st_ino, id, curdir->d_did, vol->v_stamp);
1002 while ( bitmap != 0 ) {
1003 while (( bitmap & 1 ) == 0 ) {
1010 ad_getattr(adp, &bshort);
1012 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1013 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1017 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1018 change_parent_mdate = 1;
1019 ad_setattr(adp, bshort);
1021 case FILPBIT_CDATE :
1022 ad_setdate(adp, AD_DATE_CREATE, cdate);
1024 case FILPBIT_MDATE :
1026 case FILPBIT_BDATE :
1027 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1029 case FILPBIT_FINFO :
1030 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1032 ((em = getextmap( path->m_name )) &&
1033 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1034 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1035 || ((em = getdefextmap()) &&
1036 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1037 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1039 memcpy(finder_buf, ufinderi, 8 );
1041 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1043 case FILPBIT_UNIXPR :
1045 setfilunixmode(vol, path, upriv);
1048 case FILPBIT_PDINFO :
1049 if (obj->afp_version < 30) { /* else it's UTF8 name */
1050 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1051 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1056 err = AFPERR_BITMAP;
1057 goto setfilparam_done;
1064 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1065 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1069 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1070 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1076 ad_close(adp, ADFLAGS_HF);
1079 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1080 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1081 bitmap = 1<<FILPBIT_MDATE;
1082 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1086 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1092 * renamefile and copyfile take the old and new unix pathnames
1093 * and the new mac name.
1095 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1096 * passing -1 means this is not used, src path is a full path
1097 * src the source path
1098 * dst the dest filename in current dir
1099 * newname the dest mac name
1100 * adp adouble struct of src file, if open, or & zeroed one
1103 int renamefile(struct vol *vol, struct dir *ddir, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1107 LOG(log_debug, logtype_afpd,
1108 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1110 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1113 return( AFPERR_NOOBJ );
1116 return( AFPERR_ACCESS );
1118 return AFPERR_VLOCK;
1119 case EXDEV : /* Cross device move -- try copy */
1120 /* NOTE: with open file it's an error because after the copy we will
1121 * get two files, it's fixable for our process (eg reopen the new file, get the
1122 * locks, and so on. But it doesn't solve the case with a second process
1124 if (adp->ad_open_forks) {
1125 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1126 return AFPERR_OLOCK; /* little lie */
1128 if (AFP_OK != ( rc = copyfile(vol, vol, ddir, sdir_fd, src, dst, newname, NULL )) ) {
1129 /* on error copyfile delete dest */
1132 return deletefile(vol, sdir_fd, src, 0);
1134 return( AFPERR_PARAM );
1138 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1142 /* try to undo the data fork rename,
1143 * we know we are on the same device
1146 unix_rename(-1, dst, sdir_fd, src );
1147 /* return the first error */
1150 return AFPERR_NOOBJ;
1153 return AFPERR_ACCESS ;
1155 return AFPERR_VLOCK;
1157 return AFPERR_PARAM ;
1162 /* don't care if we can't open the newly renamed ressource fork */
1163 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1164 ad_setname(adp, newname);
1166 ad_close( adp, ADFLAGS_HF );
1173 convert a Mac long name to an utf8 name,
1175 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1179 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1185 /* ---------------- */
1186 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1193 if ( type != 2 && !(vol->v_obj->afp_version >= 30 && type == 3) ) {
1199 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1200 if (vol->v_obj->afp_version >= 30) {
1201 /* convert it to UTF8
1203 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1207 strncpy( newname, ibuf, plen );
1208 newname[ plen ] = '\0';
1210 if (strlen(newname) != plen) {
1211 /* there's \0 in newname, e.g. it's a pathname not
1219 memcpy(&hint, ibuf, sizeof(hint));
1220 ibuf += sizeof(hint);
1222 memcpy(&len16, ibuf, sizeof(len16));
1223 ibuf += sizeof(len16);
1224 plen = ntohs(len16);
1227 if (plen > AFPOBJ_TMPSIZ) {
1230 strncpy( newname, ibuf, plen );
1231 newname[ plen ] = '\0';
1232 if (strlen(newname) != plen) {
1241 /* -----------------------------------
1243 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1245 struct vol *s_vol, *d_vol;
1247 char *newname, *p, *upath;
1248 struct path *s_path;
1249 uint32_t sdid, ddid;
1250 int err, retvalue = AFP_OK;
1251 uint16_t svid, dvid;
1253 struct adouble ad, *adp;
1259 memcpy(&svid, ibuf, sizeof( svid ));
1260 ibuf += sizeof( svid );
1261 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1262 return( AFPERR_PARAM );
1265 memcpy(&sdid, ibuf, sizeof( sdid ));
1266 ibuf += sizeof( sdid );
1267 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1271 memcpy(&dvid, ibuf, sizeof( dvid ));
1272 ibuf += sizeof( dvid );
1273 memcpy(&ddid, ibuf, sizeof( ddid ));
1274 ibuf += sizeof( ddid );
1276 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1277 return get_afp_errno(AFPERR_PARAM);
1279 if ( path_isadir(s_path) ) {
1280 return( AFPERR_BADTYPE );
1283 /* don't allow copies when the file is open.
1284 * XXX: the spec only calls for read/deny write access.
1285 * however, copyfile doesn't have any of that info,
1286 * and locks need to stay coherent. as a result,
1287 * we just balk if the file is opened already. */
1289 adp = of_ad(s_vol, s_path, &ad);
1291 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1292 return AFPERR_DENYCONF;
1294 #ifdef HAVE_FSHARE_T
1296 shmd.f_access = F_RDACC;
1297 shmd.f_deny = F_NODNY;
1298 if (fcntl(ad_data_fileno(adp), F_SHARE, &shmd) != 0) {
1299 retvalue = AFPERR_DENYCONF;
1302 if (AD_RSRC_OPEN(adp) && fcntl(ad_reso_fileno(adp), F_SHARE, &shmd) != 0) {
1303 retvalue = AFPERR_DENYCONF;
1307 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1308 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1311 retvalue = AFPERR_DENYCONF;
1315 newname = obj->newtmp;
1316 strcpy( newname, s_path->m_name );
1318 p = ctoupath( s_vol, curdir, newname );
1320 retvalue = AFPERR_PARAM;
1324 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1325 retvalue = AFPERR_PARAM;
1329 if (d_vol->v_flags & AFPVOL_RO) {
1330 retvalue = AFPERR_VLOCK;
1334 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1335 retvalue = afp_errno;
1339 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1340 retvalue = get_afp_errno(AFPERR_NOOBJ);
1344 if ( *s_path->m_name != '\0' ) {
1345 retvalue =path_error(s_path, AFPERR_NOOBJ);
1349 /* one of the handful of places that knows about the path type */
1350 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1351 retvalue = AFPERR_PARAM;
1354 /* newname is always only a filename so curdir *is* its
1357 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding(d_vol->v_obj)))) {
1358 retvalue =AFPERR_PARAM;
1362 if ( (err = copyfile(s_vol, d_vol, curdir, -1, p, upath , newname, adp)) < 0 ) {
1368 setvoltime(obj, d_vol );
1371 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1375 /* ----------------------------------
1376 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1377 * because we are doing it elsewhere.
1378 * currently if newname is NULL then adp is NULL.
1380 int copyfile(struct vol *s_vol,
1387 struct adouble *adp)
1389 struct adouble ads, add;
1396 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1397 sfd, src, dst, newname);
1400 ad_init(&ads, s_vol);
1404 adflags = ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RF | ADFLAGS_NORF;
1406 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1411 if (!AD_META_OPEN(adp))
1412 /* no resource fork, don't create one for dst file */
1413 adflags &= ~ADFLAGS_HF;
1415 if (!AD_RSRC_OPEN(adp))
1416 /* no resource fork, don't create one for dst file */
1417 adflags &= ~ADFLAGS_RF;
1419 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1421 if (stat_result < 0) {
1422 /* unlikely but if fstat fails, the default file mode will be 0666. */
1423 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1426 ad_init(&add, d_vol);
1427 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1429 ad_close( adp, adflags );
1430 if (EEXIST != ret_err) {
1431 deletefile(d_vol, -1, dst, 0);
1434 return AFPERR_EXIST;
1437 if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1438 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1441 if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1442 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1447 if (AD_META_OPEN(&add)) {
1448 if (AD_META_OPEN(adp))
1449 ad_copy_header(&add, adp);
1450 ad_setname(&add, dst);
1453 if (fstat(ad_meta_fileno(&add), &stdest) != 0) {
1457 if ((id = get_id(d_vol, &add, &stdest, d_dir->d_did, dst, strlen(dst))) == CNID_INVALID) {
1461 (void)ad_setid(&add, stdest.st_dev, stdest.st_ino, id, d_dir->d_did, d_vol->v_stamp);
1466 ad_close( adp, adflags );
1468 if (ad_close( &add, adflags ) <0) {
1473 deletefile(d_vol, -1, dst, 0);
1475 else if (stat_result == 0) {
1476 /* set dest modification date to src date */
1479 ut.actime = ut.modtime = st.st_mtime;
1481 /* FIXME netatalk doesn't use resource fork file date
1482 * but maybe we should set its modtime too.
1487 switch ( ret_err ) {
1493 LOG(log_info, logtype_afpd, "copyfile: DISK FULL");
1494 return AFPERR_DFULL;
1496 return AFPERR_NOOBJ;
1498 return AFPERR_ACCESS;
1500 return AFPERR_VLOCK;
1502 return AFPERR_PARAM;
1506 /* -----------------------------------
1507 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1508 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1510 when deletefile is called we don't have lock on it, file is closed (for us)
1511 untrue if called by renamefile
1513 ad_open always try to open file RDWR first and ad_lock takes care of
1514 WRITE lock on read only file.
1517 static int check_attrib(struct adouble *adp)
1519 uint16_t bshort = 0;
1521 ad_getattr(adp, &bshort);
1523 * Does kFPDeleteInhibitBit (bit 8) set?
1525 if ((bshort & htons(ATTRBIT_NODELETE))) {
1526 return AFPERR_OLOCK;
1528 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1534 * dirfd can be used for unlinkat semantics
1536 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1539 struct adouble *adp = NULL;
1540 int adflags, err = AFP_OK;
1543 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1547 /* was EACCESS error try to get only metadata */
1548 /* we never want to create a resource fork here, we are going to delete it
1549 * moreover sometimes deletefile is called with a no existent file and
1550 * ad_open would create a 0 byte resource fork
1552 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1553 if ((err = check_attrib(&ad))) {
1554 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1561 /* try to open both forks at once */
1562 adflags = ADFLAGS_DF;
1563 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1568 case EACCES: /* maybe it's a file with no write mode for us */
1569 break; /* was return AFPERR_ACCESS;*/
1582 if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1583 adflags |= ADFLAGS_RF;
1584 /* FIXME we have a pb here because we want to know if a file is open
1585 * there's a 'priority inversion' if you can't open the ressource fork RW
1586 * you can delete it if it's open because you can't get a write lock.
1588 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1591 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1593 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1599 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1600 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1602 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1604 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1605 cnid_delete(vol->v_cdb, id);
1611 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1614 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1619 /* ------------------------------------ */
1620 /* return a file id */
1621 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1630 struct path *s_path;
1636 memcpy(&vid, ibuf, sizeof(vid));
1637 ibuf += sizeof(vid);
1639 if (NULL == ( vol = getvolbyvid( vid )) ) {
1640 return( AFPERR_PARAM);
1643 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1647 if (vol->v_flags & AFPVOL_RO)
1648 return AFPERR_VLOCK;
1650 memcpy(&did, ibuf, sizeof( did ));
1651 ibuf += sizeof(did);
1653 if (NULL == ( dir = dirlookup( vol, did )) ) {
1654 return afp_errno; /* was AFPERR_PARAM */
1657 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1658 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1661 if ( path_isadir(s_path) ) {
1662 return( AFPERR_BADTYPE );
1665 upath = s_path->u_name;
1666 switch (s_path->st_errno) {
1668 break; /* success */
1671 return AFPERR_ACCESS;
1673 return AFPERR_NOOBJ;
1675 return AFPERR_PARAM;
1678 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1679 memcpy(rbuf, &id, sizeof(id));
1680 *rbuflen = sizeof(id);
1681 return AFPERR_EXISTID;
1684 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1685 memcpy(rbuf, &id, sizeof(id));
1686 *rbuflen = sizeof(id);
1693 /* ------------------------------- */
1699 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1702 struct reenum *param = data;
1703 struct vol *vol = param->vol;
1704 cnid_t did = param->did;
1707 if (ostat(de->d_name, &path.st, vol_syml_opt(vol)) < 0)
1710 /* update or add to cnid */
1711 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1716 /* --------------------
1717 * Ok the db is out of synch with the dir.
1718 * but if it's a deleted file we don't want to do it again and again.
1721 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1727 if (vol->v_cdb == NULL) {
1731 /* FIXME use of_statdir ? */
1732 if (ostat(name, &st, vol_syml_opt(vol))) {
1736 if (dirreenumerate(dir, &st)) {
1737 /* we already did it once and the dir haven't been modified */
1738 return dir->d_offcnt;
1742 data.did = dir->d_did;
1743 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1744 setdiroffcnt(curdir, &st, ret);
1745 dir->d_flags |= DIRF_CNID;
1751 /* ------------------------------
1752 resolve a file id */
1753 int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1762 uint16_t vid, bitmap;
1764 static char buffer[12 + MAXPATHLEN + 1];
1765 int len = 12 + MAXPATHLEN + 1;
1770 memcpy(&vid, ibuf, sizeof(vid));
1771 ibuf += sizeof(vid);
1773 if (NULL == ( vol = getvolbyvid( vid )) ) {
1774 return( AFPERR_PARAM);
1777 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1781 memcpy(&id, ibuf, sizeof( id ));
1786 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1790 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1791 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1794 if (NULL == ( dir = dirlookup( vol, id )) ) {
1795 return AFPERR_NOID; /* idem AFPERR_PARAM */
1797 if (movecwd(vol, dir) < 0) {
1801 return AFPERR_ACCESS;
1805 return AFPERR_PARAM;
1809 memset(&path, 0, sizeof(path));
1810 path.u_name = upath;
1811 if (of_stat(vol, &path) < 0 ) {
1813 /* with nfs and our working directory is deleted */
1814 if (errno == ESTALE) {
1818 if ( errno == ENOENT && !retry) {
1819 /* cnid db is out of sync, reenumerate the directory and update ids */
1820 reenumerate_id(vol, ".", dir);
1828 return AFPERR_ACCESS;
1832 return AFPERR_PARAM;
1836 /* directories are bad */
1837 if (S_ISDIR(path.st.st_mode)) {
1838 /* OS9 and OSX don't return the same error code */
1839 return (obj->afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1842 memcpy(&bitmap, ibuf, sizeof(bitmap));
1843 bitmap = ntohs( bitmap );
1844 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding(obj)))) {
1848 if (AFP_OK != (err = getfilparams(obj, vol, bitmap, &path , curdir,
1849 rbuf + sizeof(bitmap), &buflen, 0))) {
1852 *rbuflen = buflen + sizeof(bitmap);
1853 memcpy(rbuf, ibuf, sizeof(bitmap));
1858 /* ------------------------------ */
1859 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1869 static char buffer[12 + MAXPATHLEN + 1];
1870 int len = 12 + MAXPATHLEN + 1;
1875 memcpy(&vid, ibuf, sizeof(vid));
1876 ibuf += sizeof(vid);
1878 if (NULL == ( vol = getvolbyvid( vid )) ) {
1879 return( AFPERR_PARAM);
1882 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1886 if (vol->v_flags & AFPVOL_RO)
1887 return AFPERR_VLOCK;
1889 memcpy(&id, ibuf, sizeof( id ));
1893 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1897 if (NULL == ( dir = dirlookup( vol, id )) ) {
1898 if (afp_errno == AFPERR_NOOBJ) {
1902 return( AFPERR_PARAM );
1906 if ((movecwd(vol, dir) < 0) || (ostat(upath, &st, vol_syml_opt(vol)) < 0)) {
1910 return AFPERR_ACCESS;
1915 /* still try to delete the id */
1919 return AFPERR_PARAM;
1922 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1923 return AFPERR_BADTYPE;
1926 if (cnid_delete(vol->v_cdb, fileid)) {
1929 return AFPERR_VLOCK;
1932 return AFPERR_ACCESS;
1934 return AFPERR_PARAM;
1941 /* ------------------------------ */
1942 static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
1946 if (path->st_errno) {
1947 switch (path->st_errno) {
1949 afp_errno = AFPERR_NOID;
1953 afp_errno = AFPERR_ACCESS;
1956 afp_errno = AFPERR_PARAM;
1961 /* we use file_access both for legacy Mac perm and
1962 * for unix privilege, rename will take care of folder perms
1964 if (file_access(obj, vol, path, OPENACC_WR ) < 0) {
1965 afp_errno = AFPERR_ACCESS;
1969 if ((*of = of_findname(vol, path))) {
1970 /* reuse struct adouble so it won't break locks */
1974 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
1976 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1978 * The user must have the Read & Write privilege for both files in order to use this command.
1980 ad_close(adp, ADFLAGS_HF);
1981 afp_errno = AFPERR_ACCESS;
1988 #define APPLETEMP ".AppleTempXXXXXX"
1990 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1992 struct stat srcst, destst;
1994 struct dir *dir, *sdir;
1995 char *spath, temp[17], *p;
1996 char *supath, *upath;
2001 struct adouble *adsp = NULL;
2002 struct adouble *addp = NULL;
2003 struct ofork *s_of = NULL;
2004 struct ofork *d_of = NULL;
2014 memcpy(&vid, ibuf, sizeof(vid));
2015 ibuf += sizeof(vid);
2017 if (NULL == ( vol = getvolbyvid( vid )) ) {
2018 return( AFPERR_PARAM);
2021 if ((vol->v_flags & AFPVOL_RO))
2022 return AFPERR_VLOCK;
2024 /* source and destination dids */
2025 memcpy(&sid, ibuf, sizeof(sid));
2026 ibuf += sizeof(sid);
2027 memcpy(&did, ibuf, sizeof(did));
2028 ibuf += sizeof(did);
2031 if (NULL == (dir = dirlookup( vol, sid )) ) {
2032 return afp_errno; /* was AFPERR_PARAM */
2035 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2036 return get_afp_errno(AFPERR_NOOBJ);
2039 if ( path_isadir(path) ) {
2040 return AFPERR_BADTYPE; /* it's a dir */
2043 /* save some stuff */
2046 spath = obj->oldtmp;
2047 supath = obj->newtmp;
2048 strcpy(spath, path->m_name);
2049 strcpy(supath, path->u_name); /* this is for the cnid changing */
2050 p = absupath( vol, sdir, supath);
2052 /* pathname too long */
2053 return AFPERR_PARAM ;
2057 if (!(adsp = find_adouble(obj, vol, path, &s_of, &ads))) {
2061 /* ***** from here we may have resource fork open **** */
2063 /* look for the source cnid. if it doesn't exist, don't worry about
2065 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2067 if (NULL == ( dir = dirlookup( vol, did )) ) {
2068 err = afp_errno; /* was AFPERR_PARAM */
2069 goto err_exchangefile;
2072 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2073 err = get_afp_errno(AFPERR_NOOBJ);
2074 goto err_exchangefile;
2077 if ( path_isadir(path) ) {
2078 err = AFPERR_BADTYPE;
2079 goto err_exchangefile;
2082 /* FPExchangeFiles is the only call that can return the SameObj
2084 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2085 err = AFPERR_SAMEOBJ;
2086 goto err_exchangefile;
2090 if (!(addp = find_adouble(obj, vol, path, &d_of, &add))) {
2092 goto err_exchangefile;
2096 /* they are not on the same device and at least one is open
2097 * FIXME broken for for crossdev and adouble v2
2100 crossdev = (srcst.st_dev != destst.st_dev);
2101 if (/* (d_of || s_of) && */ crossdev) {
2103 goto err_exchangefile;
2106 /* look for destination id. */
2107 upath = path->u_name;
2108 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2110 /* construct a temp name.
2111 * NOTE: the temp file will be in the dest file's directory. it
2112 * will also be inaccessible from AFP. */
2113 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2115 if ((fd = mkstemp(temp)) == -1) {
2117 goto err_exchangefile;
2122 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2123 ad_close(adsp, ADFLAGS_HF);
2124 ad_close(addp, ADFLAGS_HF);
2127 /* now, quickly rename the file. we error if we can't. */
2128 if ((err = renamefile(vol, curdir, -1, p, temp, temp, adsp)) != AFP_OK)
2129 goto err_exchangefile;
2130 of_rename(vol, s_of, sdir, spath, curdir, temp);
2132 /* rename destination to source */
2133 if ((err = renamefile(vol, curdir, -1, upath, p, spath, addp)) != AFP_OK)
2134 goto err_src_to_tmp;
2135 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2137 /* rename temp to destination */
2138 if ((err = renamefile(vol, curdir, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2139 goto err_dest_to_src;
2140 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2142 /* id's need switching. src -> dest and dest -> src.
2143 * we need to re-stat() if it was a cross device copy.
2146 cnid_delete(vol->v_cdb, sid);
2148 cnid_delete(vol->v_cdb, did);
2150 if ((did && ( (crossdev && ostat(upath, &srcst, vol_syml_opt(vol)) < 0) ||
2151 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2153 (sid && ( (crossdev && ostat(p, &destst, vol_syml_opt(vol)) < 0) ||
2154 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2159 err = AFPERR_ACCESS;
2164 goto err_temp_to_dest;
2167 /* here we need to reopen if crossdev */
2168 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2173 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2178 /* change perms, src gets dest perm and vice versa */
2183 * we need to exchange ACL entries as well
2185 /* exchange_acls(vol, p, upath); */
2190 path->m_name = NULL;
2191 path->u_name = upath;
2193 setfilunixmode(vol, path, destst.st_mode);
2194 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2201 setfilunixmode(vol, path, srcst.st_mode);
2202 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2207 goto err_exchangefile;
2209 /* all this stuff is so that we can unwind a failed operation
2212 /* rename dest to temp */
2213 renamefile(vol, curdir, -1, upath, temp, temp, adsp);
2214 of_rename(vol, s_of, curdir, upath, curdir, temp);
2217 /* rename source back to dest */
2218 renamefile(vol, curdir, -1, p, upath, path->m_name, addp);
2219 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2222 /* rename temp back to source */
2223 renamefile(vol, curdir, -1, temp, p, spath, adsp);
2224 of_rename(vol, s_of, curdir, temp, sdir, spath);
2227 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2228 ad_close(adsp, ADFLAGS_HF);
2230 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2231 ad_close(addp, ADFLAGS_HF);
2235 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2236 (void)dir_remove(vol, cached);
2237 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2238 (void)dir_remove(vol, cached);