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_CHECK_OF : 0;
623 adp = of_ad(vol, path, &ad);
624 upath = path->u_name;
626 if ( ad_metadata( upath, flags, 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);
643 ad_close_metadata( adp);
648 /* ----------------------------- */
649 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
654 struct ofork *of = NULL;
656 int creatf, did, openf, retvalue = AFP_OK;
662 creatf = (unsigned char) *ibuf++;
664 memcpy(&vid, ibuf, sizeof( vid ));
665 ibuf += sizeof( vid );
667 if (NULL == ( vol = getvolbyvid( vid )) )
668 return( AFPERR_PARAM );
670 if (vol->v_flags & AFPVOL_RO)
673 memcpy(&did, ibuf, sizeof( did));
674 ibuf += sizeof( did );
676 if (NULL == ( dir = dirlookup( vol, did )) )
679 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
680 return get_afp_errno(AFPERR_PARAM);
682 if ( *s_path->m_name == '\0' )
683 return( AFPERR_BADTYPE );
685 upath = s_path->u_name;
686 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
688 /* if upath is deleted we already in trouble anyway */
689 if ((of = of_findname(s_path))) {
697 openf = O_RDWR|O_CREAT|O_TRUNC;
699 /* on a soft create, if the file is open then ad_open won't fail
700 because open syscall is not called */
701 openf = O_RDWR|O_CREAT|O_EXCL;
703 if ( ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF,
704 openf, 0666, openf, 0666) < 0 ) {
708 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
709 return ( AFPERR_NOOBJ );
711 return( AFPERR_EXIST );
713 return( AFPERR_ACCESS );
716 return( AFPERR_DFULL );
718 return( AFPERR_PARAM );
721 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
722 /* on noadouble volumes, just creating the data fork is ok */
723 if (vol_noadouble(vol)) {
724 ad_close( &ad, ADFLAGS_DF );
725 goto createfile_done;
727 /* FIXME with hard create on an existing file, we already
728 * corrupted the data file.
730 netatalk_unlink( upath );
731 ad_close( &ad, ADFLAGS_DF );
732 return AFPERR_ACCESS;
735 path = s_path->m_name;
736 ad_setname(&ad, path);
738 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
744 if (vol->v_flags & AFPVOL_DROPBOX) {
745 retvalue = matchfile2dirperms(upath, vol, did);
747 #endif /* DROPKLUDGE */
749 setvoltime(obj, vol );
754 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
760 u_int16_t vid, bitmap;
765 memcpy(&vid, ibuf, sizeof( vid ));
766 ibuf += sizeof( vid );
767 if (NULL == ( vol = getvolbyvid( vid )) ) {
768 return( AFPERR_PARAM );
771 if (vol->v_flags & AFPVOL_RO)
774 memcpy(&did, ibuf, sizeof( did ));
775 ibuf += sizeof( did );
776 if (NULL == ( dir = dirlookup( vol, did )) ) {
777 return afp_errno; /* was AFPERR_NOOBJ */
780 memcpy(&bitmap, ibuf, sizeof( bitmap ));
781 bitmap = ntohs( bitmap );
782 ibuf += sizeof( bitmap );
784 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
785 return get_afp_errno(AFPERR_PARAM);
788 if (path_isadir(s_path)) {
789 return( AFPERR_BADTYPE ); /* it's a directory */
792 if ( s_path->st_errno != 0 ) {
793 return( AFPERR_NOOBJ );
796 if ((u_long)ibuf & 1 ) {
800 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
801 setvoltime(obj, vol );
808 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
811 extern struct path Cur_Path;
813 int setfilparams(struct vol *vol,
814 struct path *path, u_int16_t f_bitmap, char *buf )
816 struct adouble ad, *adp;
818 int bit, isad = 1, err = AFP_OK;
820 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
821 u_int16_t ashort, bshort, oshort;
824 u_int16_t upriv_bit = 0;
828 int change_mdate = 0;
829 int change_parent_mdate = 0;
834 u_int16_t bitmap = f_bitmap;
835 u_int32_t cdate,bdate;
836 u_char finder_buf[32];
839 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
842 adp = of_ad(vol, path, &ad);
843 upath = path->u_name;
845 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
846 return AFPERR_ACCESS;
849 /* with unix priv maybe we have to change adouble file priv first */
851 while ( bitmap != 0 ) {
852 while (( bitmap & 1 ) == 0 ) {
859 memcpy(&ashort, buf, sizeof( ashort ));
860 buf += sizeof( ashort );
864 memcpy(&cdate, buf, sizeof(cdate));
865 buf += sizeof( cdate );
868 memcpy(&newdate, buf, sizeof( newdate ));
869 buf += sizeof( newdate );
873 memcpy(&bdate, buf, sizeof( bdate));
874 buf += sizeof( bdate );
878 memcpy(finder_buf, buf, 32 );
879 if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
884 char buf[PATH_MAX+1];
885 if ((fp=open(path->u_name,O_RDONLY))>=0){
886 if ((len=read(fp,buf,PATH_MAX+1))){
887 if (unlink(path->u_name)==0){
889 erc = symlink(buf, path->u_name);
898 goto setfilparam_done;
903 case FILPBIT_UNIXPR :
904 if (!vol_unix_priv(vol)) {
905 /* this volume doesn't use unix priv */
911 change_parent_mdate = 1;
913 memcpy( &aint, buf, sizeof( aint ));
914 f_uid = ntohl (aint);
915 buf += sizeof( aint );
916 memcpy( &aint, buf, sizeof( aint ));
917 f_gid = ntohl (aint);
918 buf += sizeof( aint );
919 setfilowner(vol, f_uid, f_gid, path);
921 memcpy( &upriv, buf, sizeof( upriv ));
922 buf += sizeof( upriv );
923 upriv = ntohl (upriv);
924 if ((upriv & S_IWUSR)) {
925 setfilunixmode(vol, path, upriv);
932 case FILPBIT_PDINFO :
933 if (afp_version < 30) { /* else it's UTF8 name */
936 /* Keep special case to support crlf translations */
937 if ((unsigned int) achar == 0x04) {
938 fdType = (u_char *)"TEXT";
941 xyy[0] = ( u_char ) 'p';
952 /* break while loop */
961 /* second try with adouble open
963 if ( ad_open(adp, upath, ADFLAGS_HF, O_RDWR | O_CREAT, 0666) < 0) {
964 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
966 * For some things, we don't need an adouble header:
967 * - change of modification date
968 * - UNIX privs (Bug-ID #2863424)
970 if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
971 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
972 return AFPERR_ACCESS;
974 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
976 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
977 ad_setname(adp, path->m_name);
982 while ( bitmap != 0 ) {
983 while (( bitmap & 1 ) == 0 ) {
990 ad_getattr(adp, &bshort);
992 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
993 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
997 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
998 change_parent_mdate = 1;
999 ad_setattr(adp, bshort);
1001 case FILPBIT_CDATE :
1002 ad_setdate(adp, AD_DATE_CREATE, cdate);
1004 case FILPBIT_MDATE :
1006 case FILPBIT_BDATE :
1007 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1009 case FILPBIT_FINFO :
1010 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1012 ((em = getextmap( path->m_name )) &&
1013 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1014 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1015 || ((em = getdefextmap()) &&
1016 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1017 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1019 memcpy(finder_buf, ufinderi, 8 );
1021 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1023 case FILPBIT_UNIXPR :
1025 setfilunixmode(vol, path, upriv);
1028 case FILPBIT_PDINFO :
1029 if (afp_version < 30) { /* else it's UTF8 name */
1030 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1031 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1036 err = AFPERR_BITMAP;
1037 goto setfilparam_done;
1044 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1045 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1049 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1050 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1056 ad_close_metadata( adp);
1059 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1060 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1061 bitmap = 1<<FILPBIT_MDATE;
1062 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1066 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1072 * renamefile and copyfile take the old and new unix pathnames
1073 * and the new mac name.
1075 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1076 * passing -1 means this is not used, src path is a full path
1077 * src the source path
1078 * dst the dest filename in current dir
1079 * newname the dest mac name
1080 * adp adouble struct of src file, if open, or & zeroed one
1083 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1087 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1090 return( AFPERR_NOOBJ );
1093 return( AFPERR_ACCESS );
1095 return AFPERR_VLOCK;
1096 case EXDEV : /* Cross device move -- try copy */
1097 /* NOTE: with open file it's an error because after the copy we will
1098 * get two files, it's fixable for our process (eg reopen the new file, get the
1099 * locks, and so on. But it doesn't solve the case with a second process
1101 if (adp->ad_open_forks) {
1102 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1103 return AFPERR_OLOCK; /* little lie */
1105 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1106 /* on error copyfile delete dest */
1109 return deletefile(vol, sdir_fd, src, 0);
1111 return( AFPERR_PARAM );
1115 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1119 /* try to undo the data fork rename,
1120 * we know we are on the same device
1123 unix_rename(-1, dst, sdir_fd, src );
1124 /* return the first error */
1127 return AFPERR_NOOBJ;
1130 return AFPERR_ACCESS ;
1132 return AFPERR_VLOCK;
1134 return AFPERR_PARAM ;
1139 /* don't care if we can't open the newly renamed ressource fork
1141 if (ad_open(adp, dst, ADFLAGS_HF, O_RDWR) == 0) {
1142 ad_setname(adp, newname);
1144 ad_close( adp, ADFLAGS_HF );
1151 convert a Mac long name to an utf8 name,
1153 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1157 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1163 /* ---------------- */
1164 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1171 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1177 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1178 if (afp_version >= 30) {
1179 /* convert it to UTF8
1181 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1185 strncpy( newname, ibuf, plen );
1186 newname[ plen ] = '\0';
1188 if (strlen(newname) != plen) {
1189 /* there's \0 in newname, e.g. it's a pathname not
1197 memcpy(&hint, ibuf, sizeof(hint));
1198 ibuf += sizeof(hint);
1200 memcpy(&len16, ibuf, sizeof(len16));
1201 ibuf += sizeof(len16);
1202 plen = ntohs(len16);
1205 if (plen > AFPOBJ_TMPSIZ) {
1208 strncpy( newname, ibuf, plen );
1209 newname[ plen ] = '\0';
1210 if (strlen(newname) != plen) {
1219 /* -----------------------------------
1221 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1223 struct vol *s_vol, *d_vol;
1225 char *newname, *p, *upath;
1226 struct path *s_path;
1227 u_int32_t sdid, ddid;
1228 int err, retvalue = AFP_OK;
1229 u_int16_t svid, dvid;
1231 struct adouble ad, *adp;
1237 memcpy(&svid, ibuf, sizeof( svid ));
1238 ibuf += sizeof( svid );
1239 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1240 return( AFPERR_PARAM );
1243 memcpy(&sdid, ibuf, sizeof( sdid ));
1244 ibuf += sizeof( sdid );
1245 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1249 memcpy(&dvid, ibuf, sizeof( dvid ));
1250 ibuf += sizeof( dvid );
1251 memcpy(&ddid, ibuf, sizeof( ddid ));
1252 ibuf += sizeof( ddid );
1254 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1255 return get_afp_errno(AFPERR_PARAM);
1257 if ( path_isadir(s_path) ) {
1258 return( AFPERR_BADTYPE );
1261 /* don't allow copies when the file is open.
1262 * XXX: the spec only calls for read/deny write access.
1263 * however, copyfile doesn't have any of that info,
1264 * and locks need to stay coherent. as a result,
1265 * we just balk if the file is opened already. */
1267 adp = of_ad(s_vol, s_path, &ad);
1269 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0) {
1270 return AFPERR_DENYCONF;
1272 denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1273 getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1276 retvalue = AFPERR_DENYCONF;
1280 newname = obj->newtmp;
1281 strcpy( newname, s_path->m_name );
1283 p = ctoupath( s_vol, curdir, newname );
1285 retvalue = AFPERR_PARAM;
1290 /* FIXME svid != dvid && dvid's user can't read svid */
1292 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1293 retvalue = AFPERR_PARAM;
1297 if (d_vol->v_flags & AFPVOL_RO) {
1298 retvalue = AFPERR_VLOCK;
1302 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1303 retvalue = afp_errno;
1307 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1308 retvalue = get_afp_errno(AFPERR_NOOBJ);
1312 if ( *s_path->m_name != '\0' ) {
1313 retvalue =path_error(s_path, AFPERR_NOOBJ);
1317 /* one of the handful of places that knows about the path type */
1318 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1319 retvalue = AFPERR_PARAM;
1322 /* newname is always only a filename so curdir *is* its
1325 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1326 retvalue =AFPERR_PARAM;
1330 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1337 if (vol->v_flags & AFPVOL_DROPBOX) {
1338 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1340 #endif /* DROPKLUDGE */
1342 setvoltime(obj, d_vol );
1345 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1349 /* ----------------------- */
1350 static int copy_all(const int dfd, const void *buf,
1356 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1359 while (buflen > 0) {
1360 if ((cc = write(dfd, buf, buflen)) < 0) {
1372 LOG(log_debug9, logtype_afpd, "end copy_all:");
1378 /* --------------------------
1379 * copy only the fork data stream
1381 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1388 if (eid == ADEID_DFORK) {
1389 sfd = ad_data_fileno(ads);
1390 dfd = ad_data_fileno(add);
1393 sfd = ad_reso_fileno(ads);
1394 dfd = ad_reso_fileno(add);
1397 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1400 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1403 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1404 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1408 #define BUF 128*1024*1024
1410 if (fstat(sfd, &st) == 0) {
1413 if ( offset >= st.st_size) {
1416 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1417 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1420 case EINVAL: /* there's no guarantee that all fs support sendfile */
1429 lseek(sfd, offset, SEEK_SET);
1433 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1440 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1447 /* ----------------------------------
1448 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1449 * because we are doing it elsewhere.
1450 * currently if newname is NULL then adp is NULL.
1452 int copyfile(const struct vol *s_vol,
1453 const struct vol *d_vol,
1458 struct adouble *adp)
1460 struct adouble ads, add;
1467 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1468 sfd, src, dst, newname);
1471 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1475 adflags = ADFLAGS_DF;
1477 adflags |= ADFLAGS_HF;
1480 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0) {
1485 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1486 /* no resource fork, don't create one for dst file */
1487 adflags &= ~ADFLAGS_HF;
1490 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1492 if (stat_result < 0) {
1493 /* unlikely but if fstat fails, the default file mode will be 0666. */
1494 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1497 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1498 if (ad_open(&add, dst, adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, O_RDWR|O_CREAT|O_EXCL, st.st_mode) < 0) {
1500 ad_close( adp, adflags );
1501 if (EEXIST != ret_err) {
1502 deletefile(d_vol, -1, dst, 0);
1505 return AFPERR_EXIST;
1509 * XXX if the source and the dest don't use the same resource type it's broken
1511 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1512 /* copy the data fork */
1513 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1514 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1522 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1523 /* set the new name in the resource fork */
1524 ad_copy_header(&add, adp);
1525 ad_setname(&add, newname);
1528 ad_close( adp, adflags );
1530 if (ad_close( &add, adflags ) <0) {
1535 deletefile(d_vol, -1, dst, 0);
1537 else if (stat_result == 0) {
1538 /* set dest modification date to src date */
1541 ut.actime = ut.modtime = st.st_mtime;
1543 /* FIXME netatalk doesn't use resource fork file date
1544 * but maybe we should set its modtime too.
1549 switch ( ret_err ) {
1555 return AFPERR_DFULL;
1557 return AFPERR_NOOBJ;
1559 return AFPERR_ACCESS;
1561 return AFPERR_VLOCK;
1563 return AFPERR_PARAM;
1567 /* -----------------------------------
1568 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1569 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1571 when deletefile is called we don't have lock on it, file is closed (for us)
1572 untrue if called by renamefile
1574 ad_open always try to open file RDWR first and ad_lock takes care of
1575 WRITE lock on read only file.
1578 static int check_attrib(struct adouble *adp)
1580 u_int16_t bshort = 0;
1582 ad_getattr(adp, &bshort);
1584 * Does kFPDeleteInhibitBit (bit 8) set?
1586 if ((bshort & htons(ATTRBIT_NODELETE))) {
1587 return AFPERR_OLOCK;
1589 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1595 * dirfd can be used for unlinkat semantics
1597 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1600 struct adouble *adp = NULL;
1601 int adflags, err = AFP_OK;
1604 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1606 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1608 /* was EACCESS error try to get only metadata */
1609 /* we never want to create a resource fork here, we are going to delete it
1610 * moreover sometimes deletefile is called with a no existent file and
1611 * ad_open would create a 0 byte resource fork
1613 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1614 if ((err = check_attrib(&ad))) {
1615 ad_close_metadata(&ad);
1622 /* try to open both forks at once */
1623 adflags = ADFLAGS_DF;
1624 if ( ad_openat(&ad, dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0 ) {
1629 case EACCES: /* maybe it's a file with no write mode for us */
1630 break; /* was return AFPERR_ACCESS;*/
1643 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1644 adflags |= ADFLAGS_HF;
1645 /* FIXME we have a pb here because we want to know if a file is open
1646 * there's a 'priority inversion' if you can't open the ressource fork RW
1647 * you can delete it if it's open because you can't get a write lock.
1649 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1652 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1654 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1660 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1662 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1664 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1665 cnid_delete(vol->v_cdb, id);
1671 ad_close_metadata(&ad);
1674 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1679 /* ------------------------------------ */
1680 /* return a file id */
1681 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1690 struct path *s_path;
1696 memcpy(&vid, ibuf, sizeof(vid));
1697 ibuf += sizeof(vid);
1699 if (NULL == ( vol = getvolbyvid( vid )) ) {
1700 return( AFPERR_PARAM);
1703 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1707 if (vol->v_flags & AFPVOL_RO)
1708 return AFPERR_VLOCK;
1710 memcpy(&did, ibuf, sizeof( did ));
1711 ibuf += sizeof(did);
1713 if (NULL == ( dir = dirlookup( vol, did )) ) {
1714 return afp_errno; /* was AFPERR_PARAM */
1717 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1718 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1721 if ( path_isadir(s_path) ) {
1722 return( AFPERR_BADTYPE );
1725 upath = s_path->u_name;
1726 switch (s_path->st_errno) {
1728 break; /* success */
1731 return AFPERR_ACCESS;
1733 return AFPERR_NOOBJ;
1735 return AFPERR_PARAM;
1738 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1739 memcpy(rbuf, &id, sizeof(id));
1740 *rbuflen = sizeof(id);
1741 return AFPERR_EXISTID;
1744 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1745 memcpy(rbuf, &id, sizeof(id));
1746 *rbuflen = sizeof(id);
1753 /* ------------------------------- */
1759 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1762 struct reenum *param = data;
1763 struct vol *vol = param->vol;
1764 cnid_t did = param->did;
1767 if ( lstat(de->d_name, &path.st) < 0 )
1770 /* update or add to cnid */
1771 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1776 /* --------------------
1777 * Ok the db is out of synch with the dir.
1778 * but if it's a deleted file we don't want to do it again and again.
1781 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1787 if (vol->v_cdb == NULL) {
1791 /* FIXME use of_statdir ? */
1792 if (lstat(name, &st)) {
1796 if (dirreenumerate(dir, &st)) {
1797 /* we already did it once and the dir haven't been modified */
1802 data.did = dir->d_did;
1803 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1804 setdiroffcnt(curdir, &st, ret);
1805 dir->d_flags |= DIRF_CNID;
1811 /* ------------------------------
1812 resolve a file id */
1813 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1822 u_int16_t vid, bitmap;
1824 static char buffer[12 + MAXPATHLEN + 1];
1825 int len = 12 + MAXPATHLEN + 1;
1830 memcpy(&vid, ibuf, sizeof(vid));
1831 ibuf += sizeof(vid);
1833 if (NULL == ( vol = getvolbyvid( vid )) ) {
1834 return( AFPERR_PARAM);
1837 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1841 memcpy(&id, ibuf, sizeof( id ));
1846 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1850 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1851 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1854 if (NULL == ( dir = dirlookup( vol, id )) ) {
1855 return AFPERR_NOID; /* idem AFPERR_PARAM */
1857 if (movecwd(vol, dir) < 0) {
1861 return AFPERR_ACCESS;
1865 return AFPERR_PARAM;
1869 memset(&path, 0, sizeof(path));
1870 path.u_name = upath;
1871 if ( of_stat(&path) < 0 ) {
1873 /* with nfs and our working directory is deleted */
1874 if (errno == ESTALE) {
1878 if ( errno == ENOENT && !retry) {
1879 /* cnid db is out of sync, reenumerate the directory and update ids */
1880 reenumerate_id(vol, ".", dir);
1888 return AFPERR_ACCESS;
1892 return AFPERR_PARAM;
1896 /* directories are bad */
1897 if (S_ISDIR(path.st.st_mode)) {
1898 /* OS9 and OSX don't return the same error code */
1899 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1902 memcpy(&bitmap, ibuf, sizeof(bitmap));
1903 bitmap = ntohs( bitmap );
1904 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1908 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1909 rbuf + sizeof(bitmap), &buflen))) {
1912 *rbuflen = buflen + sizeof(bitmap);
1913 memcpy(rbuf, ibuf, sizeof(bitmap));
1918 /* ------------------------------ */
1919 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1929 static char buffer[12 + MAXPATHLEN + 1];
1930 int len = 12 + MAXPATHLEN + 1;
1935 memcpy(&vid, ibuf, sizeof(vid));
1936 ibuf += sizeof(vid);
1938 if (NULL == ( vol = getvolbyvid( vid )) ) {
1939 return( AFPERR_PARAM);
1942 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1946 if (vol->v_flags & AFPVOL_RO)
1947 return AFPERR_VLOCK;
1949 memcpy(&id, ibuf, sizeof( id ));
1953 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1957 if (NULL == ( dir = dirlookup( vol, id )) ) {
1958 if (afp_errno == AFPERR_NOOBJ) {
1962 return( AFPERR_PARAM );
1966 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1970 return AFPERR_ACCESS;
1975 /* still try to delete the id */
1979 return AFPERR_PARAM;
1982 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1983 return AFPERR_BADTYPE;
1986 if (cnid_delete(vol->v_cdb, fileid)) {
1989 return AFPERR_VLOCK;
1992 return AFPERR_ACCESS;
1994 return AFPERR_PARAM;
2001 /* ------------------------------ */
2002 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
2006 if (path->st_errno) {
2007 switch (path->st_errno) {
2009 afp_errno = AFPERR_NOID;
2013 afp_errno = AFPERR_ACCESS;
2016 afp_errno = AFPERR_PARAM;
2021 /* we use file_access both for legacy Mac perm and
2022 * for unix privilege, rename will take care of folder perms
2024 if (file_access(path, OPENACC_WR ) < 0) {
2025 afp_errno = AFPERR_ACCESS;
2029 if ((*of = of_findname(path))) {
2030 /* reuse struct adouble so it won't break locks */
2034 ret = ad_open(adp, path->u_name, ADFLAGS_HF, O_RDONLY);
2036 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2038 * The user must have the Read & Write privilege for both files in order to use this command.
2040 ad_close(adp, ADFLAGS_HF);
2041 afp_errno = AFPERR_ACCESS;
2048 #define APPLETEMP ".AppleTempXXXXXX"
2050 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2052 struct stat srcst, destst;
2054 struct dir *dir, *sdir;
2055 char *spath, temp[17], *p;
2056 char *supath, *upath;
2061 struct adouble *adsp = NULL;
2062 struct adouble *addp = NULL;
2063 struct ofork *s_of = NULL;
2064 struct ofork *d_of = NULL;
2077 memcpy(&vid, ibuf, sizeof(vid));
2078 ibuf += sizeof(vid);
2080 if (NULL == ( vol = getvolbyvid( vid )) ) {
2081 return( AFPERR_PARAM);
2084 if ((vol->v_flags & AFPVOL_RO))
2085 return AFPERR_VLOCK;
2087 /* source and destination dids */
2088 memcpy(&sid, ibuf, sizeof(sid));
2089 ibuf += sizeof(sid);
2090 memcpy(&did, ibuf, sizeof(did));
2091 ibuf += sizeof(did);
2094 if (NULL == (dir = dirlookup( vol, sid )) ) {
2095 return afp_errno; /* was AFPERR_PARAM */
2098 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2099 return get_afp_errno(AFPERR_NOOBJ);
2102 if ( path_isadir(path) ) {
2103 return AFPERR_BADTYPE; /* it's a dir */
2106 /* save some stuff */
2109 spath = obj->oldtmp;
2110 supath = obj->newtmp;
2111 strcpy(spath, path->m_name);
2112 strcpy(supath, path->u_name); /* this is for the cnid changing */
2113 p = absupath( vol, sdir, supath);
2115 /* pathname too long */
2116 return AFPERR_PARAM ;
2119 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2120 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2124 /* ***** from here we may have resource fork open **** */
2126 /* look for the source cnid. if it doesn't exist, don't worry about
2128 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2130 if (NULL == ( dir = dirlookup( vol, did )) ) {
2131 err = afp_errno; /* was AFPERR_PARAM */
2132 goto err_exchangefile;
2135 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2136 err = get_afp_errno(AFPERR_NOOBJ);
2137 goto err_exchangefile;
2140 if ( path_isadir(path) ) {
2141 err = AFPERR_BADTYPE;
2142 goto err_exchangefile;
2145 /* FPExchangeFiles is the only call that can return the SameObj
2147 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2148 err = AFPERR_SAMEOBJ;
2149 goto err_exchangefile;
2152 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2153 if (!(addp = find_adouble( path, &d_of, &add))) {
2155 goto err_exchangefile;
2159 /* they are not on the same device and at least one is open
2160 * FIXME broken for for crossdev and adouble v2
2163 crossdev = (srcst.st_dev != destst.st_dev);
2164 if (/* (d_of || s_of) && */ crossdev) {
2166 goto err_exchangefile;
2169 /* look for destination id. */
2170 upath = path->u_name;
2171 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2173 /* construct a temp name.
2174 * NOTE: the temp file will be in the dest file's directory. it
2175 * will also be inaccessible from AFP. */
2176 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2177 if (!mktemp(temp)) {
2179 goto err_exchangefile;
2183 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2184 ad_close(adsp, ADFLAGS_HF);
2185 ad_close(addp, ADFLAGS_HF);
2188 /* now, quickly rename the file. we error if we can't. */
2189 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2190 goto err_exchangefile;
2191 of_rename(vol, s_of, sdir, spath, curdir, temp);
2193 /* rename destination to source */
2194 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2195 goto err_src_to_tmp;
2196 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2198 /* rename temp to destination */
2199 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2200 goto err_dest_to_src;
2201 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2203 /* id's need switching. src -> dest and dest -> src.
2204 * we need to re-stat() if it was a cross device copy.
2207 cnid_delete(vol->v_cdb, sid);
2209 cnid_delete(vol->v_cdb, did);
2211 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2212 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2214 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2215 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2220 err = AFPERR_ACCESS;
2225 goto err_temp_to_dest;
2228 /* here we need to reopen if crossdev */
2229 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2234 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2239 /* change perms, src gets dest perm and vice versa */
2244 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2245 err = AFP_OK; /* ignore error */
2246 goto err_temp_to_dest;
2250 * we need to exchange ACL entries as well
2252 /* exchange_acls(vol, p, upath); */
2257 path->m_name = NULL;
2258 path->u_name = upath;
2260 setfilunixmode(vol, path, destst.st_mode);
2261 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2268 setfilunixmode(vol, path, srcst.st_mode);
2269 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2271 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2272 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2277 goto err_exchangefile;
2279 /* all this stuff is so that we can unwind a failed operation
2282 /* rename dest to temp */
2283 renamefile(vol, -1, upath, temp, temp, adsp);
2284 of_rename(vol, s_of, curdir, upath, curdir, temp);
2287 /* rename source back to dest */
2288 renamefile(vol, -1, p, upath, path->m_name, addp);
2289 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2292 /* rename temp back to source */
2293 renamefile(vol, -1, temp, p, spath, adsp);
2294 of_rename(vol, s_of, curdir, temp, sdir, spath);
2297 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2298 ad_close(adsp, ADFLAGS_HF);
2300 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2301 ad_close(addp, ADFLAGS_HF);
2305 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2306 (void)dir_remove(vol, cached);
2307 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2308 (void)dir_remove(vol, cached);