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 > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
169 aint = UTF8FILELEN_EARLY;
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|ADFLAGS_CREATE,
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_reso_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 LOG(log_debug, logtype_afpd,
1101 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1103 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1106 return( AFPERR_NOOBJ );
1109 return( AFPERR_ACCESS );
1111 return AFPERR_VLOCK;
1112 case EXDEV : /* Cross device move -- try copy */
1113 /* NOTE: with open file it's an error because after the copy we will
1114 * get two files, it's fixable for our process (eg reopen the new file, get the
1115 * locks, and so on. But it doesn't solve the case with a second process
1117 if (adp->ad_open_forks) {
1118 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1119 return AFPERR_OLOCK; /* little lie */
1121 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1122 /* on error copyfile delete dest */
1125 return deletefile(vol, sdir_fd, src, 0);
1127 return( AFPERR_PARAM );
1131 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1135 /* try to undo the data fork rename,
1136 * we know we are on the same device
1139 unix_rename(-1, dst, sdir_fd, src );
1140 /* return the first error */
1143 return AFPERR_NOOBJ;
1146 return AFPERR_ACCESS ;
1148 return AFPERR_VLOCK;
1150 return AFPERR_PARAM ;
1155 /* don't care if we can't open the newly renamed ressource fork
1157 if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
1158 ad_setname(adp, newname);
1160 ad_close( adp, ADFLAGS_HF );
1167 convert a Mac long name to an utf8 name,
1169 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1173 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1179 /* ---------------- */
1180 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1187 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1193 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1194 if (afp_version >= 30) {
1195 /* convert it to UTF8
1197 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1201 strncpy( newname, ibuf, plen );
1202 newname[ plen ] = '\0';
1204 if (strlen(newname) != plen) {
1205 /* there's \0 in newname, e.g. it's a pathname not
1213 memcpy(&hint, ibuf, sizeof(hint));
1214 ibuf += sizeof(hint);
1216 memcpy(&len16, ibuf, sizeof(len16));
1217 ibuf += sizeof(len16);
1218 plen = ntohs(len16);
1221 if (plen > AFPOBJ_TMPSIZ) {
1224 strncpy( newname, ibuf, plen );
1225 newname[ plen ] = '\0';
1226 if (strlen(newname) != plen) {
1235 /* -----------------------------------
1237 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1239 struct vol *s_vol, *d_vol;
1241 char *newname, *p, *upath;
1242 struct path *s_path;
1243 u_int32_t sdid, ddid;
1244 int err, retvalue = AFP_OK;
1245 u_int16_t svid, dvid;
1247 struct adouble ad, *adp;
1253 memcpy(&svid, ibuf, sizeof( svid ));
1254 ibuf += sizeof( svid );
1255 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1256 return( AFPERR_PARAM );
1259 memcpy(&sdid, ibuf, sizeof( sdid ));
1260 ibuf += sizeof( sdid );
1261 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1265 memcpy(&dvid, ibuf, sizeof( dvid ));
1266 ibuf += sizeof( dvid );
1267 memcpy(&ddid, ibuf, sizeof( ddid ));
1268 ibuf += sizeof( ddid );
1270 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1271 return get_afp_errno(AFPERR_PARAM);
1273 if ( path_isadir(s_path) ) {
1274 return( AFPERR_BADTYPE );
1277 /* don't allow copies when the file is open.
1278 * XXX: the spec only calls for read/deny write access.
1279 * however, copyfile doesn't have any of that info,
1280 * and locks need to stay coherent. as a result,
1281 * we just balk if the file is opened already. */
1283 adp = of_ad(s_vol, s_path, &ad);
1285 if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1286 return AFPERR_DENYCONF;
1288 denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1289 getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1292 retvalue = AFPERR_DENYCONF;
1296 newname = obj->newtmp;
1297 strcpy( newname, s_path->m_name );
1299 p = ctoupath( s_vol, curdir, newname );
1301 retvalue = AFPERR_PARAM;
1306 /* FIXME svid != dvid && dvid's user can't read svid */
1308 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1309 retvalue = AFPERR_PARAM;
1313 if (d_vol->v_flags & AFPVOL_RO) {
1314 retvalue = AFPERR_VLOCK;
1318 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1319 retvalue = afp_errno;
1323 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1324 retvalue = get_afp_errno(AFPERR_NOOBJ);
1328 if ( *s_path->m_name != '\0' ) {
1329 retvalue =path_error(s_path, AFPERR_NOOBJ);
1333 /* one of the handful of places that knows about the path type */
1334 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1335 retvalue = AFPERR_PARAM;
1338 /* newname is always only a filename so curdir *is* its
1341 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1342 retvalue =AFPERR_PARAM;
1346 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1353 if (vol->v_flags & AFPVOL_DROPBOX) {
1354 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1356 #endif /* DROPKLUDGE */
1358 setvoltime(obj, d_vol );
1361 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1365 /* ----------------------- */
1366 static int copy_all(const int dfd, const void *buf,
1372 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1375 while (buflen > 0) {
1376 if ((cc = write(dfd, buf, buflen)) < 0) {
1388 LOG(log_debug9, logtype_afpd, "end copy_all:");
1394 /* --------------------------
1395 * copy only the fork data stream
1397 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1404 if (eid == ADEID_DFORK) {
1405 sfd = ad_data_fileno(ads);
1406 dfd = ad_data_fileno(add);
1409 sfd = ad_reso_fileno(ads);
1410 dfd = ad_reso_fileno(add);
1413 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1416 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1419 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1420 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1424 #define BUF 128*1024*1024
1426 if (fstat(sfd, &st) == 0) {
1429 if ( offset >= st.st_size) {
1432 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1433 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1436 case EINVAL: /* there's no guarantee that all fs support sendfile */
1445 lseek(sfd, offset, SEEK_SET);
1449 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1456 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1463 /* ----------------------------------
1464 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1465 * because we are doing it elsewhere.
1466 * currently if newname is NULL then adp is NULL.
1468 int copyfile(const struct vol *s_vol,
1469 const struct vol *d_vol,
1474 struct adouble *adp)
1476 struct adouble ads, add;
1483 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1484 sfd, src, dst, newname);
1487 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1491 adflags = ADFLAGS_DF;
1493 adflags |= ADFLAGS_HF;
1496 if (ad_openat(sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1501 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1502 /* no resource fork, don't create one for dst file */
1503 adflags &= ~ADFLAGS_HF;
1506 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1508 if (stat_result < 0) {
1509 /* unlikely but if fstat fails, the default file mode will be 0666. */
1510 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1513 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1514 if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1516 ad_close( adp, adflags );
1517 if (EEXIST != ret_err) {
1518 deletefile(d_vol, -1, dst, 0);
1521 return AFPERR_EXIST;
1525 * XXX if the source and the dest don't use the same resource type it's broken
1527 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1528 /* copy the data fork */
1529 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1530 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1538 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1539 /* set the new name in the resource fork */
1540 ad_copy_header(&add, adp);
1541 ad_setname(&add, newname);
1544 ad_close( adp, adflags );
1546 if (ad_close( &add, adflags ) <0) {
1551 deletefile(d_vol, -1, dst, 0);
1553 else if (stat_result == 0) {
1554 /* set dest modification date to src date */
1557 ut.actime = ut.modtime = st.st_mtime;
1559 /* FIXME netatalk doesn't use resource fork file date
1560 * but maybe we should set its modtime too.
1565 switch ( ret_err ) {
1571 return AFPERR_DFULL;
1573 return AFPERR_NOOBJ;
1575 return AFPERR_ACCESS;
1577 return AFPERR_VLOCK;
1579 return AFPERR_PARAM;
1583 /* -----------------------------------
1584 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1585 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1587 when deletefile is called we don't have lock on it, file is closed (for us)
1588 untrue if called by renamefile
1590 ad_open always try to open file RDWR first and ad_lock takes care of
1591 WRITE lock on read only file.
1594 static int check_attrib(struct adouble *adp)
1596 u_int16_t bshort = 0;
1598 ad_getattr(adp, &bshort);
1600 * Does kFPDeleteInhibitBit (bit 8) set?
1602 if ((bshort & htons(ATTRBIT_NODELETE))) {
1603 return AFPERR_OLOCK;
1605 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1611 * dirfd can be used for unlinkat semantics
1613 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1616 struct adouble *adp = NULL;
1617 int adflags, err = AFP_OK;
1620 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1622 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1624 /* was EACCESS error try to get only metadata */
1625 /* we never want to create a resource fork here, we are going to delete it
1626 * moreover sometimes deletefile is called with a no existent file and
1627 * ad_open would create a 0 byte resource fork
1629 if ( ad_metadataat(dirfd, file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
1630 if ((err = check_attrib(&ad))) {
1631 ad_close_metadata(&ad);
1638 /* try to open both forks at once */
1639 adflags = ADFLAGS_DF;
1640 if ( ad_openat(dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, 0, &ad ) < 0 ) {
1645 case EACCES: /* maybe it's a file with no write mode for us */
1646 break; /* was return AFPERR_ACCESS;*/
1659 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1660 adflags |= ADFLAGS_HF;
1661 /* FIXME we have a pb here because we want to know if a file is open
1662 * there's a 'priority inversion' if you can't open the ressource fork RW
1663 * you can delete it if it's open because you can't get a write lock.
1665 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1668 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1670 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1676 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1678 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1680 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1681 cnid_delete(vol->v_cdb, id);
1687 ad_close_metadata(&ad);
1690 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1695 /* ------------------------------------ */
1696 /* return a file id */
1697 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1706 struct path *s_path;
1712 memcpy(&vid, ibuf, sizeof(vid));
1713 ibuf += sizeof(vid);
1715 if (NULL == ( vol = getvolbyvid( vid )) ) {
1716 return( AFPERR_PARAM);
1719 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1723 if (vol->v_flags & AFPVOL_RO)
1724 return AFPERR_VLOCK;
1726 memcpy(&did, ibuf, sizeof( did ));
1727 ibuf += sizeof(did);
1729 if (NULL == ( dir = dirlookup( vol, did )) ) {
1730 return afp_errno; /* was AFPERR_PARAM */
1733 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1734 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1737 if ( path_isadir(s_path) ) {
1738 return( AFPERR_BADTYPE );
1741 upath = s_path->u_name;
1742 switch (s_path->st_errno) {
1744 break; /* success */
1747 return AFPERR_ACCESS;
1749 return AFPERR_NOOBJ;
1751 return AFPERR_PARAM;
1754 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1755 memcpy(rbuf, &id, sizeof(id));
1756 *rbuflen = sizeof(id);
1757 return AFPERR_EXISTID;
1760 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1761 memcpy(rbuf, &id, sizeof(id));
1762 *rbuflen = sizeof(id);
1769 /* ------------------------------- */
1775 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1778 struct reenum *param = data;
1779 struct vol *vol = param->vol;
1780 cnid_t did = param->did;
1783 if ( lstat(de->d_name, &path.st)<0 )
1786 /* update or add to cnid */
1787 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1789 #if AD_VERSION > AD_VERSION1
1790 if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1791 struct adouble ad, *adp;
1795 path.u_name = de->d_name;
1797 adp = of_ad(vol, &path, &ad);
1799 if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1802 if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1805 ad_close_metadata(adp);
1807 #endif /* AD_VERSION > AD_VERSION1 */
1812 /* --------------------
1813 * Ok the db is out of synch with the dir.
1814 * but if it's a deleted file we don't want to do it again and again.
1817 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1823 if (vol->v_cdb == NULL) {
1827 /* FIXME use of_statdir ? */
1828 if (lstat(name, &st)) {
1832 if (dirreenumerate(dir, &st)) {
1833 /* we already did it once and the dir haven't been modified */
1838 data.did = dir->d_did;
1839 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1840 setdiroffcnt(curdir, &st, ret);
1841 dir->d_flags |= DIRF_CNID;
1847 /* ------------------------------
1848 resolve a file id */
1849 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1858 u_int16_t vid, bitmap;
1860 static char buffer[12 + MAXPATHLEN + 1];
1861 int len = 12 + MAXPATHLEN + 1;
1866 memcpy(&vid, ibuf, sizeof(vid));
1867 ibuf += sizeof(vid);
1869 if (NULL == ( vol = getvolbyvid( vid )) ) {
1870 return( AFPERR_PARAM);
1873 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1877 memcpy(&id, ibuf, sizeof( id ));
1882 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1886 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1887 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1890 if (NULL == ( dir = dirlookup( vol, id )) ) {
1891 return AFPERR_NOID; /* idem AFPERR_PARAM */
1893 if (movecwd(vol, dir) < 0) {
1897 return AFPERR_ACCESS;
1901 return AFPERR_PARAM;
1905 memset(&path, 0, sizeof(path));
1906 path.u_name = upath;
1907 if ( of_stat(&path) < 0 ) {
1909 /* with nfs and our working directory is deleted */
1910 if (errno == ESTALE) {
1914 if ( errno == ENOENT && !retry) {
1915 /* cnid db is out of sync, reenumerate the directory and update ids */
1916 reenumerate_id(vol, ".", dir);
1924 return AFPERR_ACCESS;
1928 return AFPERR_PARAM;
1932 /* directories are bad */
1933 if (S_ISDIR(path.st.st_mode)) {
1934 /* OS9 and OSX don't return the same error code */
1935 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1938 memcpy(&bitmap, ibuf, sizeof(bitmap));
1939 bitmap = ntohs( bitmap );
1940 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1944 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1945 rbuf + sizeof(bitmap), &buflen))) {
1948 *rbuflen = buflen + sizeof(bitmap);
1949 memcpy(rbuf, ibuf, sizeof(bitmap));
1954 /* ------------------------------ */
1955 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1965 static char buffer[12 + MAXPATHLEN + 1];
1966 int len = 12 + MAXPATHLEN + 1;
1971 memcpy(&vid, ibuf, sizeof(vid));
1972 ibuf += sizeof(vid);
1974 if (NULL == ( vol = getvolbyvid( vid )) ) {
1975 return( AFPERR_PARAM);
1978 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1982 if (vol->v_flags & AFPVOL_RO)
1983 return AFPERR_VLOCK;
1985 memcpy(&id, ibuf, sizeof( id ));
1989 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1993 if (NULL == ( dir = dirlookup( vol, id )) ) {
1994 if (afp_errno == AFPERR_NOOBJ) {
1998 return( AFPERR_PARAM );
2002 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
2006 return AFPERR_ACCESS;
2011 /* still try to delete the id */
2015 return AFPERR_PARAM;
2018 else if (S_ISDIR(st.st_mode)) /* directories are bad */
2019 return AFPERR_BADTYPE;
2022 if (cnid_delete(vol->v_cdb, fileid)) {
2025 return AFPERR_VLOCK;
2028 return AFPERR_ACCESS;
2030 return AFPERR_PARAM;
2037 /* ------------------------------ */
2038 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
2042 if (path->st_errno) {
2043 switch (path->st_errno) {
2045 afp_errno = AFPERR_NOID;
2049 afp_errno = AFPERR_ACCESS;
2052 afp_errno = AFPERR_PARAM;
2057 /* we use file_access both for legacy Mac perm and
2058 * for unix privilege, rename will take care of folder perms
2060 if (file_access(path, OPENACC_WR ) < 0) {
2061 afp_errno = AFPERR_ACCESS;
2065 if ((*of = of_findname(path))) {
2066 /* reuse struct adouble so it won't break locks */
2070 ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
2072 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2074 * The user must have the Read & Write privilege for both files in order to use this command.
2076 ad_close(adp, ADFLAGS_HF);
2077 afp_errno = AFPERR_ACCESS;
2084 #define APPLETEMP ".AppleTempXXXXXX"
2086 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2088 struct stat srcst, destst;
2090 struct dir *dir, *sdir;
2091 char *spath, temp[17], *p;
2092 char *supath, *upath;
2097 struct adouble *adsp = NULL;
2098 struct adouble *addp = NULL;
2099 struct ofork *s_of = NULL;
2100 struct ofork *d_of = NULL;
2113 memcpy(&vid, ibuf, sizeof(vid));
2114 ibuf += sizeof(vid);
2116 if (NULL == ( vol = getvolbyvid( vid )) ) {
2117 return( AFPERR_PARAM);
2120 if ((vol->v_flags & AFPVOL_RO))
2121 return AFPERR_VLOCK;
2123 /* source and destination dids */
2124 memcpy(&sid, ibuf, sizeof(sid));
2125 ibuf += sizeof(sid);
2126 memcpy(&did, ibuf, sizeof(did));
2127 ibuf += sizeof(did);
2130 if (NULL == (dir = dirlookup( vol, sid )) ) {
2131 return afp_errno; /* was AFPERR_PARAM */
2134 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2135 return get_afp_errno(AFPERR_NOOBJ);
2138 if ( path_isadir(path) ) {
2139 return AFPERR_BADTYPE; /* it's a dir */
2142 /* save some stuff */
2145 spath = obj->oldtmp;
2146 supath = obj->newtmp;
2147 strcpy(spath, path->m_name);
2148 strcpy(supath, path->u_name); /* this is for the cnid changing */
2149 p = absupath( vol, sdir, supath);
2151 /* pathname too long */
2152 return AFPERR_PARAM ;
2155 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2156 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2160 /* ***** from here we may have resource fork open **** */
2162 /* look for the source cnid. if it doesn't exist, don't worry about
2164 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2166 if (NULL == ( dir = dirlookup( vol, did )) ) {
2167 err = afp_errno; /* was AFPERR_PARAM */
2168 goto err_exchangefile;
2171 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2172 err = get_afp_errno(AFPERR_NOOBJ);
2173 goto err_exchangefile;
2176 if ( path_isadir(path) ) {
2177 err = AFPERR_BADTYPE;
2178 goto err_exchangefile;
2181 /* FPExchangeFiles is the only call that can return the SameObj
2183 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2184 err = AFPERR_SAMEOBJ;
2185 goto err_exchangefile;
2188 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2189 if (!(addp = find_adouble( path, &d_of, &add))) {
2191 goto err_exchangefile;
2195 /* they are not on the same device and at least one is open
2196 * FIXME broken for for crossdev and adouble v2
2199 crossdev = (srcst.st_dev != destst.st_dev);
2200 if (/* (d_of || s_of) && */ crossdev) {
2202 goto err_exchangefile;
2205 /* look for destination id. */
2206 upath = path->u_name;
2207 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2209 /* construct a temp name.
2210 * NOTE: the temp file will be in the dest file's directory. it
2211 * will also be inaccessible from AFP. */
2212 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2213 if (!mktemp(temp)) {
2215 goto err_exchangefile;
2219 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2220 ad_close(adsp, ADFLAGS_HF);
2221 ad_close(addp, ADFLAGS_HF);
2224 /* now, quickly rename the file. we error if we can't. */
2225 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2226 goto err_exchangefile;
2227 of_rename(vol, s_of, sdir, spath, curdir, temp);
2229 /* rename destination to source */
2230 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2231 goto err_src_to_tmp;
2232 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2234 /* rename temp to destination */
2235 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2236 goto err_dest_to_src;
2237 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2239 /* id's need switching. src -> dest and dest -> src.
2240 * we need to re-stat() if it was a cross device copy.
2243 cnid_delete(vol->v_cdb, sid);
2245 cnid_delete(vol->v_cdb, did);
2247 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2248 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2250 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2251 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2256 err = AFPERR_ACCESS;
2261 goto err_temp_to_dest;
2264 /* here we need to reopen if crossdev */
2265 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2270 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2275 /* change perms, src gets dest perm and vice versa */
2280 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2281 err = AFP_OK; /* ignore error */
2282 goto err_temp_to_dest;
2286 * we need to exchange ACL entries as well
2288 /* exchange_acls(vol, p, upath); */
2293 path->m_name = NULL;
2294 path->u_name = upath;
2296 setfilunixmode(vol, path, destst.st_mode);
2297 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2304 setfilunixmode(vol, path, srcst.st_mode);
2305 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2307 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2308 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2313 goto err_exchangefile;
2315 /* all this stuff is so that we can unwind a failed operation
2318 /* rename dest to temp */
2319 renamefile(vol, -1, upath, temp, temp, adsp);
2320 of_rename(vol, s_of, curdir, upath, curdir, temp);
2323 /* rename source back to dest */
2324 renamefile(vol, -1, p, upath, path->m_name, addp);
2325 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2328 /* rename temp back to source */
2329 renamefile(vol, -1, temp, p, spath, adsp);
2330 of_rename(vol, s_of, curdir, temp, sdir, spath);
2333 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2334 ad_close(adsp, ADFLAGS_HF);
2336 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2337 ad_close(addp, ADFLAGS_HF);
2341 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2342 (void)dir_remove(vol, cached);
2343 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2344 (void)dir_remove(vol, cached);