2 * Copyright (c) 1990,1993 Regents of The University of Michigan.
3 * All Rights Reserved. See COPYRIGHT.
8 #endif /* HAVE_CONFIG_H */
16 #else /* STDC_HEADERS */
20 #endif /* HAVE_STRCHR */
21 char *strchr (), *strrchr ();
24 #define memcpy(d,s,n) bcopy ((s), (d), (n))
25 #define memmove(d,s,n) bcopy ((s), (d), (n))
26 #endif /* ! HAVE_MEMCPY */
27 #endif /* STDC_HEADERS */
31 #include <sys/param.h>
33 #include <atalk/adouble.h>
34 #include <atalk/vfs.h>
35 #include <atalk/logger.h>
36 #include <atalk/afp.h>
37 #include <atalk/util.h>
38 #include <atalk/cnid.h>
39 #include <atalk/unix.h>
40 #include <atalk/globals.h>
41 #include <atalk/fce_api.h>
43 #include "directory.h"
52 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
53 * field bytes subfield bytes
56 * ioFlFndrInfo 16 -> type 4 type field
57 * creator 4 creator field
58 * flags 2 finder flags:
60 * location 4 location in window
61 * folder 2 window that contains file
63 * ioFlXFndrInfo 16 -> iconID 2 icon id
65 * script 1 script system
67 * commentID 2 comment id
68 * putawayID 4 home directory id
71 const u_char ufinderi[ADEDLEN_FINDERI] = {
72 0, 0, 0, 0, 0, 0, 0, 0,
73 1, 0, 0, 0, 0, 0, 0, 0,
74 0, 0, 0, 0, 0, 0, 0, 0,
75 0, 0, 0, 0, 0, 0, 0, 0
78 static const u_char old_ufinderi[] = {
79 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
82 /* ----------------------
84 static int default_type(void *finder)
86 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
91 /* FIXME path : unix or mac name ? (for now it's unix name ) */
92 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
95 void *ad_finder = NULL;
99 ad_finder = ad_entry(adp, ADEID_FINDERI);
102 memcpy(data, ad_finder, ADEDLEN_FINDERI);
104 if (default_type(ad_finder))
108 memcpy(data, ufinderi, ADEDLEN_FINDERI);
110 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
113 ashort = htons(FINDERINFO_INVISIBLE);
114 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
118 if (islink && !vol_syml_opt(vol)) {
120 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
121 linkflag |= htons(FINDERINFO_ISALIAS);
122 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
123 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
124 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
128 /** Only enter if no appledouble information and no finder information found. */
129 if (chk_ext && (em = getextmap( upath ))) {
130 memcpy(data, em->em_type, sizeof( em->em_type ));
131 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
136 /* ---------------------
138 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, u_int32_t utf8)
143 aint = strlen( name );
147 if (utf8_encoding()) {
148 /* but name is an utf8 mac name */
151 /* global static variable... */
153 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
162 if (aint > MACFILELEN)
169 if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
170 aint = UTF8FILELEN_EARLY;
172 utf8 = vol->v_kTextEncoding;
173 memcpy(data, &utf8, sizeof(utf8));
174 data += sizeof(utf8);
177 memcpy(data, &temp, sizeof(temp));
178 data += sizeof(temp);
181 memcpy( data, src, aint );
191 * FIXME: PDINFO is UTF8 and doesn't need adp
193 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
194 (1 << FILPBIT_CDATE) |\
195 (1 << FILPBIT_MDATE) |\
196 (1 << FILPBIT_BDATE) |\
197 (1 << FILPBIT_FINFO) |\
198 (1 << FILPBIT_RFLEN) |\
199 (1 << FILPBIT_EXTRFLEN) |\
200 (1 << FILPBIT_PDINFO) |\
201 (1 << FILPBIT_FNUM) |\
202 (1 << FILPBIT_UNIXPR)))
205 * @brief Get CNID for did/upath args both from database and adouble file
207 * 1. Get the objects CNID as stored in its adouble file
208 * 2. Get the objects CNID from the database
209 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
210 * 4. In case 2 and 3 differ, store 3 in the adouble file
212 * @param vol (rw) volume
213 * @param adp (rw) adouble struct of object upath, might be NULL
214 * @param st (r) stat of upath, must NOT be NULL
215 * @param did (r) parent CNID of upath
216 * @param upath (r) name of object
217 * @param len (r) strlen of upath
219 uint32_t get_id(struct vol *vol,
221 const struct stat *st,
226 static int first = 1; /* mark if this func is called the first time */
228 u_int32_t dbcnid = CNID_INVALID;
231 if (vol->v_cdb != NULL) {
232 /* prime aint with what we think is the cnid, set did to zero for
233 catching moved files */
234 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
236 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
237 /* Throw errors if cnid_add fails. */
238 if (dbcnid == CNID_INVALID) {
240 case CNID_ERR_CLOSE: /* the db is closed */
243 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
244 afp_errno = AFPERR_PARAM;
247 afp_errno = AFPERR_PARAM;
250 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
251 /* we have to do it here for "dbd" because it uses "lazy opening" */
252 /* In order to not end in a loop somehow with goto restart below */
254 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
255 cnid_close(vol->v_cdb);
256 free(vol->v_cnidscheme);
257 vol->v_cnidscheme = strdup("tdb");
259 int flags = CNID_FLAG_MEMORY;
260 if ((vol->v_flags & AFPVOL_NODEV)) {
261 flags |= CNID_FLAG_NODEV;
263 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
265 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
267 vol->v_flags &= ~AFPVOL_CACHE;
268 if (!(vol->v_flags & AFPVOL_TM)) {
269 vol->v_flags |= AFPVOL_RO;
270 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
271 "Check server messages for details. Switching to read-only mode.");
272 kill(getpid(), SIGUSR2);
274 goto restart; /* now try again with the temp CNID db */
276 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
277 "Check server messages for details, can't recover from this state!");
280 afp_errno = AFPERR_MISC;
284 else if (adp && (adcnid != dbcnid)) { /* 4 */
285 /* Update the ressource fork. For a folder adp is always null */
286 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
287 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
288 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
299 /* -------------------------- */
300 int getmetadata(struct vol *vol,
302 struct path *path, struct dir *dir,
303 char *buf, size_t *buflen, struct adouble *adp)
305 char *data, *l_nameoff = NULL, *upath;
306 char *utf_nameoff = NULL;
311 u_char achar, fdType[4];
316 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
318 upath = path->u_name;
322 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
323 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
324 || (bitmap & (1 << FILPBIT_FNUM))) {
327 struct dir *cachedfile;
328 int len = strlen(upath);
329 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
330 id = cachedfile->d_did;
332 id = get_id(vol, adp, st, dir->d_did, upath, len);
334 /* Add it to the cache */
335 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
336 ntohl(dir->d_did), upath, ntohl(id));
338 /* Get macname from unixname first */
339 if (path->m_name == NULL) {
340 if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
341 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
347 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
348 || (bconchar(fullpath, '/') != BSTR_OK)
349 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
350 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
354 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
355 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
359 if ((dircache_add(vol, cachedfile)) != 0) {
360 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
368 if (id == CNID_INVALID)
372 path->m_name = utompath(vol, upath, id, utf8_encoding());
375 while ( bitmap != 0 ) {
376 while (( bitmap & 1 ) == 0 ) {
384 ad_getattr(adp, &ashort);
385 } else if (vol_inv_dots(vol) && *upath == '.') {
386 ashort = htons(ATTRBIT_INVISIBLE);
390 /* FIXME do we want a visual clue if the file is read only
393 accessmode(vol, ".", &ma, dir , NULL);
394 if ((ma.ma_user & AR_UWRITE)) {
395 accessmode(vol, upath, &ma, dir , st);
396 if (!(ma.ma_user & AR_UWRITE)) {
397 ashort |= htons(ATTRBIT_NOWRITE);
401 memcpy(data, &ashort, sizeof( ashort ));
402 data += sizeof( ashort );
403 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
404 path->u_name, ntohs(ashort));
408 memcpy(data, &dir->d_did, sizeof( u_int32_t ));
409 data += sizeof( u_int32_t );
410 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
411 path->u_name, ntohl(dir->d_did));
415 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
416 aint = AD_DATE_FROM_UNIX(st->st_mtime);
417 memcpy(data, &aint, sizeof( aint ));
418 data += sizeof( aint );
422 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
423 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
424 aint = AD_DATE_FROM_UNIX(st->st_mtime);
427 aint = AD_DATE_FROM_UNIX(st->st_mtime);
429 memcpy(data, &aint, sizeof( int ));
430 data += sizeof( int );
434 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
435 aint = AD_DATE_START;
436 memcpy(data, &aint, sizeof( int ));
437 data += sizeof( int );
441 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
442 data += ADEDLEN_FINDERI;
447 data += sizeof( u_int16_t );
451 memset(data, 0, sizeof(u_int16_t));
452 data += sizeof( u_int16_t );
456 memcpy(data, &id, sizeof( id ));
457 data += sizeof( id );
458 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
459 path->u_name, ntohl(id));
463 if (st->st_size > 0xffffffff)
466 aint = htonl( st->st_size );
467 memcpy(data, &aint, sizeof( aint ));
468 data += sizeof( aint );
473 if (adp->ad_rlen > 0xffffffff)
476 aint = htonl( adp->ad_rlen);
480 memcpy(data, &aint, sizeof( aint ));
481 data += sizeof( aint );
484 /* Current client needs ProDOS info block for this file.
485 Use simple heuristic and let the Mac "type" string tell
486 us what the PD file code should be. Everything gets a
487 subtype of 0x0000 unless the original value was hashed
488 to "pXYZ" when we created it. See IA, Ver 2.
489 <shirsch@adelphia.net> */
490 case FILPBIT_PDINFO :
491 if (afp_version >= 30) { /* UTF8 name */
492 utf8 = kTextEncodingUTF8;
494 data += sizeof( u_int16_t );
496 memcpy(data, &aint, sizeof( aint ));
497 data += sizeof( aint );
501 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
503 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
507 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
511 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
515 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
519 else if ( fdType[0] == 'p' ) {
521 ashort = (fdType[2] * 256) + fdType[3];
535 memcpy(data, &ashort, sizeof( ashort ));
536 data += sizeof( ashort );
537 memset(data, 0, sizeof( ashort ));
538 data += sizeof( ashort );
541 case FILPBIT_EXTDFLEN:
542 aint = htonl(st->st_size >> 32);
543 memcpy(data, &aint, sizeof( aint ));
544 data += sizeof( aint );
545 aint = htonl(st->st_size);
546 memcpy(data, &aint, sizeof( aint ));
547 data += sizeof( aint );
549 case FILPBIT_EXTRFLEN:
552 aint = htonl(adp->ad_rlen >> 32);
553 memcpy(data, &aint, sizeof( aint ));
554 data += sizeof( aint );
556 aint = htonl(adp->ad_rlen);
557 memcpy(data, &aint, sizeof( aint ));
558 data += sizeof( aint );
560 case FILPBIT_UNIXPR :
561 /* accessmode may change st_mode with ACLs */
562 accessmode(vol, upath, &ma, dir , st);
564 aint = htonl(st->st_uid);
565 memcpy( data, &aint, sizeof( aint ));
566 data += sizeof( aint );
567 aint = htonl(st->st_gid);
568 memcpy( data, &aint, sizeof( aint ));
569 data += sizeof( aint );
572 type == slnk indicates an OSX style symlink,
573 we have to add S_IFLNK to the mode, otherwise
574 10.3 clients freak out. */
578 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
579 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
585 memcpy( data, &aint, sizeof( aint ));
586 data += sizeof( aint );
588 *data++ = ma.ma_user;
589 *data++ = ma.ma_world;
590 *data++ = ma.ma_group;
591 *data++ = ma.ma_owner;
595 return( AFPERR_BITMAP );
601 ashort = htons( data - buf );
602 memcpy(l_nameoff, &ashort, sizeof( ashort ));
603 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
606 ashort = htons( data - buf );
607 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
608 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
610 *buflen = data - buf;
614 /* ----------------------- */
615 int getfilparams(struct vol *vol,
617 struct path *path, struct dir *dir,
618 char *buf, size_t *buflen )
620 struct adouble ad, *adp;
624 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
626 opened = PARAM_NEED_ADP(bitmap);
631 int flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0;
633 adp = of_ad(vol, path, &ad);
634 upath = path->u_name;
636 if ( ad_metadata( upath, flags|ADFLAGS_CREATE, adp) < 0 ) {
639 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
640 upath, strerror(errno));
641 return AFPERR_ACCESS;
643 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
652 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
654 ad_close_metadata( adp);
660 /* ----------------------------- */
661 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
663 struct adouble ad, *adp;
666 struct ofork *of = NULL;
668 int creatf, did, openf, retvalue = AFP_OK;
674 creatf = (unsigned char) *ibuf++;
676 memcpy(&vid, ibuf, sizeof( vid ));
677 ibuf += sizeof( vid );
679 if (NULL == ( vol = getvolbyvid( vid )) ) {
680 return( AFPERR_PARAM );
683 if (vol->v_flags & AFPVOL_RO)
686 memcpy(&did, ibuf, sizeof( did));
687 ibuf += sizeof( did );
689 if (NULL == ( dir = dirlookup( vol, did )) ) {
693 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
694 return get_afp_errno(AFPERR_PARAM);
697 if ( *s_path->m_name == '\0' ) {
698 return( AFPERR_BADTYPE );
701 upath = s_path->u_name;
703 /* if upath is deleted we already in trouble anyway */
704 if ((of = of_findname(vol, s_path))) {
707 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
711 /* on a hard create, fail if file exists and is open */
714 openf = O_RDWR|O_CREAT|O_TRUNC;
716 /* on a soft create, if the file is open then ad_open won't fail
717 because open syscall is not called
722 openf = O_RDWR|O_CREAT|O_EXCL;
725 if ( ad_open( upath, ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF|ADFLAGS_CREATE,
726 openf, 0666, adp) < 0 ) {
730 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
731 return ( AFPERR_NOOBJ );
733 return( AFPERR_EXIST );
735 return( AFPERR_ACCESS );
738 LOG(log_info, logtype_afpd, "afp_createfile: DISK FULL");
739 return( AFPERR_DFULL );
741 return( AFPERR_PARAM );
744 if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */
745 /* on noadouble volumes, just creating the data fork is ok */
746 if (vol_noadouble(vol)) {
747 ad_close( adp, ADFLAGS_DF );
748 goto createfile_done;
750 /* FIXME with hard create on an existing file, we already
751 * corrupted the data file.
753 netatalk_unlink( upath );
754 ad_close( adp, ADFLAGS_DF );
755 return AFPERR_ACCESS;
758 path = s_path->m_name;
759 ad_setname(adp, path);
762 if (lstat(upath, &st) != 0) {
763 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
764 upath, strerror(errno));
765 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF);
769 (void)get_id(vol, adp, &st, dir->d_did, upath, strlen(upath));
773 fce_register_new_file(s_path);
775 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
781 if (vol->v_flags & AFPVOL_DROPBOX) {
782 retvalue = matchfile2dirperms(upath, vol, did);
784 #endif /* DROPKLUDGE */
786 setvoltime(obj, vol );
791 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
797 u_int16_t vid, bitmap;
802 memcpy(&vid, ibuf, sizeof( vid ));
803 ibuf += sizeof( vid );
804 if (NULL == ( vol = getvolbyvid( vid )) ) {
805 return( AFPERR_PARAM );
808 if (vol->v_flags & AFPVOL_RO)
811 memcpy(&did, ibuf, sizeof( did ));
812 ibuf += sizeof( did );
813 if (NULL == ( dir = dirlookup( vol, did )) ) {
814 return afp_errno; /* was AFPERR_NOOBJ */
817 memcpy(&bitmap, ibuf, sizeof( bitmap ));
818 bitmap = ntohs( bitmap );
819 ibuf += sizeof( bitmap );
821 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
822 return get_afp_errno(AFPERR_PARAM);
825 if (path_isadir(s_path)) {
826 return( AFPERR_BADTYPE ); /* it's a directory */
829 if ( s_path->st_errno != 0 ) {
830 return( AFPERR_NOOBJ );
833 if ((u_long)ibuf & 1 ) {
837 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
838 setvoltime(obj, vol );
845 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
848 extern struct path Cur_Path;
850 int setfilparams(struct vol *vol,
851 struct path *path, u_int16_t f_bitmap, char *buf )
853 struct adouble ad, *adp;
855 int bit, isad = 1, err = AFP_OK;
857 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
858 u_int16_t ashort, bshort, oshort;
861 u_int16_t upriv_bit = 0;
865 int change_mdate = 0;
866 int change_parent_mdate = 0;
871 u_int16_t bitmap = f_bitmap;
872 u_int32_t cdate,bdate;
873 u_char finder_buf[32];
876 char symbuf[MAXPATHLEN+1];
879 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
882 adp = of_ad(vol, path, &ad);
883 upath = path->u_name;
885 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
886 return AFPERR_ACCESS;
889 /* with unix priv maybe we have to change adouble file priv first */
891 while ( bitmap != 0 ) {
892 while (( bitmap & 1 ) == 0 ) {
899 memcpy(&ashort, buf, sizeof( ashort ));
900 buf += sizeof( ashort );
904 memcpy(&cdate, buf, sizeof(cdate));
905 buf += sizeof( cdate );
908 memcpy(&newdate, buf, sizeof( newdate ));
909 buf += sizeof( newdate );
913 memcpy(&bdate, buf, sizeof( bdate));
914 buf += sizeof( bdate );
918 if (memcmp(buf,"slnkrhap",8) == 0
919 && !(S_ISLNK(path->st.st_mode))
920 && !(vol->v_flags & AFPVOL_FOLLOWSYM)) {
921 /* request to turn this into a symlink */
922 if ((fp = open(path->u_name, O_RDONLY)) == -1) {
924 goto setfilparam_done;
926 len = read(fp, symbuf, MAXPATHLEN);
930 goto setfilparam_done;
932 if (unlink(path->u_name) != 0) {
934 goto setfilparam_done;
937 if (symlink(symbuf, path->u_name) != 0) {
939 goto setfilparam_done;
943 memcpy(finder_buf, buf, 32 );
946 case FILPBIT_UNIXPR :
947 if (!vol_unix_priv(vol)) {
948 /* this volume doesn't use unix priv */
954 change_parent_mdate = 1;
956 memcpy( &aint, buf, sizeof( aint ));
957 f_uid = ntohl (aint);
958 buf += sizeof( aint );
959 memcpy( &aint, buf, sizeof( aint ));
960 f_gid = ntohl (aint);
961 buf += sizeof( aint );
962 setfilowner(vol, f_uid, f_gid, path);
964 memcpy( &upriv, buf, sizeof( upriv ));
965 buf += sizeof( upriv );
966 upriv = ntohl (upriv);
967 if ((upriv & S_IWUSR)) {
968 setfilunixmode(vol, path, upriv);
975 case FILPBIT_PDINFO :
976 if (afp_version < 30) { /* else it's UTF8 name */
979 /* Keep special case to support crlf translations */
980 if ((unsigned int) achar == 0x04) {
981 fdType = (u_char *)"TEXT";
984 xyy[0] = ( u_char ) 'p';
995 /* break while loop */
1004 /* second try with adouble open
1006 if ( ad_open_metadata( upath, 0, O_CREAT, adp) < 0) {
1007 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
1009 * For some things, we don't need an adouble header:
1010 * - change of modification date
1011 * - UNIX privs (Bug-ID #2863424)
1013 if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
1014 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
1015 return AFPERR_ACCESS;
1017 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
1019 } else if ((ad_get_HF_flags( adp ) & O_CREAT) ) {
1020 ad_setname(adp, path->m_name);
1025 while ( bitmap != 0 ) {
1026 while (( bitmap & 1 ) == 0 ) {
1033 ad_getattr(adp, &bshort);
1035 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1036 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1040 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1041 change_parent_mdate = 1;
1042 ad_setattr(adp, bshort);
1044 case FILPBIT_CDATE :
1045 ad_setdate(adp, AD_DATE_CREATE, cdate);
1047 case FILPBIT_MDATE :
1049 case FILPBIT_BDATE :
1050 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1052 case FILPBIT_FINFO :
1053 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1055 ((em = getextmap( path->m_name )) &&
1056 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1057 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1058 || ((em = getdefextmap()) &&
1059 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1060 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1062 memcpy(finder_buf, ufinderi, 8 );
1064 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1066 case FILPBIT_UNIXPR :
1068 setfilunixmode(vol, path, upriv);
1071 case FILPBIT_PDINFO :
1072 if (afp_version < 30) { /* else it's UTF8 name */
1073 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1074 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1079 err = AFPERR_BITMAP;
1080 goto setfilparam_done;
1087 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1088 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1092 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1093 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1099 ad_close_metadata( adp);
1103 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1104 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1105 bitmap = 1<<FILPBIT_MDATE;
1106 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1110 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1116 * renamefile and copyfile take the old and new unix pathnames
1117 * and the new mac name.
1119 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1120 * passing -1 means this is not used, src path is a full path
1121 * src the source path
1122 * dst the dest filename in current dir
1123 * newname the dest mac name
1124 * adp adouble struct of src file, if open, or & zeroed one
1127 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1131 LOG(log_debug, logtype_afpd,
1132 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1134 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1137 return( AFPERR_NOOBJ );
1140 return( AFPERR_ACCESS );
1142 return AFPERR_VLOCK;
1143 case EXDEV : /* Cross device move -- try copy */
1144 /* NOTE: with open file it's an error because after the copy we will
1145 * get two files, it's fixable for our process (eg reopen the new file, get the
1146 * locks, and so on. But it doesn't solve the case with a second process
1148 if (adp->ad_open_forks) {
1149 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1150 return AFPERR_OLOCK; /* little lie */
1152 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1153 /* on error copyfile delete dest */
1156 return deletefile(vol, sdir_fd, src, 0);
1158 return( AFPERR_PARAM );
1162 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1166 /* try to undo the data fork rename,
1167 * we know we are on the same device
1170 unix_rename(-1, dst, sdir_fd, src );
1171 /* return the first error */
1174 return AFPERR_NOOBJ;
1177 return AFPERR_ACCESS ;
1179 return AFPERR_VLOCK;
1181 return AFPERR_PARAM ;
1186 /* don't care if we can't open the newly renamed ressource fork
1188 if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
1189 ad_setname(adp, newname);
1191 ad_close( adp, ADFLAGS_HF );
1198 convert a Mac long name to an utf8 name,
1200 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1204 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1210 /* ---------------- */
1211 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1218 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1224 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1225 if (afp_version >= 30) {
1226 /* convert it to UTF8
1228 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1232 strncpy( newname, ibuf, plen );
1233 newname[ plen ] = '\0';
1235 if (strlen(newname) != plen) {
1236 /* there's \0 in newname, e.g. it's a pathname not
1244 memcpy(&hint, ibuf, sizeof(hint));
1245 ibuf += sizeof(hint);
1247 memcpy(&len16, ibuf, sizeof(len16));
1248 ibuf += sizeof(len16);
1249 plen = ntohs(len16);
1252 if (plen > AFPOBJ_TMPSIZ) {
1255 strncpy( newname, ibuf, plen );
1256 newname[ plen ] = '\0';
1257 if (strlen(newname) != plen) {
1266 /* -----------------------------------
1268 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1270 struct vol *s_vol, *d_vol;
1272 char *newname, *p, *upath;
1273 struct path *s_path;
1274 u_int32_t sdid, ddid;
1275 int err, retvalue = AFP_OK;
1276 u_int16_t svid, dvid;
1278 struct adouble ad, *adp;
1284 memcpy(&svid, ibuf, sizeof( svid ));
1285 ibuf += sizeof( svid );
1286 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1287 return( AFPERR_PARAM );
1290 memcpy(&sdid, ibuf, sizeof( sdid ));
1291 ibuf += sizeof( sdid );
1292 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1296 memcpy(&dvid, ibuf, sizeof( dvid ));
1297 ibuf += sizeof( dvid );
1298 memcpy(&ddid, ibuf, sizeof( ddid ));
1299 ibuf += sizeof( ddid );
1301 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1302 return get_afp_errno(AFPERR_PARAM);
1304 if ( path_isadir(s_path) ) {
1305 return( AFPERR_BADTYPE );
1308 /* don't allow copies when the file is open.
1309 * XXX: the spec only calls for read/deny write access.
1310 * however, copyfile doesn't have any of that info,
1311 * and locks need to stay coherent. as a result,
1312 * we just balk if the file is opened already. */
1314 adp = of_ad(s_vol, s_path, &ad);
1316 if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1317 return AFPERR_DENYCONF;
1319 denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1320 getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1323 retvalue = AFPERR_DENYCONF;
1327 newname = obj->newtmp;
1328 strcpy( newname, s_path->m_name );
1330 p = ctoupath( s_vol, curdir, newname );
1332 retvalue = AFPERR_PARAM;
1337 /* FIXME svid != dvid && dvid's user can't read svid */
1339 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1340 retvalue = AFPERR_PARAM;
1344 if (d_vol->v_flags & AFPVOL_RO) {
1345 retvalue = AFPERR_VLOCK;
1349 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1350 retvalue = afp_errno;
1354 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1355 retvalue = get_afp_errno(AFPERR_NOOBJ);
1359 if ( *s_path->m_name != '\0' ) {
1360 retvalue =path_error(s_path, AFPERR_NOOBJ);
1364 /* one of the handful of places that knows about the path type */
1365 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1366 retvalue = AFPERR_PARAM;
1369 /* newname is always only a filename so curdir *is* its
1372 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1373 retvalue =AFPERR_PARAM;
1377 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1384 if (vol->v_flags & AFPVOL_DROPBOX) {
1385 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1387 #endif /* DROPKLUDGE */
1389 setvoltime(obj, d_vol );
1392 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1396 /* ----------------------- */
1397 static int copy_all(const int dfd, const void *buf,
1403 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1406 while (buflen > 0) {
1407 if ((cc = write(dfd, buf, buflen)) < 0) {
1419 LOG(log_debug9, logtype_afpd, "end copy_all:");
1425 /* --------------------------
1426 * copy only the fork data stream
1428 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1435 if (eid == ADEID_DFORK) {
1436 sfd = ad_data_fileno(ads);
1437 dfd = ad_data_fileno(add);
1440 sfd = ad_reso_fileno(ads);
1441 dfd = ad_reso_fileno(add);
1444 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1447 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1450 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1451 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1455 #define BUF 128*1024*1024
1457 if (fstat(sfd, &st) == 0) {
1460 if ( offset >= st.st_size) {
1463 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1464 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1467 case EINVAL: /* there's no guarantee that all fs support sendfile */
1476 lseek(sfd, offset, SEEK_SET);
1480 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1487 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1494 /* ----------------------------------
1495 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1496 * because we are doing it elsewhere.
1497 * currently if newname is NULL then adp is NULL.
1499 int copyfile(const struct vol *s_vol,
1500 const struct vol *d_vol,
1505 struct adouble *adp)
1507 struct adouble ads, add;
1514 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1515 sfd, src, dst, newname);
1518 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1522 adflags = ADFLAGS_DF;
1524 adflags |= ADFLAGS_HF;
1527 if (ad_openat(sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1532 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1533 /* no resource fork, don't create one for dst file */
1534 adflags &= ~ADFLAGS_HF;
1537 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1539 if (stat_result < 0) {
1540 /* unlikely but if fstat fails, the default file mode will be 0666. */
1541 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1544 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1545 if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1547 ad_close( adp, adflags );
1548 if (EEXIST != ret_err) {
1549 deletefile(d_vol, -1, dst, 0);
1552 return AFPERR_EXIST;
1556 * XXX if the source and the dest don't use the same resource type it's broken
1558 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1559 /* copy the data fork */
1560 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1561 if (ad_meta_fileno(adp) != -1)
1562 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1570 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1571 /* set the new name in the resource fork */
1572 ad_copy_header(&add, adp);
1573 ad_setname(&add, newname);
1576 ad_close( adp, adflags );
1578 if (ad_close( &add, adflags ) <0) {
1583 deletefile(d_vol, -1, dst, 0);
1585 else if (stat_result == 0) {
1586 /* set dest modification date to src date */
1589 ut.actime = ut.modtime = st.st_mtime;
1591 /* FIXME netatalk doesn't use resource fork file date
1592 * but maybe we should set its modtime too.
1597 switch ( ret_err ) {
1603 LOG(log_info, logtype_afpd, "copyfile: DISK FULL");
1604 return AFPERR_DFULL;
1606 return AFPERR_NOOBJ;
1608 return AFPERR_ACCESS;
1610 return AFPERR_VLOCK;
1612 return AFPERR_PARAM;
1616 /* -----------------------------------
1617 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1618 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1620 when deletefile is called we don't have lock on it, file is closed (for us)
1621 untrue if called by renamefile
1623 ad_open always try to open file RDWR first and ad_lock takes care of
1624 WRITE lock on read only file.
1627 static int check_attrib(struct adouble *adp)
1629 u_int16_t bshort = 0;
1631 ad_getattr(adp, &bshort);
1633 * Does kFPDeleteInhibitBit (bit 8) set?
1635 if ((bshort & htons(ATTRBIT_NODELETE))) {
1636 return AFPERR_OLOCK;
1638 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1644 * dirfd can be used for unlinkat semantics
1646 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1649 struct adouble *adp = NULL;
1650 int adflags, err = AFP_OK;
1653 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1655 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1657 /* was EACCESS error try to get only metadata */
1658 /* we never want to create a resource fork here, we are going to delete it
1659 * moreover sometimes deletefile is called with a no existent file and
1660 * ad_open would create a 0 byte resource fork
1662 if ( ad_metadataat(dirfd, file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
1663 if ((err = check_attrib(&ad))) {
1664 ad_close_metadata(&ad);
1671 /* try to open both forks at once */
1672 adflags = ADFLAGS_DF;
1673 if ( ad_openat(dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, 0, &ad ) < 0 ) {
1678 case EACCES: /* maybe it's a file with no write mode for us */
1679 break; /* was return AFPERR_ACCESS;*/
1692 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1693 adflags |= ADFLAGS_HF;
1694 /* FIXME we have a pb here because we want to know if a file is open
1695 * there's a 'priority inversion' if you can't open the ressource fork RW
1696 * you can delete it if it's open because you can't get a write lock.
1698 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1701 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1703 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1709 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1711 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1713 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1714 cnid_delete(vol->v_cdb, id);
1720 ad_close_metadata(&ad);
1723 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1728 /* ------------------------------------ */
1729 /* return a file id */
1730 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1739 struct path *s_path;
1745 memcpy(&vid, ibuf, sizeof(vid));
1746 ibuf += sizeof(vid);
1748 if (NULL == ( vol = getvolbyvid( vid )) ) {
1749 return( AFPERR_PARAM);
1752 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1756 if (vol->v_flags & AFPVOL_RO)
1757 return AFPERR_VLOCK;
1759 memcpy(&did, ibuf, sizeof( did ));
1760 ibuf += sizeof(did);
1762 if (NULL == ( dir = dirlookup( vol, did )) ) {
1763 return afp_errno; /* was AFPERR_PARAM */
1766 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1767 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1770 if ( path_isadir(s_path) ) {
1771 return( AFPERR_BADTYPE );
1774 upath = s_path->u_name;
1775 switch (s_path->st_errno) {
1777 break; /* success */
1780 return AFPERR_ACCESS;
1782 return AFPERR_NOOBJ;
1784 return AFPERR_PARAM;
1787 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1788 memcpy(rbuf, &id, sizeof(id));
1789 *rbuflen = sizeof(id);
1790 return AFPERR_EXISTID;
1793 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1794 memcpy(rbuf, &id, sizeof(id));
1795 *rbuflen = sizeof(id);
1802 /* ------------------------------- */
1808 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1811 struct reenum *param = data;
1812 struct vol *vol = param->vol;
1813 cnid_t did = param->did;
1816 if (ostat(de->d_name, &path.st, vol_syml_opt(vol)) < 0)
1819 /* update or add to cnid */
1820 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1822 #if AD_VERSION > AD_VERSION1
1823 if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1824 struct adouble ad, *adp;
1828 path.u_name = de->d_name;
1830 adp = of_ad(vol, &path, &ad);
1832 if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1835 if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1838 ad_close_metadata(adp);
1840 #endif /* AD_VERSION > AD_VERSION1 */
1845 /* --------------------
1846 * Ok the db is out of synch with the dir.
1847 * but if it's a deleted file we don't want to do it again and again.
1850 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1856 if (vol->v_cdb == NULL) {
1860 /* FIXME use of_statdir ? */
1861 if (ostat(name, &st, vol_syml_opt(vol))) {
1865 if (dirreenumerate(dir, &st)) {
1866 /* we already did it once and the dir haven't been modified */
1867 return dir->d_offcnt;
1871 data.did = dir->d_did;
1872 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1873 setdiroffcnt(curdir, &st, ret);
1874 dir->d_flags |= DIRF_CNID;
1880 /* ------------------------------
1881 resolve a file id */
1882 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1891 u_int16_t vid, bitmap;
1893 static char buffer[12 + MAXPATHLEN + 1];
1894 int len = 12 + MAXPATHLEN + 1;
1899 memcpy(&vid, ibuf, sizeof(vid));
1900 ibuf += sizeof(vid);
1902 if (NULL == ( vol = getvolbyvid( vid )) ) {
1903 return( AFPERR_PARAM);
1906 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1910 memcpy(&id, ibuf, sizeof( id ));
1915 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1919 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1920 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1923 if (NULL == ( dir = dirlookup( vol, id )) ) {
1924 return AFPERR_NOID; /* idem AFPERR_PARAM */
1926 if (movecwd(vol, dir) < 0) {
1930 return AFPERR_ACCESS;
1934 return AFPERR_PARAM;
1938 memset(&path, 0, sizeof(path));
1939 path.u_name = upath;
1940 if (of_stat(vol, &path) < 0 ) {
1942 /* with nfs and our working directory is deleted */
1943 if (errno == ESTALE) {
1947 if ( errno == ENOENT && !retry) {
1948 /* cnid db is out of sync, reenumerate the directory and update ids */
1949 reenumerate_id(vol, ".", dir);
1957 return AFPERR_ACCESS;
1961 return AFPERR_PARAM;
1965 /* directories are bad */
1966 if (S_ISDIR(path.st.st_mode)) {
1967 /* OS9 and OSX don't return the same error code */
1968 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1971 memcpy(&bitmap, ibuf, sizeof(bitmap));
1972 bitmap = ntohs( bitmap );
1973 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1977 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1978 rbuf + sizeof(bitmap), &buflen))) {
1981 *rbuflen = buflen + sizeof(bitmap);
1982 memcpy(rbuf, ibuf, sizeof(bitmap));
1987 /* ------------------------------ */
1988 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1998 static char buffer[12 + MAXPATHLEN + 1];
1999 int len = 12 + MAXPATHLEN + 1;
2004 memcpy(&vid, ibuf, sizeof(vid));
2005 ibuf += sizeof(vid);
2007 if (NULL == ( vol = getvolbyvid( vid )) ) {
2008 return( AFPERR_PARAM);
2011 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
2015 if (vol->v_flags & AFPVOL_RO)
2016 return AFPERR_VLOCK;
2018 memcpy(&id, ibuf, sizeof( id ));
2022 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
2026 if (NULL == ( dir = dirlookup( vol, id )) ) {
2027 if (afp_errno == AFPERR_NOOBJ) {
2031 return( AFPERR_PARAM );
2035 if ((movecwd(vol, dir) < 0) || (ostat(upath, &st, vol_syml_opt(vol)) < 0)) {
2039 return AFPERR_ACCESS;
2044 /* still try to delete the id */
2048 return AFPERR_PARAM;
2051 else if (S_ISDIR(st.st_mode)) /* directories are bad */
2052 return AFPERR_BADTYPE;
2055 if (cnid_delete(vol->v_cdb, fileid)) {
2058 return AFPERR_VLOCK;
2061 return AFPERR_ACCESS;
2063 return AFPERR_PARAM;
2070 /* ------------------------------ */
2071 static struct adouble *find_adouble(const struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
2075 if (path->st_errno) {
2076 switch (path->st_errno) {
2078 afp_errno = AFPERR_NOID;
2082 afp_errno = AFPERR_ACCESS;
2085 afp_errno = AFPERR_PARAM;
2090 /* we use file_access both for legacy Mac perm and
2091 * for unix privilege, rename will take care of folder perms
2093 if (file_access(path, OPENACC_WR ) < 0) {
2094 afp_errno = AFPERR_ACCESS;
2098 if ((*of = of_findname(vol, path))) {
2099 /* reuse struct adouble so it won't break locks */
2103 ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
2105 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2107 * The user must have the Read & Write privilege for both files in order to use this command.
2109 ad_close(adp, ADFLAGS_HF);
2110 afp_errno = AFPERR_ACCESS;
2117 #define APPLETEMP ".AppleTempXXXXXX"
2119 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2121 struct stat srcst, destst;
2123 struct dir *dir, *sdir;
2124 char *spath, temp[17], *p;
2125 char *supath, *upath;
2130 struct adouble *adsp = NULL;
2131 struct adouble *addp = NULL;
2132 struct ofork *s_of = NULL;
2133 struct ofork *d_of = NULL;
2146 memcpy(&vid, ibuf, sizeof(vid));
2147 ibuf += sizeof(vid);
2149 if (NULL == ( vol = getvolbyvid( vid )) ) {
2150 return( AFPERR_PARAM);
2153 if ((vol->v_flags & AFPVOL_RO))
2154 return AFPERR_VLOCK;
2156 /* source and destination dids */
2157 memcpy(&sid, ibuf, sizeof(sid));
2158 ibuf += sizeof(sid);
2159 memcpy(&did, ibuf, sizeof(did));
2160 ibuf += sizeof(did);
2163 if (NULL == (dir = dirlookup( vol, sid )) ) {
2164 return afp_errno; /* was AFPERR_PARAM */
2167 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2168 return get_afp_errno(AFPERR_NOOBJ);
2171 if ( path_isadir(path) ) {
2172 return AFPERR_BADTYPE; /* it's a dir */
2175 /* save some stuff */
2178 spath = obj->oldtmp;
2179 supath = obj->newtmp;
2180 strcpy(spath, path->m_name);
2181 strcpy(supath, path->u_name); /* this is for the cnid changing */
2182 p = absupath( vol, sdir, supath);
2184 /* pathname too long */
2185 return AFPERR_PARAM ;
2188 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2189 if (!(adsp = find_adouble(vol, path, &s_of, &ads))) {
2193 /* ***** from here we may have resource fork open **** */
2195 /* look for the source cnid. if it doesn't exist, don't worry about
2197 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2199 if (NULL == ( dir = dirlookup( vol, did )) ) {
2200 err = afp_errno; /* was AFPERR_PARAM */
2201 goto err_exchangefile;
2204 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2205 err = get_afp_errno(AFPERR_NOOBJ);
2206 goto err_exchangefile;
2209 if ( path_isadir(path) ) {
2210 err = AFPERR_BADTYPE;
2211 goto err_exchangefile;
2214 /* FPExchangeFiles is the only call that can return the SameObj
2216 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2217 err = AFPERR_SAMEOBJ;
2218 goto err_exchangefile;
2221 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2222 if (!(addp = find_adouble(vol, path, &d_of, &add))) {
2224 goto err_exchangefile;
2228 /* they are not on the same device and at least one is open
2229 * FIXME broken for for crossdev and adouble v2
2232 crossdev = (srcst.st_dev != destst.st_dev);
2233 if (/* (d_of || s_of) && */ crossdev) {
2235 goto err_exchangefile;
2238 /* look for destination id. */
2239 upath = path->u_name;
2240 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2242 /* construct a temp name.
2243 * NOTE: the temp file will be in the dest file's directory. it
2244 * will also be inaccessible from AFP. */
2245 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2246 if (!mktemp(temp)) {
2248 goto err_exchangefile;
2252 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2253 ad_close(adsp, ADFLAGS_HF);
2254 ad_close(addp, ADFLAGS_HF);
2257 /* now, quickly rename the file. we error if we can't. */
2258 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2259 goto err_exchangefile;
2260 of_rename(vol, s_of, sdir, spath, curdir, temp);
2262 /* rename destination to source */
2263 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2264 goto err_src_to_tmp;
2265 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2267 /* rename temp to destination */
2268 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2269 goto err_dest_to_src;
2270 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2272 /* id's need switching. src -> dest and dest -> src.
2273 * we need to re-stat() if it was a cross device copy.
2276 cnid_delete(vol->v_cdb, sid);
2278 cnid_delete(vol->v_cdb, did);
2280 if ((did && ( (crossdev && ostat(upath, &srcst, vol_syml_opt(vol)) < 0) ||
2281 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2283 (sid && ( (crossdev && ostat(p, &destst, vol_syml_opt(vol)) < 0) ||
2284 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2289 err = AFPERR_ACCESS;
2294 goto err_temp_to_dest;
2297 /* here we need to reopen if crossdev */
2298 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2303 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2308 /* change perms, src gets dest perm and vice versa */
2313 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2314 err = AFP_OK; /* ignore error */
2315 goto err_temp_to_dest;
2319 * we need to exchange ACL entries as well
2321 /* exchange_acls(vol, p, upath); */
2326 path->m_name = NULL;
2327 path->u_name = upath;
2329 setfilunixmode(vol, path, destst.st_mode);
2330 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2337 setfilunixmode(vol, path, srcst.st_mode);
2338 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2340 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2341 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2346 goto err_exchangefile;
2348 /* all this stuff is so that we can unwind a failed operation
2351 /* rename dest to temp */
2352 renamefile(vol, -1, upath, temp, temp, adsp);
2353 of_rename(vol, s_of, curdir, upath, curdir, temp);
2356 /* rename source back to dest */
2357 renamefile(vol, -1, p, upath, path->m_name, addp);
2358 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2361 /* rename temp back to source */
2362 renamefile(vol, -1, temp, p, spath, adsp);
2363 of_rename(vol, s_of, curdir, temp, sdir, spath);
2366 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2367 ad_close(adsp, ADFLAGS_HF);
2369 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2370 ad_close(addp, ADFLAGS_HF);
2374 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2375 (void)dir_remove(vol, cached);
2376 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2377 (void)dir_remove(vol, cached);