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>
41 #include "directory.h"
51 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
52 * field bytes subfield bytes
55 * ioFlFndrInfo 16 -> type 4 type field
56 * creator 4 creator field
57 * flags 2 finder flags:
59 * location 4 location in window
60 * folder 2 window that contains file
62 * ioFlXFndrInfo 16 -> iconID 2 icon id
64 * script 1 script system
66 * commentID 2 comment id
67 * putawayID 4 home directory id
70 const u_char ufinderi[ADEDLEN_FINDERI] = {
71 0, 0, 0, 0, 0, 0, 0, 0,
72 1, 0, 0, 0, 0, 0, 0, 0,
73 0, 0, 0, 0, 0, 0, 0, 0,
74 0, 0, 0, 0, 0, 0, 0, 0
77 static const u_char old_ufinderi[] = {
78 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
81 /* ----------------------
83 static int default_type(void *finder)
85 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
90 /* FIXME path : unix or mac name ? (for now it's unix name ) */
91 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
94 void *ad_finder = NULL;
98 ad_finder = ad_entry(adp, ADEID_FINDERI);
101 memcpy(data, ad_finder, ADEDLEN_FINDERI);
103 if (default_type(ad_finder))
107 memcpy(data, ufinderi, ADEDLEN_FINDERI);
109 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
112 ashort = htons(FINDERINFO_INVISIBLE);
113 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
119 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
120 linkflag |= htons(FINDERINFO_ISALIAS);
121 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
122 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
123 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
127 /** Only enter if no appledouble information and no finder information found. */
128 if (chk_ext && (em = getextmap( upath ))) {
129 memcpy(data, em->em_type, sizeof( em->em_type ));
130 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
135 /* ---------------------
137 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, u_int32_t utf8)
142 aint = strlen( name );
146 if (utf8_encoding()) {
147 /* but name is an utf8 mac name */
150 /* global static variable... */
152 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
161 if (aint > MACFILELEN)
168 if (aint > 255) /* FIXME safeguard, anyway if no ascii char it's game over*/
171 utf8 = vol->v_kTextEncoding;
172 memcpy(data, &utf8, sizeof(utf8));
173 data += sizeof(utf8);
176 memcpy(data, &temp, sizeof(temp));
177 data += sizeof(temp);
180 memcpy( data, src, aint );
190 * FIXME: PDINFO is UTF8 and doesn't need adp
192 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
193 (1 << FILPBIT_CDATE) |\
194 (1 << FILPBIT_MDATE) |\
195 (1 << FILPBIT_BDATE) |\
196 (1 << FILPBIT_FINFO) |\
197 (1 << FILPBIT_RFLEN) |\
198 (1 << FILPBIT_EXTRFLEN) |\
199 (1 << FILPBIT_PDINFO) |\
200 (1 << FILPBIT_FNUM) |\
201 (1 << FILPBIT_UNIXPR)))
204 * @brief Get CNID for did/upath args both from database and adouble file
206 * 1. Get the objects CNID as stored in its adouble file
207 * 2. Get the objects CNID from the database
208 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
209 * 4. In case 2 and 3 differ, store 3 in the adouble file
211 * @param vol (rw) volume
212 * @param adp (rw) adouble struct of object upath, might be NULL
213 * @param st (r) stat of upath, must NOT be NULL
214 * @param did (r) parent CNID of upath
215 * @param upath (r) name of object
216 * @param len (r) strlen of upath
218 uint32_t get_id(struct vol *vol,
220 const struct stat *st,
225 static int first = 1; /* mark if this func is called the first time */
227 u_int32_t dbcnid = CNID_INVALID;
230 if (vol->v_cdb != NULL) {
231 /* prime aint with what we think is the cnid, set did to zero for
232 catching moved files */
233 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
235 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
236 /* Throw errors if cnid_add fails. */
237 if (dbcnid == CNID_INVALID) {
239 case CNID_ERR_CLOSE: /* the db is closed */
242 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
243 afp_errno = AFPERR_PARAM;
246 afp_errno = AFPERR_PARAM;
249 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
250 /* we have to do it here for "dbd" because it uses "lazy opening" */
251 /* In order to not end in a loop somehow with goto restart below */
253 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
254 cnid_close(vol->v_cdb);
255 free(vol->v_cnidscheme);
256 vol->v_cnidscheme = strdup("tdb");
258 int flags = CNID_FLAG_MEMORY;
259 if ((vol->v_flags & AFPVOL_NODEV)) {
260 flags |= CNID_FLAG_NODEV;
262 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
264 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
266 /* deactivate cnid caching/storing in AppleDouble files and set ro mode*/
267 vol->v_flags &= ~AFPVOL_CACHE;
268 vol->v_flags |= AFPVOL_RO;
270 /* kill ourself with SIGUSR2 aka msg pending */
271 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
272 "Check server messages for details. Switching to read-only mode.");
273 kill(getpid(), SIGUSR2);
275 goto restart; /* not try again with the temp CNID db */
278 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
279 "Check server messages for details, can't recover from this state!");
283 afp_errno = AFPERR_MISC;
287 else if (adp && (adcnid != dbcnid)) { /* 4 */
288 /* Update the ressource fork. For a folder adp is always null */
289 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
290 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
291 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
302 /* -------------------------- */
303 int getmetadata(struct vol *vol,
305 struct path *path, struct dir *dir,
306 char *buf, size_t *buflen, struct adouble *adp)
308 char *data, *l_nameoff = NULL, *upath;
309 char *utf_nameoff = NULL;
314 u_char achar, fdType[4];
319 upath = path->u_name;
323 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
324 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
325 || (bitmap & (1 << FILPBIT_FNUM))) {
327 struct dir *cachedfile;
328 int len = strlen(upath);
329 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len, st->st_ctime)) != 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");
346 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, NULL, st->st_ctime)) == NULL) {
347 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
351 if ((dircache_add(cachedfile)) != 0) {
352 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
360 if (id == CNID_INVALID)
364 path->m_name = utompath(vol, upath, id, utf8_encoding());
367 while ( bitmap != 0 ) {
368 while (( bitmap & 1 ) == 0 ) {
376 ad_getattr(adp, &ashort);
377 } else if (vol_inv_dots(vol) && *upath == '.') {
378 ashort = htons(ATTRBIT_INVISIBLE);
382 /* FIXME do we want a visual clue if the file is read only
385 accessmode( ".", &ma, dir , NULL);
386 if ((ma.ma_user & AR_UWRITE)) {
387 accessmode( upath, &ma, dir , st);
388 if (!(ma.ma_user & AR_UWRITE)) {
389 ashort |= htons(ATTRBIT_NOWRITE);
393 memcpy(data, &ashort, sizeof( ashort ));
394 data += sizeof( ashort );
395 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
396 path->u_name, ntohs(ashort));
400 memcpy(data, &dir->d_did, sizeof( u_int32_t ));
401 data += sizeof( u_int32_t );
402 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
403 path->u_name, ntohl(dir->d_did));
407 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
408 aint = AD_DATE_FROM_UNIX(st->st_mtime);
409 memcpy(data, &aint, sizeof( aint ));
410 data += sizeof( aint );
414 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
415 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
416 aint = AD_DATE_FROM_UNIX(st->st_mtime);
419 aint = AD_DATE_FROM_UNIX(st->st_mtime);
421 memcpy(data, &aint, sizeof( int ));
422 data += sizeof( int );
426 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
427 aint = AD_DATE_START;
428 memcpy(data, &aint, sizeof( int ));
429 data += sizeof( int );
433 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
434 data += ADEDLEN_FINDERI;
439 data += sizeof( u_int16_t );
443 memset(data, 0, sizeof(u_int16_t));
444 data += sizeof( u_int16_t );
448 memcpy(data, &id, sizeof( id ));
449 data += sizeof( id );
450 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
451 path->u_name, ntohl(id));
455 if (st->st_size > 0xffffffff)
458 aint = htonl( st->st_size );
459 memcpy(data, &aint, sizeof( aint ));
460 data += sizeof( aint );
465 if (adp->ad_rlen > 0xffffffff)
468 aint = htonl( adp->ad_rlen);
472 memcpy(data, &aint, sizeof( aint ));
473 data += sizeof( aint );
476 /* Current client needs ProDOS info block for this file.
477 Use simple heuristic and let the Mac "type" string tell
478 us what the PD file code should be. Everything gets a
479 subtype of 0x0000 unless the original value was hashed
480 to "pXYZ" when we created it. See IA, Ver 2.
481 <shirsch@adelphia.net> */
482 case FILPBIT_PDINFO :
483 if (afp_version >= 30) { /* UTF8 name */
484 utf8 = kTextEncodingUTF8;
486 data += sizeof( u_int16_t );
488 memcpy(data, &aint, sizeof( aint ));
489 data += sizeof( aint );
493 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
495 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
499 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
503 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
507 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
511 else if ( fdType[0] == 'p' ) {
513 ashort = (fdType[2] * 256) + fdType[3];
527 memcpy(data, &ashort, sizeof( ashort ));
528 data += sizeof( ashort );
529 memset(data, 0, sizeof( ashort ));
530 data += sizeof( ashort );
533 case FILPBIT_EXTDFLEN:
534 aint = htonl(st->st_size >> 32);
535 memcpy(data, &aint, sizeof( aint ));
536 data += sizeof( aint );
537 aint = htonl(st->st_size);
538 memcpy(data, &aint, sizeof( aint ));
539 data += sizeof( aint );
541 case FILPBIT_EXTRFLEN:
544 aint = htonl(adp->ad_rlen >> 32);
545 memcpy(data, &aint, sizeof( aint ));
546 data += sizeof( aint );
548 aint = htonl(adp->ad_rlen);
549 memcpy(data, &aint, sizeof( aint ));
550 data += sizeof( aint );
552 case FILPBIT_UNIXPR :
553 /* accessmode may change st_mode with ACLs */
554 accessmode( upath, &ma, dir , st);
556 aint = htonl(st->st_uid);
557 memcpy( data, &aint, sizeof( aint ));
558 data += sizeof( aint );
559 aint = htonl(st->st_gid);
560 memcpy( data, &aint, sizeof( aint ));
561 data += sizeof( aint );
564 type == slnk indicates an OSX style symlink,
565 we have to add S_IFLNK to the mode, otherwise
566 10.3 clients freak out. */
570 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
571 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
577 memcpy( data, &aint, sizeof( aint ));
578 data += sizeof( aint );
580 *data++ = ma.ma_user;
581 *data++ = ma.ma_world;
582 *data++ = ma.ma_group;
583 *data++ = ma.ma_owner;
587 return( AFPERR_BITMAP );
593 ashort = htons( data - buf );
594 memcpy(l_nameoff, &ashort, sizeof( ashort ));
595 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
598 ashort = htons( data - buf );
599 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
600 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
602 *buflen = data - buf;
606 /* ----------------------- */
607 int getfilparams(struct vol *vol,
609 struct path *path, struct dir *dir,
610 char *buf, size_t *buflen )
612 struct adouble ad, *adp;
616 opened = PARAM_NEED_ADP(bitmap);
621 int flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0;
623 adp = of_ad(vol, path, &ad);
624 upath = path->u_name;
626 if ( ad_metadata( upath, flags|ADFLAGS_CREATE, adp) < 0 ) {
629 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
630 upath, strerror(errno));
631 return AFPERR_ACCESS;
633 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
642 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
644 ad_close_metadata( adp);
650 /* ----------------------------- */
651 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
653 struct adouble ad, *adp;
656 struct ofork *of = NULL;
658 int creatf, did, openf, retvalue = AFP_OK;
664 creatf = (unsigned char) *ibuf++;
666 memcpy(&vid, ibuf, sizeof( vid ));
667 ibuf += sizeof( vid );
669 if (NULL == ( vol = getvolbyvid( vid )) ) {
670 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 )) ) {
683 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
684 return get_afp_errno(AFPERR_PARAM);
687 if ( *s_path->m_name == '\0' ) {
688 return( AFPERR_BADTYPE );
691 upath = s_path->u_name;
693 /* if upath is deleted we already in trouble anyway */
694 if ((of = of_findname(s_path))) {
697 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
701 /* on a hard create, fail if file exists and is open */
704 openf = O_RDWR|O_CREAT|O_TRUNC;
706 /* on a soft create, if the file is open then ad_open won't fail
707 because open syscall is not called
712 openf = O_RDWR|O_CREAT|O_EXCL;
715 if ( ad_open( upath, ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF,
716 openf, 0666, adp) < 0 ) {
720 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
721 return ( AFPERR_NOOBJ );
723 return( AFPERR_EXIST );
725 return( AFPERR_ACCESS );
728 return( AFPERR_DFULL );
730 return( AFPERR_PARAM );
733 if ( ad_meta_fileno( adp ) == -1 ) { /* Hard META / HF */
734 /* on noadouble volumes, just creating the data fork is ok */
735 if (vol_noadouble(vol)) {
736 ad_close( adp, ADFLAGS_DF );
737 goto createfile_done;
739 /* FIXME with hard create on an existing file, we already
740 * corrupted the data file.
742 netatalk_unlink( upath );
743 ad_close( adp, ADFLAGS_DF );
744 return AFPERR_ACCESS;
747 path = s_path->m_name;
748 ad_setname(adp, path);
750 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
756 if (vol->v_flags & AFPVOL_DROPBOX) {
757 retvalue = matchfile2dirperms(upath, vol, did);
759 #endif /* DROPKLUDGE */
761 setvoltime(obj, vol );
766 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
772 u_int16_t vid, bitmap;
777 memcpy(&vid, ibuf, sizeof( vid ));
778 ibuf += sizeof( vid );
779 if (NULL == ( vol = getvolbyvid( vid )) ) {
780 return( AFPERR_PARAM );
783 if (vol->v_flags & AFPVOL_RO)
786 memcpy(&did, ibuf, sizeof( did ));
787 ibuf += sizeof( did );
788 if (NULL == ( dir = dirlookup( vol, did )) ) {
789 return afp_errno; /* was AFPERR_NOOBJ */
792 memcpy(&bitmap, ibuf, sizeof( bitmap ));
793 bitmap = ntohs( bitmap );
794 ibuf += sizeof( bitmap );
796 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
797 return get_afp_errno(AFPERR_PARAM);
800 if (path_isadir(s_path)) {
801 return( AFPERR_BADTYPE ); /* it's a directory */
804 if ( s_path->st_errno != 0 ) {
805 return( AFPERR_NOOBJ );
808 if ((u_long)ibuf & 1 ) {
812 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
813 setvoltime(obj, vol );
820 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
823 extern struct path Cur_Path;
825 int setfilparams(struct vol *vol,
826 struct path *path, u_int16_t f_bitmap, char *buf )
828 struct adouble ad, *adp;
830 int bit, isad = 1, err = AFP_OK;
832 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
833 u_int16_t ashort, bshort, oshort;
836 u_int16_t upriv_bit = 0;
840 int change_mdate = 0;
841 int change_parent_mdate = 0;
846 u_int16_t bitmap = f_bitmap;
847 u_int32_t cdate,bdate;
848 u_char finder_buf[32];
851 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
854 adp = of_ad(vol, path, &ad);
855 upath = path->u_name;
857 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
858 return AFPERR_ACCESS;
861 /* with unix priv maybe we have to change adouble file priv first */
863 while ( bitmap != 0 ) {
864 while (( bitmap & 1 ) == 0 ) {
871 memcpy(&ashort, buf, sizeof( ashort ));
872 buf += sizeof( ashort );
876 memcpy(&cdate, buf, sizeof(cdate));
877 buf += sizeof( cdate );
880 memcpy(&newdate, buf, sizeof( newdate ));
881 buf += sizeof( newdate );
885 memcpy(&bdate, buf, sizeof( bdate));
886 buf += sizeof( bdate );
890 memcpy(finder_buf, buf, 32 );
891 if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
896 char buf[PATH_MAX+1];
897 if ((fp=open(path->u_name,O_RDONLY))>=0){
898 if ((len=read(fp,buf,PATH_MAX+1))){
899 if (unlink(path->u_name)==0){
901 erc = symlink(buf, path->u_name);
910 goto setfilparam_done;
915 case FILPBIT_UNIXPR :
916 if (!vol_unix_priv(vol)) {
917 /* this volume doesn't use unix priv */
923 change_parent_mdate = 1;
925 memcpy( &aint, buf, sizeof( aint ));
926 f_uid = ntohl (aint);
927 buf += sizeof( aint );
928 memcpy( &aint, buf, sizeof( aint ));
929 f_gid = ntohl (aint);
930 buf += sizeof( aint );
931 setfilowner(vol, f_uid, f_gid, path);
933 memcpy( &upriv, buf, sizeof( upriv ));
934 buf += sizeof( upriv );
935 upriv = ntohl (upriv);
936 if ((upriv & S_IWUSR)) {
937 setfilunixmode(vol, path, upriv);
944 case FILPBIT_PDINFO :
945 if (afp_version < 30) { /* else it's UTF8 name */
948 /* Keep special case to support crlf translations */
949 if ((unsigned int) achar == 0x04) {
950 fdType = (u_char *)"TEXT";
953 xyy[0] = ( u_char ) 'p';
964 /* break while loop */
973 /* second try with adouble open
975 if ( ad_open_metadata( upath, 0, O_CREAT, adp) < 0) {
976 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
978 * For some things, we don't need an adouble header:
979 * - change of modification date
980 * - UNIX privs (Bug-ID #2863424)
982 if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
983 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
984 return AFPERR_ACCESS;
986 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
988 } else if ((ad_get_HF_flags( adp ) & O_CREAT) ) {
989 ad_setname(adp, path->m_name);
994 while ( bitmap != 0 ) {
995 while (( bitmap & 1 ) == 0 ) {
1002 ad_getattr(adp, &bshort);
1004 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1005 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1009 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1010 change_parent_mdate = 1;
1011 ad_setattr(adp, bshort);
1013 case FILPBIT_CDATE :
1014 ad_setdate(adp, AD_DATE_CREATE, cdate);
1016 case FILPBIT_MDATE :
1018 case FILPBIT_BDATE :
1019 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1021 case FILPBIT_FINFO :
1022 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1024 ((em = getextmap( path->m_name )) &&
1025 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1026 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1027 || ((em = getdefextmap()) &&
1028 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1029 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1031 memcpy(finder_buf, ufinderi, 8 );
1033 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1035 case FILPBIT_UNIXPR :
1037 setfilunixmode(vol, path, upriv);
1040 case FILPBIT_PDINFO :
1041 if (afp_version < 30) { /* else it's UTF8 name */
1042 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1043 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1048 err = AFPERR_BITMAP;
1049 goto setfilparam_done;
1056 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1057 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1061 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1062 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1068 ad_close_metadata( adp);
1072 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1073 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1074 bitmap = 1<<FILPBIT_MDATE;
1075 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1079 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1085 * renamefile and copyfile take the old and new unix pathnames
1086 * and the new mac name.
1088 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1089 * passing -1 means this is not used, src path is a full path
1090 * src the source path
1091 * dst the dest filename in current dir
1092 * newname the dest mac name
1093 * adp adouble struct of src file, if open, or & zeroed one
1096 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1100 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1103 return( AFPERR_NOOBJ );
1106 return( AFPERR_ACCESS );
1108 return AFPERR_VLOCK;
1109 case EXDEV : /* Cross device move -- try copy */
1110 /* NOTE: with open file it's an error because after the copy we will
1111 * get two files, it's fixable for our process (eg reopen the new file, get the
1112 * locks, and so on. But it doesn't solve the case with a second process
1114 if (adp->ad_open_forks) {
1115 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1116 return AFPERR_OLOCK; /* little lie */
1118 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1119 /* on error copyfile delete dest */
1122 return deletefile(vol, sdir_fd, src, 0);
1124 return( AFPERR_PARAM );
1128 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1132 /* try to undo the data fork rename,
1133 * we know we are on the same device
1136 unix_rename(-1, dst, sdir_fd, src );
1137 /* return the first error */
1140 return AFPERR_NOOBJ;
1143 return AFPERR_ACCESS ;
1145 return AFPERR_VLOCK;
1147 return AFPERR_PARAM ;
1152 /* don't care if we can't open the newly renamed ressource fork
1154 if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
1155 ad_setname(adp, newname);
1157 ad_close( adp, ADFLAGS_HF );
1164 convert a Mac long name to an utf8 name,
1166 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1170 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1176 /* ---------------- */
1177 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1184 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1190 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1191 if (afp_version >= 30) {
1192 /* convert it to UTF8
1194 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1198 strncpy( newname, ibuf, plen );
1199 newname[ plen ] = '\0';
1201 if (strlen(newname) != plen) {
1202 /* there's \0 in newname, e.g. it's a pathname not
1210 memcpy(&hint, ibuf, sizeof(hint));
1211 ibuf += sizeof(hint);
1213 memcpy(&len16, ibuf, sizeof(len16));
1214 ibuf += sizeof(len16);
1215 plen = ntohs(len16);
1218 if (plen > AFPOBJ_TMPSIZ) {
1221 strncpy( newname, ibuf, plen );
1222 newname[ plen ] = '\0';
1223 if (strlen(newname) != plen) {
1232 /* -----------------------------------
1234 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1236 struct vol *s_vol, *d_vol;
1238 char *newname, *p, *upath;
1239 struct path *s_path;
1240 u_int32_t sdid, ddid;
1241 int err, retvalue = AFP_OK;
1242 u_int16_t svid, dvid;
1244 struct adouble ad, *adp;
1250 memcpy(&svid, ibuf, sizeof( svid ));
1251 ibuf += sizeof( svid );
1252 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1253 return( AFPERR_PARAM );
1256 memcpy(&sdid, ibuf, sizeof( sdid ));
1257 ibuf += sizeof( sdid );
1258 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1262 memcpy(&dvid, ibuf, sizeof( dvid ));
1263 ibuf += sizeof( dvid );
1264 memcpy(&ddid, ibuf, sizeof( ddid ));
1265 ibuf += sizeof( ddid );
1267 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1268 return get_afp_errno(AFPERR_PARAM);
1270 if ( path_isadir(s_path) ) {
1271 return( AFPERR_BADTYPE );
1274 /* don't allow copies when the file is open.
1275 * XXX: the spec only calls for read/deny write access.
1276 * however, copyfile doesn't have any of that info,
1277 * and locks need to stay coherent. as a result,
1278 * we just balk if the file is opened already. */
1280 adp = of_ad(s_vol, s_path, &ad);
1282 if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1283 return AFPERR_DENYCONF;
1285 denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1286 getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1289 retvalue = AFPERR_DENYCONF;
1293 newname = obj->newtmp;
1294 strcpy( newname, s_path->m_name );
1296 p = ctoupath( s_vol, curdir, newname );
1298 retvalue = AFPERR_PARAM;
1303 /* FIXME svid != dvid && dvid's user can't read svid */
1305 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1306 retvalue = AFPERR_PARAM;
1310 if (d_vol->v_flags & AFPVOL_RO) {
1311 retvalue = AFPERR_VLOCK;
1315 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1316 retvalue = afp_errno;
1320 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1321 retvalue = get_afp_errno(AFPERR_NOOBJ);
1325 if ( *s_path->m_name != '\0' ) {
1326 retvalue =path_error(s_path, AFPERR_NOOBJ);
1330 /* one of the handful of places that knows about the path type */
1331 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1332 retvalue = AFPERR_PARAM;
1335 /* newname is always only a filename so curdir *is* its
1338 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1339 retvalue =AFPERR_PARAM;
1343 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1350 if (vol->v_flags & AFPVOL_DROPBOX) {
1351 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1353 #endif /* DROPKLUDGE */
1355 setvoltime(obj, d_vol );
1358 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1362 /* ----------------------- */
1363 static int copy_all(const int dfd, const void *buf,
1369 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1372 while (buflen > 0) {
1373 if ((cc = write(dfd, buf, buflen)) < 0) {
1385 LOG(log_debug9, logtype_afpd, "end copy_all:");
1391 /* --------------------------
1392 * copy only the fork data stream
1394 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1401 if (eid == ADEID_DFORK) {
1402 sfd = ad_data_fileno(ads);
1403 dfd = ad_data_fileno(add);
1406 sfd = ad_reso_fileno(ads);
1407 dfd = ad_reso_fileno(add);
1410 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1413 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1416 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1417 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1421 #define BUF 128*1024*1024
1423 if (fstat(sfd, &st) == 0) {
1426 if ( offset >= st.st_size) {
1429 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1430 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1433 case EINVAL: /* there's no guarantee that all fs support sendfile */
1442 lseek(sfd, offset, SEEK_SET);
1446 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1453 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1460 /* ----------------------------------
1461 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1462 * because we are doing it elsewhere.
1463 * currently if newname is NULL then adp is NULL.
1465 int copyfile(const struct vol *s_vol,
1466 const struct vol *d_vol,
1471 struct adouble *adp)
1473 struct adouble ads, add;
1480 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1481 sfd, src, dst, newname);
1484 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1488 adflags = ADFLAGS_DF;
1490 adflags |= ADFLAGS_HF;
1493 if (ad_openat(sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1498 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1499 /* no resource fork, don't create one for dst file */
1500 adflags &= ~ADFLAGS_HF;
1503 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1505 if (stat_result < 0) {
1506 /* unlikely but if fstat fails, the default file mode will be 0666. */
1507 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1510 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1511 if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1513 ad_close( adp, adflags );
1514 if (EEXIST != ret_err) {
1515 deletefile(d_vol, -1, dst, 0);
1518 return AFPERR_EXIST;
1522 * XXX if the source and the dest don't use the same resource type it's broken
1524 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1525 /* copy the data fork */
1526 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1527 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1535 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1536 /* set the new name in the resource fork */
1537 ad_copy_header(&add, adp);
1538 ad_setname(&add, newname);
1541 ad_close( adp, adflags );
1543 if (ad_close( &add, adflags ) <0) {
1548 deletefile(d_vol, -1, dst, 0);
1550 else if (stat_result == 0) {
1551 /* set dest modification date to src date */
1554 ut.actime = ut.modtime = st.st_mtime;
1556 /* FIXME netatalk doesn't use resource fork file date
1557 * but maybe we should set its modtime too.
1562 switch ( ret_err ) {
1568 return AFPERR_DFULL;
1570 return AFPERR_NOOBJ;
1572 return AFPERR_ACCESS;
1574 return AFPERR_VLOCK;
1576 return AFPERR_PARAM;
1580 /* -----------------------------------
1581 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1582 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1584 when deletefile is called we don't have lock on it, file is closed (for us)
1585 untrue if called by renamefile
1587 ad_open always try to open file RDWR first and ad_lock takes care of
1588 WRITE lock on read only file.
1591 static int check_attrib(struct adouble *adp)
1593 u_int16_t bshort = 0;
1595 ad_getattr(adp, &bshort);
1597 * Does kFPDeleteInhibitBit (bit 8) set?
1599 if ((bshort & htons(ATTRBIT_NODELETE))) {
1600 return AFPERR_OLOCK;
1602 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1608 * dirfd can be used for unlinkat semantics
1610 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1613 struct adouble *adp = NULL;
1614 int adflags, err = AFP_OK;
1617 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1619 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1621 /* was EACCESS error try to get only metadata */
1622 /* we never want to create a resource fork here, we are going to delete it
1623 * moreover sometimes deletefile is called with a no existent file and
1624 * ad_open would create a 0 byte resource fork
1626 if ( ad_metadataat(dirfd, file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
1627 if ((err = check_attrib(&ad))) {
1628 ad_close_metadata(&ad);
1635 /* try to open both forks at once */
1636 adflags = ADFLAGS_DF;
1637 if ( ad_openat(dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, 0, &ad ) < 0 ) {
1642 case EACCES: /* maybe it's a file with no write mode for us */
1643 break; /* was return AFPERR_ACCESS;*/
1656 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1657 adflags |= ADFLAGS_HF;
1658 /* FIXME we have a pb here because we want to know if a file is open
1659 * there's a 'priority inversion' if you can't open the ressource fork RW
1660 * you can delete it if it's open because you can't get a write lock.
1662 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1665 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1667 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1673 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1675 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1677 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1678 cnid_delete(vol->v_cdb, id);
1684 ad_close_metadata(&ad);
1687 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1692 /* ------------------------------------ */
1693 /* return a file id */
1694 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1703 struct path *s_path;
1709 memcpy(&vid, ibuf, sizeof(vid));
1710 ibuf += sizeof(vid);
1712 if (NULL == ( vol = getvolbyvid( vid )) ) {
1713 return( AFPERR_PARAM);
1716 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1720 if (vol->v_flags & AFPVOL_RO)
1721 return AFPERR_VLOCK;
1723 memcpy(&did, ibuf, sizeof( did ));
1724 ibuf += sizeof(did);
1726 if (NULL == ( dir = dirlookup( vol, did )) ) {
1727 return afp_errno; /* was AFPERR_PARAM */
1730 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1731 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1734 if ( path_isadir(s_path) ) {
1735 return( AFPERR_BADTYPE );
1738 upath = s_path->u_name;
1739 switch (s_path->st_errno) {
1741 break; /* success */
1744 return AFPERR_ACCESS;
1746 return AFPERR_NOOBJ;
1748 return AFPERR_PARAM;
1751 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1752 memcpy(rbuf, &id, sizeof(id));
1753 *rbuflen = sizeof(id);
1754 return AFPERR_EXISTID;
1757 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1758 memcpy(rbuf, &id, sizeof(id));
1759 *rbuflen = sizeof(id);
1766 /* ------------------------------- */
1772 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1775 struct reenum *param = data;
1776 struct vol *vol = param->vol;
1777 cnid_t did = param->did;
1780 if ( lstat(de->d_name, &path.st)<0 )
1783 /* update or add to cnid */
1784 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1786 #if AD_VERSION > AD_VERSION1
1787 if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1788 struct adouble ad, *adp;
1792 path.u_name = de->d_name;
1794 adp = of_ad(vol, &path, &ad);
1796 if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1799 if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1802 ad_close_metadata(adp);
1804 #endif /* AD_VERSION > AD_VERSION1 */
1809 /* --------------------
1810 * Ok the db is out of synch with the dir.
1811 * but if it's a deleted file we don't want to do it again and again.
1814 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1820 if (vol->v_cdb == NULL) {
1824 /* FIXME use of_statdir ? */
1825 if (lstat(name, &st)) {
1829 if (dirreenumerate(dir, &st)) {
1830 /* we already did it once and the dir haven't been modified */
1835 data.did = dir->d_did;
1836 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1837 setdiroffcnt(curdir, &st, ret);
1838 dir->d_flags |= DIRF_CNID;
1844 /* ------------------------------
1845 resolve a file id */
1846 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1855 u_int16_t vid, bitmap;
1857 static char buffer[12 + MAXPATHLEN + 1];
1858 int len = 12 + MAXPATHLEN + 1;
1863 memcpy(&vid, ibuf, sizeof(vid));
1864 ibuf += sizeof(vid);
1866 if (NULL == ( vol = getvolbyvid( vid )) ) {
1867 return( AFPERR_PARAM);
1870 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1874 memcpy(&id, ibuf, sizeof( id ));
1879 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1883 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1884 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1887 if (NULL == ( dir = dirlookup( vol, id )) ) {
1888 return AFPERR_NOID; /* idem AFPERR_PARAM */
1890 if (movecwd(vol, dir) < 0) {
1894 return AFPERR_ACCESS;
1898 return AFPERR_PARAM;
1902 memset(&path, 0, sizeof(path));
1903 path.u_name = upath;
1904 if ( of_stat(&path) < 0 ) {
1906 /* with nfs and our working directory is deleted */
1907 if (errno == ESTALE) {
1911 if ( errno == ENOENT && !retry) {
1912 /* cnid db is out of sync, reenumerate the directory and update ids */
1913 reenumerate_id(vol, ".", dir);
1921 return AFPERR_ACCESS;
1925 return AFPERR_PARAM;
1929 /* directories are bad */
1930 if (S_ISDIR(path.st.st_mode)) {
1931 /* OS9 and OSX don't return the same error code */
1932 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1935 memcpy(&bitmap, ibuf, sizeof(bitmap));
1936 bitmap = ntohs( bitmap );
1937 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1941 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1942 rbuf + sizeof(bitmap), &buflen))) {
1945 *rbuflen = buflen + sizeof(bitmap);
1946 memcpy(rbuf, ibuf, sizeof(bitmap));
1951 /* ------------------------------ */
1952 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1962 static char buffer[12 + MAXPATHLEN + 1];
1963 int len = 12 + MAXPATHLEN + 1;
1968 memcpy(&vid, ibuf, sizeof(vid));
1969 ibuf += sizeof(vid);
1971 if (NULL == ( vol = getvolbyvid( vid )) ) {
1972 return( AFPERR_PARAM);
1975 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1979 if (vol->v_flags & AFPVOL_RO)
1980 return AFPERR_VLOCK;
1982 memcpy(&id, ibuf, sizeof( id ));
1986 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1990 if (NULL == ( dir = dirlookup( vol, id )) ) {
1991 if (afp_errno == AFPERR_NOOBJ) {
1995 return( AFPERR_PARAM );
1999 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
2003 return AFPERR_ACCESS;
2008 /* still try to delete the id */
2012 return AFPERR_PARAM;
2015 else if (S_ISDIR(st.st_mode)) /* directories are bad */
2016 return AFPERR_BADTYPE;
2019 if (cnid_delete(vol->v_cdb, fileid)) {
2022 return AFPERR_VLOCK;
2025 return AFPERR_ACCESS;
2027 return AFPERR_PARAM;
2034 /* ------------------------------ */
2035 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
2039 if (path->st_errno) {
2040 switch (path->st_errno) {
2042 afp_errno = AFPERR_NOID;
2046 afp_errno = AFPERR_ACCESS;
2049 afp_errno = AFPERR_PARAM;
2054 /* we use file_access both for legacy Mac perm and
2055 * for unix privilege, rename will take care of folder perms
2057 if (file_access(path, OPENACC_WR ) < 0) {
2058 afp_errno = AFPERR_ACCESS;
2062 if ((*of = of_findname(path))) {
2063 /* reuse struct adouble so it won't break locks */
2067 ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
2069 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2071 * The user must have the Read & Write privilege for both files in order to use this command.
2073 ad_close(adp, ADFLAGS_HF);
2074 afp_errno = AFPERR_ACCESS;
2081 #define APPLETEMP ".AppleTempXXXXXX"
2083 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2085 struct stat srcst, destst;
2087 struct dir *dir, *sdir;
2088 char *spath, temp[17], *p;
2089 char *supath, *upath;
2094 struct adouble *adsp = NULL;
2095 struct adouble *addp = NULL;
2096 struct ofork *s_of = NULL;
2097 struct ofork *d_of = NULL;
2110 memcpy(&vid, ibuf, sizeof(vid));
2111 ibuf += sizeof(vid);
2113 if (NULL == ( vol = getvolbyvid( vid )) ) {
2114 return( AFPERR_PARAM);
2117 if ((vol->v_flags & AFPVOL_RO))
2118 return AFPERR_VLOCK;
2120 /* source and destination dids */
2121 memcpy(&sid, ibuf, sizeof(sid));
2122 ibuf += sizeof(sid);
2123 memcpy(&did, ibuf, sizeof(did));
2124 ibuf += sizeof(did);
2127 if (NULL == (dir = dirlookup( vol, sid )) ) {
2128 return afp_errno; /* was AFPERR_PARAM */
2131 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2132 return get_afp_errno(AFPERR_NOOBJ);
2135 if ( path_isadir(path) ) {
2136 return AFPERR_BADTYPE; /* it's a dir */
2139 /* save some stuff */
2142 spath = obj->oldtmp;
2143 supath = obj->newtmp;
2144 strcpy(spath, path->m_name);
2145 strcpy(supath, path->u_name); /* this is for the cnid changing */
2146 p = absupath( vol, sdir, supath);
2148 /* pathname too long */
2149 return AFPERR_PARAM ;
2152 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2153 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2157 /* ***** from here we may have resource fork open **** */
2159 /* look for the source cnid. if it doesn't exist, don't worry about
2161 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2163 if (NULL == ( dir = dirlookup( vol, did )) ) {
2164 err = afp_errno; /* was AFPERR_PARAM */
2165 goto err_exchangefile;
2168 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2169 err = get_afp_errno(AFPERR_NOOBJ);
2170 goto err_exchangefile;
2173 if ( path_isadir(path) ) {
2174 err = AFPERR_BADTYPE;
2175 goto err_exchangefile;
2178 /* FPExchangeFiles is the only call that can return the SameObj
2180 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2181 err = AFPERR_SAMEOBJ;
2182 goto err_exchangefile;
2185 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2186 if (!(addp = find_adouble( path, &d_of, &add))) {
2188 goto err_exchangefile;
2192 /* they are not on the same device and at least one is open
2193 * FIXME broken for for crossdev and adouble v2
2196 crossdev = (srcst.st_dev != destst.st_dev);
2197 if (/* (d_of || s_of) && */ crossdev) {
2199 goto err_exchangefile;
2202 /* look for destination id. */
2203 upath = path->u_name;
2204 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2206 /* construct a temp name.
2207 * NOTE: the temp file will be in the dest file's directory. it
2208 * will also be inaccessible from AFP. */
2209 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2210 if (!mktemp(temp)) {
2212 goto err_exchangefile;
2216 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2217 ad_close(adsp, ADFLAGS_HF);
2218 ad_close(addp, ADFLAGS_HF);
2221 /* now, quickly rename the file. we error if we can't. */
2222 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2223 goto err_exchangefile;
2224 of_rename(vol, s_of, sdir, spath, curdir, temp);
2226 /* rename destination to source */
2227 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2228 goto err_src_to_tmp;
2229 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2231 /* rename temp to destination */
2232 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2233 goto err_dest_to_src;
2234 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2236 /* id's need switching. src -> dest and dest -> src.
2237 * we need to re-stat() if it was a cross device copy.
2240 cnid_delete(vol->v_cdb, sid);
2242 cnid_delete(vol->v_cdb, did);
2244 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2245 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2247 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2248 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2253 err = AFPERR_ACCESS;
2258 goto err_temp_to_dest;
2261 /* here we need to reopen if crossdev */
2262 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2267 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2272 /* change perms, src gets dest perm and vice versa */
2277 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2278 err = AFP_OK; /* ignore error */
2279 goto err_temp_to_dest;
2283 * we need to exchange ACL entries as well
2285 /* exchange_acls(vol, p, upath); */
2290 path->m_name = NULL;
2291 path->u_name = upath;
2293 setfilunixmode(vol, path, destst.st_mode);
2294 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2301 setfilunixmode(vol, path, srcst.st_mode);
2302 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2304 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2305 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2310 goto err_exchangefile;
2312 /* all this stuff is so that we can unwind a failed operation
2315 /* rename dest to temp */
2316 renamefile(vol, -1, upath, temp, temp, adsp);
2317 of_rename(vol, s_of, curdir, upath, curdir, temp);
2320 /* rename source back to dest */
2321 renamefile(vol, -1, p, upath, path->m_name, addp);
2322 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2325 /* rename temp back to source */
2326 renamefile(vol, -1, temp, p, spath, adsp);
2327 of_rename(vol, s_of, curdir, temp, sdir, spath);
2330 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2331 ad_close(adsp, ADFLAGS_HF);
2333 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2334 ad_close(addp, ADFLAGS_HF);
2338 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2339 (void)dir_remove(vol, cached);
2340 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2341 (void)dir_remove(vol, cached);