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)) != 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)) == NULL) {
347 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
350 if ((dircache_add(cachedfile)) != 0) {
351 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
359 if (id == CNID_INVALID)
363 path->m_name = utompath(vol, upath, id, utf8_encoding());
366 while ( bitmap != 0 ) {
367 while (( bitmap & 1 ) == 0 ) {
375 ad_getattr(adp, &ashort);
376 } else if (vol_inv_dots(vol) && *upath == '.') {
377 ashort = htons(ATTRBIT_INVISIBLE);
381 /* FIXME do we want a visual clue if the file is read only
384 accessmode( ".", &ma, dir , NULL);
385 if ((ma.ma_user & AR_UWRITE)) {
386 accessmode( upath, &ma, dir , st);
387 if (!(ma.ma_user & AR_UWRITE)) {
388 ashort |= htons(ATTRBIT_NOWRITE);
392 memcpy(data, &ashort, sizeof( ashort ));
393 data += sizeof( ashort );
394 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
395 path->u_name, ntohs(ashort));
399 memcpy(data, &dir->d_did, sizeof( u_int32_t ));
400 data += sizeof( u_int32_t );
401 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
402 path->u_name, ntohl(dir->d_did));
406 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
407 aint = AD_DATE_FROM_UNIX(st->st_mtime);
408 memcpy(data, &aint, sizeof( aint ));
409 data += sizeof( aint );
413 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
414 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
415 aint = AD_DATE_FROM_UNIX(st->st_mtime);
418 aint = AD_DATE_FROM_UNIX(st->st_mtime);
420 memcpy(data, &aint, sizeof( int ));
421 data += sizeof( int );
425 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
426 aint = AD_DATE_START;
427 memcpy(data, &aint, sizeof( int ));
428 data += sizeof( int );
432 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
433 data += ADEDLEN_FINDERI;
438 data += sizeof( u_int16_t );
442 memset(data, 0, sizeof(u_int16_t));
443 data += sizeof( u_int16_t );
447 memcpy(data, &id, sizeof( id ));
448 data += sizeof( id );
449 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
450 path->u_name, ntohl(id));
454 if (st->st_size > 0xffffffff)
457 aint = htonl( st->st_size );
458 memcpy(data, &aint, sizeof( aint ));
459 data += sizeof( aint );
464 if (adp->ad_rlen > 0xffffffff)
467 aint = htonl( adp->ad_rlen);
471 memcpy(data, &aint, sizeof( aint ));
472 data += sizeof( aint );
475 /* Current client needs ProDOS info block for this file.
476 Use simple heuristic and let the Mac "type" string tell
477 us what the PD file code should be. Everything gets a
478 subtype of 0x0000 unless the original value was hashed
479 to "pXYZ" when we created it. See IA, Ver 2.
480 <shirsch@adelphia.net> */
481 case FILPBIT_PDINFO :
482 if (afp_version >= 30) { /* UTF8 name */
483 utf8 = kTextEncodingUTF8;
485 data += sizeof( u_int16_t );
487 memcpy(data, &aint, sizeof( aint ));
488 data += sizeof( aint );
492 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
494 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
498 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
502 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
506 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
510 else if ( fdType[0] == 'p' ) {
512 ashort = (fdType[2] * 256) + fdType[3];
526 memcpy(data, &ashort, sizeof( ashort ));
527 data += sizeof( ashort );
528 memset(data, 0, sizeof( ashort ));
529 data += sizeof( ashort );
532 case FILPBIT_EXTDFLEN:
533 aint = htonl(st->st_size >> 32);
534 memcpy(data, &aint, sizeof( aint ));
535 data += sizeof( aint );
536 aint = htonl(st->st_size);
537 memcpy(data, &aint, sizeof( aint ));
538 data += sizeof( aint );
540 case FILPBIT_EXTRFLEN:
543 aint = htonl(adp->ad_rlen >> 32);
544 memcpy(data, &aint, sizeof( aint ));
545 data += sizeof( aint );
547 aint = htonl(adp->ad_rlen);
548 memcpy(data, &aint, sizeof( aint ));
549 data += sizeof( aint );
551 case FILPBIT_UNIXPR :
552 /* accessmode may change st_mode with ACLs */
553 accessmode( upath, &ma, dir , st);
555 aint = htonl(st->st_uid);
556 memcpy( data, &aint, sizeof( aint ));
557 data += sizeof( aint );
558 aint = htonl(st->st_gid);
559 memcpy( data, &aint, sizeof( aint ));
560 data += sizeof( aint );
563 type == slnk indicates an OSX style symlink,
564 we have to add S_IFLNK to the mode, otherwise
565 10.3 clients freak out. */
569 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
570 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
576 memcpy( data, &aint, sizeof( aint ));
577 data += sizeof( aint );
579 *data++ = ma.ma_user;
580 *data++ = ma.ma_world;
581 *data++ = ma.ma_group;
582 *data++ = ma.ma_owner;
586 return( AFPERR_BITMAP );
592 ashort = htons( data - buf );
593 memcpy(l_nameoff, &ashort, sizeof( ashort ));
594 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
597 ashort = htons( data - buf );
598 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
599 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
601 *buflen = data - buf;
605 /* ----------------------- */
606 int getfilparams(struct vol *vol,
608 struct path *path, struct dir *dir,
609 char *buf, size_t *buflen )
611 struct adouble ad, *adp;
615 opened = PARAM_NEED_ADP(bitmap);
620 int flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0;
622 adp = of_ad(vol, path, &ad);
623 upath = path->u_name;
625 if ( ad_metadata( upath, flags|ADFLAGS_CREATE, adp) < 0 ) {
628 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
629 upath, strerror(errno));
630 return AFPERR_ACCESS;
632 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
641 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
643 ad_close_metadata( adp);
649 /* ----------------------------- */
650 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
652 struct adouble ad, *adp;
655 struct ofork *of = NULL;
657 int creatf, did, openf, retvalue = AFP_OK;
663 creatf = (unsigned char) *ibuf++;
665 memcpy(&vid, ibuf, sizeof( vid ));
666 ibuf += sizeof( vid );
668 if (NULL == ( vol = getvolbyvid( vid )) ) {
669 return( AFPERR_PARAM );
672 if (vol->v_flags & AFPVOL_RO)
675 memcpy(&did, ibuf, sizeof( did));
676 ibuf += sizeof( did );
678 if (NULL == ( dir = dirlookup( vol, did )) ) {
682 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
683 return get_afp_errno(AFPERR_PARAM);
686 if ( *s_path->m_name == '\0' ) {
687 return( AFPERR_BADTYPE );
690 upath = s_path->u_name;
692 /* if upath is deleted we already in trouble anyway */
693 if ((of = of_findname(s_path))) {
696 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
700 /* on a hard create, fail if file exists and is open */
703 openf = O_RDWR|O_CREAT|O_TRUNC;
705 /* on a soft create, if the file is open then ad_open won't fail
706 because open syscall is not called
711 openf = O_RDWR|O_CREAT|O_EXCL;
714 if ( ad_open( upath, ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF|ADFLAGS_CREATE,
715 openf, 0666, adp) < 0 ) {
719 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
720 return ( AFPERR_NOOBJ );
722 return( AFPERR_EXIST );
724 return( AFPERR_ACCESS );
727 return( AFPERR_DFULL );
729 return( AFPERR_PARAM );
732 if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */
733 /* on noadouble volumes, just creating the data fork is ok */
734 if (vol_noadouble(vol)) {
735 ad_close( adp, ADFLAGS_DF );
736 goto createfile_done;
738 /* FIXME with hard create on an existing file, we already
739 * corrupted the data file.
741 netatalk_unlink( upath );
742 ad_close( adp, ADFLAGS_DF );
743 return AFPERR_ACCESS;
746 path = s_path->m_name;
747 ad_setname(adp, path);
749 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
755 if (vol->v_flags & AFPVOL_DROPBOX) {
756 retvalue = matchfile2dirperms(upath, vol, did);
758 #endif /* DROPKLUDGE */
760 setvoltime(obj, vol );
765 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
771 u_int16_t vid, bitmap;
776 memcpy(&vid, ibuf, sizeof( vid ));
777 ibuf += sizeof( vid );
778 if (NULL == ( vol = getvolbyvid( vid )) ) {
779 return( AFPERR_PARAM );
782 if (vol->v_flags & AFPVOL_RO)
785 memcpy(&did, ibuf, sizeof( did ));
786 ibuf += sizeof( did );
787 if (NULL == ( dir = dirlookup( vol, did )) ) {
788 return afp_errno; /* was AFPERR_NOOBJ */
791 memcpy(&bitmap, ibuf, sizeof( bitmap ));
792 bitmap = ntohs( bitmap );
793 ibuf += sizeof( bitmap );
795 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
796 return get_afp_errno(AFPERR_PARAM);
799 if (path_isadir(s_path)) {
800 return( AFPERR_BADTYPE ); /* it's a directory */
803 if ( s_path->st_errno != 0 ) {
804 return( AFPERR_NOOBJ );
807 if ((u_long)ibuf & 1 ) {
811 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
812 setvoltime(obj, vol );
819 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
822 extern struct path Cur_Path;
824 int setfilparams(struct vol *vol,
825 struct path *path, u_int16_t f_bitmap, char *buf )
827 struct adouble ad, *adp;
829 int bit, isad = 1, err = AFP_OK;
831 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
832 u_int16_t ashort, bshort, oshort;
835 u_int16_t upriv_bit = 0;
839 int change_mdate = 0;
840 int change_parent_mdate = 0;
845 u_int16_t bitmap = f_bitmap;
846 u_int32_t cdate,bdate;
847 u_char finder_buf[32];
850 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
853 adp = of_ad(vol, path, &ad);
854 upath = path->u_name;
856 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
857 return AFPERR_ACCESS;
860 /* with unix priv maybe we have to change adouble file priv first */
862 while ( bitmap != 0 ) {
863 while (( bitmap & 1 ) == 0 ) {
870 memcpy(&ashort, buf, sizeof( ashort ));
871 buf += sizeof( ashort );
875 memcpy(&cdate, buf, sizeof(cdate));
876 buf += sizeof( cdate );
879 memcpy(&newdate, buf, sizeof( newdate ));
880 buf += sizeof( newdate );
884 memcpy(&bdate, buf, sizeof( bdate));
885 buf += sizeof( bdate );
889 memcpy(finder_buf, buf, 32 );
890 if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
895 char buf[PATH_MAX+1];
896 if ((fp=open(path->u_name,O_RDONLY))>=0){
897 if ((len=read(fp,buf,PATH_MAX+1))){
898 if (unlink(path->u_name)==0){
900 erc = symlink(buf, path->u_name);
909 goto setfilparam_done;
914 case FILPBIT_UNIXPR :
915 if (!vol_unix_priv(vol)) {
916 /* this volume doesn't use unix priv */
922 change_parent_mdate = 1;
924 memcpy( &aint, buf, sizeof( aint ));
925 f_uid = ntohl (aint);
926 buf += sizeof( aint );
927 memcpy( &aint, buf, sizeof( aint ));
928 f_gid = ntohl (aint);
929 buf += sizeof( aint );
930 setfilowner(vol, f_uid, f_gid, path);
932 memcpy( &upriv, buf, sizeof( upriv ));
933 buf += sizeof( upriv );
934 upriv = ntohl (upriv);
935 if ((upriv & S_IWUSR)) {
936 setfilunixmode(vol, path, upriv);
943 case FILPBIT_PDINFO :
944 if (afp_version < 30) { /* else it's UTF8 name */
947 /* Keep special case to support crlf translations */
948 if ((unsigned int) achar == 0x04) {
949 fdType = (u_char *)"TEXT";
952 xyy[0] = ( u_char ) 'p';
963 /* break while loop */
972 /* second try with adouble open
974 if ( ad_open_metadata( upath, 0, O_CREAT, adp) < 0) {
975 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
977 * For some things, we don't need an adouble header:
978 * - change of modification date
979 * - UNIX privs (Bug-ID #2863424)
981 if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
982 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
983 return AFPERR_ACCESS;
985 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
987 } else if ((ad_get_HF_flags( adp ) & O_CREAT) ) {
988 ad_setname(adp, path->m_name);
993 while ( bitmap != 0 ) {
994 while (( bitmap & 1 ) == 0 ) {
1001 ad_getattr(adp, &bshort);
1003 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1004 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1008 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1009 change_parent_mdate = 1;
1010 ad_setattr(adp, bshort);
1012 case FILPBIT_CDATE :
1013 ad_setdate(adp, AD_DATE_CREATE, cdate);
1015 case FILPBIT_MDATE :
1017 case FILPBIT_BDATE :
1018 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1020 case FILPBIT_FINFO :
1021 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1023 ((em = getextmap( path->m_name )) &&
1024 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1025 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1026 || ((em = getdefextmap()) &&
1027 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1028 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1030 memcpy(finder_buf, ufinderi, 8 );
1032 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1034 case FILPBIT_UNIXPR :
1036 setfilunixmode(vol, path, upriv);
1039 case FILPBIT_PDINFO :
1040 if (afp_version < 30) { /* else it's UTF8 name */
1041 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1042 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1047 err = AFPERR_BITMAP;
1048 goto setfilparam_done;
1055 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1056 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1060 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1061 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1067 ad_close_metadata( adp);
1071 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1072 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1073 bitmap = 1<<FILPBIT_MDATE;
1074 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1078 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1084 * renamefile and copyfile take the old and new unix pathnames
1085 * and the new mac name.
1087 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1088 * passing -1 means this is not used, src path is a full path
1089 * src the source path
1090 * dst the dest filename in current dir
1091 * newname the dest mac name
1092 * adp adouble struct of src file, if open, or & zeroed one
1095 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1099 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1102 return( AFPERR_NOOBJ );
1105 return( AFPERR_ACCESS );
1107 return AFPERR_VLOCK;
1108 case EXDEV : /* Cross device move -- try copy */
1109 /* NOTE: with open file it's an error because after the copy we will
1110 * get two files, it's fixable for our process (eg reopen the new file, get the
1111 * locks, and so on. But it doesn't solve the case with a second process
1113 if (adp->ad_open_forks) {
1114 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1115 return AFPERR_OLOCK; /* little lie */
1117 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1118 /* on error copyfile delete dest */
1121 return deletefile(vol, sdir_fd, src, 0);
1123 return( AFPERR_PARAM );
1127 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1131 /* try to undo the data fork rename,
1132 * we know we are on the same device
1135 unix_rename(-1, dst, sdir_fd, src );
1136 /* return the first error */
1139 return AFPERR_NOOBJ;
1142 return AFPERR_ACCESS ;
1144 return AFPERR_VLOCK;
1146 return AFPERR_PARAM ;
1151 /* don't care if we can't open the newly renamed ressource fork
1153 if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
1154 ad_setname(adp, newname);
1156 ad_close( adp, ADFLAGS_HF );
1163 convert a Mac long name to an utf8 name,
1165 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1169 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1175 /* ---------------- */
1176 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1183 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1189 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1190 if (afp_version >= 30) {
1191 /* convert it to UTF8
1193 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1197 strncpy( newname, ibuf, plen );
1198 newname[ plen ] = '\0';
1200 if (strlen(newname) != plen) {
1201 /* there's \0 in newname, e.g. it's a pathname not
1209 memcpy(&hint, ibuf, sizeof(hint));
1210 ibuf += sizeof(hint);
1212 memcpy(&len16, ibuf, sizeof(len16));
1213 ibuf += sizeof(len16);
1214 plen = ntohs(len16);
1217 if (plen > AFPOBJ_TMPSIZ) {
1220 strncpy( newname, ibuf, plen );
1221 newname[ plen ] = '\0';
1222 if (strlen(newname) != plen) {
1231 /* -----------------------------------
1233 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1235 struct vol *s_vol, *d_vol;
1237 char *newname, *p, *upath;
1238 struct path *s_path;
1239 u_int32_t sdid, ddid;
1240 int err, retvalue = AFP_OK;
1241 u_int16_t svid, dvid;
1243 struct adouble ad, *adp;
1249 memcpy(&svid, ibuf, sizeof( svid ));
1250 ibuf += sizeof( svid );
1251 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1252 return( AFPERR_PARAM );
1255 memcpy(&sdid, ibuf, sizeof( sdid ));
1256 ibuf += sizeof( sdid );
1257 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1261 memcpy(&dvid, ibuf, sizeof( dvid ));
1262 ibuf += sizeof( dvid );
1263 memcpy(&ddid, ibuf, sizeof( ddid ));
1264 ibuf += sizeof( ddid );
1266 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1267 return get_afp_errno(AFPERR_PARAM);
1269 if ( path_isadir(s_path) ) {
1270 return( AFPERR_BADTYPE );
1273 /* don't allow copies when the file is open.
1274 * XXX: the spec only calls for read/deny write access.
1275 * however, copyfile doesn't have any of that info,
1276 * and locks need to stay coherent. as a result,
1277 * we just balk if the file is opened already. */
1279 adp = of_ad(s_vol, s_path, &ad);
1281 if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1282 return AFPERR_DENYCONF;
1284 denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1285 getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1288 retvalue = AFPERR_DENYCONF;
1292 newname = obj->newtmp;
1293 strcpy( newname, s_path->m_name );
1295 p = ctoupath( s_vol, curdir, newname );
1297 retvalue = AFPERR_PARAM;
1302 /* FIXME svid != dvid && dvid's user can't read svid */
1304 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1305 retvalue = AFPERR_PARAM;
1309 if (d_vol->v_flags & AFPVOL_RO) {
1310 retvalue = AFPERR_VLOCK;
1314 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1315 retvalue = afp_errno;
1319 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1320 retvalue = get_afp_errno(AFPERR_NOOBJ);
1324 if ( *s_path->m_name != '\0' ) {
1325 retvalue =path_error(s_path, AFPERR_NOOBJ);
1329 /* one of the handful of places that knows about the path type */
1330 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1331 retvalue = AFPERR_PARAM;
1334 /* newname is always only a filename so curdir *is* its
1337 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1338 retvalue =AFPERR_PARAM;
1342 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1349 if (vol->v_flags & AFPVOL_DROPBOX) {
1350 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1352 #endif /* DROPKLUDGE */
1354 setvoltime(obj, d_vol );
1357 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1361 /* ----------------------- */
1362 static int copy_all(const int dfd, const void *buf,
1368 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1371 while (buflen > 0) {
1372 if ((cc = write(dfd, buf, buflen)) < 0) {
1384 LOG(log_debug9, logtype_afpd, "end copy_all:");
1390 /* --------------------------
1391 * copy only the fork data stream
1393 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1400 if (eid == ADEID_DFORK) {
1401 sfd = ad_data_fileno(ads);
1402 dfd = ad_data_fileno(add);
1405 sfd = ad_reso_fileno(ads);
1406 dfd = ad_reso_fileno(add);
1409 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1412 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1415 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1416 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1420 #define BUF 128*1024*1024
1422 if (fstat(sfd, &st) == 0) {
1425 if ( offset >= st.st_size) {
1428 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1429 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1432 case EINVAL: /* there's no guarantee that all fs support sendfile */
1441 lseek(sfd, offset, SEEK_SET);
1445 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1452 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1459 /* ----------------------------------
1460 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1461 * because we are doing it elsewhere.
1462 * currently if newname is NULL then adp is NULL.
1464 int copyfile(const struct vol *s_vol,
1465 const struct vol *d_vol,
1470 struct adouble *adp)
1472 struct adouble ads, add;
1479 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1480 sfd, src, dst, newname);
1483 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1487 adflags = ADFLAGS_DF;
1489 adflags |= ADFLAGS_HF;
1492 if (ad_openat(sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1497 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1498 /* no resource fork, don't create one for dst file */
1499 adflags &= ~ADFLAGS_HF;
1502 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1504 if (stat_result < 0) {
1505 /* unlikely but if fstat fails, the default file mode will be 0666. */
1506 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1509 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1510 if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1512 ad_close( adp, adflags );
1513 if (EEXIST != ret_err) {
1514 deletefile(d_vol, -1, dst, 0);
1517 return AFPERR_EXIST;
1521 * XXX if the source and the dest don't use the same resource type it's broken
1523 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1524 /* copy the data fork */
1525 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1526 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1534 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1535 /* set the new name in the resource fork */
1536 ad_copy_header(&add, adp);
1537 ad_setname(&add, newname);
1540 ad_close( adp, adflags );
1542 if (ad_close( &add, adflags ) <0) {
1547 deletefile(d_vol, -1, dst, 0);
1549 else if (stat_result == 0) {
1550 /* set dest modification date to src date */
1553 ut.actime = ut.modtime = st.st_mtime;
1555 /* FIXME netatalk doesn't use resource fork file date
1556 * but maybe we should set its modtime too.
1561 switch ( ret_err ) {
1567 return AFPERR_DFULL;
1569 return AFPERR_NOOBJ;
1571 return AFPERR_ACCESS;
1573 return AFPERR_VLOCK;
1575 return AFPERR_PARAM;
1579 /* -----------------------------------
1580 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1581 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1583 when deletefile is called we don't have lock on it, file is closed (for us)
1584 untrue if called by renamefile
1586 ad_open always try to open file RDWR first and ad_lock takes care of
1587 WRITE lock on read only file.
1590 static int check_attrib(struct adouble *adp)
1592 u_int16_t bshort = 0;
1594 ad_getattr(adp, &bshort);
1596 * Does kFPDeleteInhibitBit (bit 8) set?
1598 if ((bshort & htons(ATTRBIT_NODELETE))) {
1599 return AFPERR_OLOCK;
1601 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1607 * dirfd can be used for unlinkat semantics
1609 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1612 struct adouble *adp = NULL;
1613 int adflags, err = AFP_OK;
1616 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1618 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1620 /* was EACCESS error try to get only metadata */
1621 /* we never want to create a resource fork here, we are going to delete it
1622 * moreover sometimes deletefile is called with a no existent file and
1623 * ad_open would create a 0 byte resource fork
1625 if ( ad_metadataat(dirfd, file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
1626 if ((err = check_attrib(&ad))) {
1627 ad_close_metadata(&ad);
1634 /* try to open both forks at once */
1635 adflags = ADFLAGS_DF;
1636 if ( ad_openat(dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, 0, &ad ) < 0 ) {
1641 case EACCES: /* maybe it's a file with no write mode for us */
1642 break; /* was return AFPERR_ACCESS;*/
1655 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1656 adflags |= ADFLAGS_HF;
1657 /* FIXME we have a pb here because we want to know if a file is open
1658 * there's a 'priority inversion' if you can't open the ressource fork RW
1659 * you can delete it if it's open because you can't get a write lock.
1661 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1664 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1666 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1672 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1674 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1676 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1677 cnid_delete(vol->v_cdb, id);
1683 ad_close_metadata(&ad);
1686 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1691 /* ------------------------------------ */
1692 /* return a file id */
1693 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1702 struct path *s_path;
1708 memcpy(&vid, ibuf, sizeof(vid));
1709 ibuf += sizeof(vid);
1711 if (NULL == ( vol = getvolbyvid( vid )) ) {
1712 return( AFPERR_PARAM);
1715 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1719 if (vol->v_flags & AFPVOL_RO)
1720 return AFPERR_VLOCK;
1722 memcpy(&did, ibuf, sizeof( did ));
1723 ibuf += sizeof(did);
1725 if (NULL == ( dir = dirlookup( vol, did )) ) {
1726 return afp_errno; /* was AFPERR_PARAM */
1729 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1730 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1733 if ( path_isadir(s_path) ) {
1734 return( AFPERR_BADTYPE );
1737 upath = s_path->u_name;
1738 switch (s_path->st_errno) {
1740 break; /* success */
1743 return AFPERR_ACCESS;
1745 return AFPERR_NOOBJ;
1747 return AFPERR_PARAM;
1750 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1751 memcpy(rbuf, &id, sizeof(id));
1752 *rbuflen = sizeof(id);
1753 return AFPERR_EXISTID;
1756 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1757 memcpy(rbuf, &id, sizeof(id));
1758 *rbuflen = sizeof(id);
1765 /* ------------------------------- */
1771 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1774 struct reenum *param = data;
1775 struct vol *vol = param->vol;
1776 cnid_t did = param->did;
1779 if ( lstat(de->d_name, &path.st)<0 )
1782 /* update or add to cnid */
1783 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1785 #if AD_VERSION > AD_VERSION1
1786 if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1787 struct adouble ad, *adp;
1791 path.u_name = de->d_name;
1793 adp = of_ad(vol, &path, &ad);
1795 if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1798 if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1801 ad_close_metadata(adp);
1803 #endif /* AD_VERSION > AD_VERSION1 */
1808 /* --------------------
1809 * Ok the db is out of synch with the dir.
1810 * but if it's a deleted file we don't want to do it again and again.
1813 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1819 if (vol->v_cdb == NULL) {
1823 /* FIXME use of_statdir ? */
1824 if (lstat(name, &st)) {
1828 if (dirreenumerate(dir, &st)) {
1829 /* we already did it once and the dir haven't been modified */
1834 data.did = dir->d_did;
1835 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1836 setdiroffcnt(curdir, &st, ret);
1837 dir->d_flags |= DIRF_CNID;
1843 /* ------------------------------
1844 resolve a file id */
1845 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1854 u_int16_t vid, bitmap;
1856 static char buffer[12 + MAXPATHLEN + 1];
1857 int len = 12 + MAXPATHLEN + 1;
1862 memcpy(&vid, ibuf, sizeof(vid));
1863 ibuf += sizeof(vid);
1865 if (NULL == ( vol = getvolbyvid( vid )) ) {
1866 return( AFPERR_PARAM);
1869 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1873 memcpy(&id, ibuf, sizeof( id ));
1878 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1882 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1883 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1886 if (NULL == ( dir = dirlookup( vol, id )) ) {
1887 return AFPERR_NOID; /* idem AFPERR_PARAM */
1889 if (movecwd(vol, dir) < 0) {
1893 return AFPERR_ACCESS;
1897 return AFPERR_PARAM;
1901 memset(&path, 0, sizeof(path));
1902 path.u_name = upath;
1903 if ( of_stat(&path) < 0 ) {
1905 /* with nfs and our working directory is deleted */
1906 if (errno == ESTALE) {
1910 if ( errno == ENOENT && !retry) {
1911 /* cnid db is out of sync, reenumerate the directory and update ids */
1912 reenumerate_id(vol, ".", dir);
1920 return AFPERR_ACCESS;
1924 return AFPERR_PARAM;
1928 /* directories are bad */
1929 if (S_ISDIR(path.st.st_mode)) {
1930 /* OS9 and OSX don't return the same error code */
1931 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1934 memcpy(&bitmap, ibuf, sizeof(bitmap));
1935 bitmap = ntohs( bitmap );
1936 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1940 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1941 rbuf + sizeof(bitmap), &buflen))) {
1944 *rbuflen = buflen + sizeof(bitmap);
1945 memcpy(rbuf, ibuf, sizeof(bitmap));
1950 /* ------------------------------ */
1951 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1961 static char buffer[12 + MAXPATHLEN + 1];
1962 int len = 12 + MAXPATHLEN + 1;
1967 memcpy(&vid, ibuf, sizeof(vid));
1968 ibuf += sizeof(vid);
1970 if (NULL == ( vol = getvolbyvid( vid )) ) {
1971 return( AFPERR_PARAM);
1974 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1978 if (vol->v_flags & AFPVOL_RO)
1979 return AFPERR_VLOCK;
1981 memcpy(&id, ibuf, sizeof( id ));
1985 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1989 if (NULL == ( dir = dirlookup( vol, id )) ) {
1990 if (afp_errno == AFPERR_NOOBJ) {
1994 return( AFPERR_PARAM );
1998 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
2002 return AFPERR_ACCESS;
2007 /* still try to delete the id */
2011 return AFPERR_PARAM;
2014 else if (S_ISDIR(st.st_mode)) /* directories are bad */
2015 return AFPERR_BADTYPE;
2018 if (cnid_delete(vol->v_cdb, fileid)) {
2021 return AFPERR_VLOCK;
2024 return AFPERR_ACCESS;
2026 return AFPERR_PARAM;
2033 /* ------------------------------ */
2034 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
2038 if (path->st_errno) {
2039 switch (path->st_errno) {
2041 afp_errno = AFPERR_NOID;
2045 afp_errno = AFPERR_ACCESS;
2048 afp_errno = AFPERR_PARAM;
2053 /* we use file_access both for legacy Mac perm and
2054 * for unix privilege, rename will take care of folder perms
2056 if (file_access(path, OPENACC_WR ) < 0) {
2057 afp_errno = AFPERR_ACCESS;
2061 if ((*of = of_findname(path))) {
2062 /* reuse struct adouble so it won't break locks */
2066 ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
2068 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2070 * The user must have the Read & Write privilege for both files in order to use this command.
2072 ad_close(adp, ADFLAGS_HF);
2073 afp_errno = AFPERR_ACCESS;
2080 #define APPLETEMP ".AppleTempXXXXXX"
2082 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2084 struct stat srcst, destst;
2086 struct dir *dir, *sdir;
2087 char *spath, temp[17], *p;
2088 char *supath, *upath;
2093 struct adouble *adsp = NULL;
2094 struct adouble *addp = NULL;
2095 struct ofork *s_of = NULL;
2096 struct ofork *d_of = NULL;
2109 memcpy(&vid, ibuf, sizeof(vid));
2110 ibuf += sizeof(vid);
2112 if (NULL == ( vol = getvolbyvid( vid )) ) {
2113 return( AFPERR_PARAM);
2116 if ((vol->v_flags & AFPVOL_RO))
2117 return AFPERR_VLOCK;
2119 /* source and destination dids */
2120 memcpy(&sid, ibuf, sizeof(sid));
2121 ibuf += sizeof(sid);
2122 memcpy(&did, ibuf, sizeof(did));
2123 ibuf += sizeof(did);
2126 if (NULL == (dir = dirlookup( vol, sid )) ) {
2127 return afp_errno; /* was AFPERR_PARAM */
2130 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2131 return get_afp_errno(AFPERR_NOOBJ);
2134 if ( path_isadir(path) ) {
2135 return AFPERR_BADTYPE; /* it's a dir */
2138 /* save some stuff */
2141 spath = obj->oldtmp;
2142 supath = obj->newtmp;
2143 strcpy(spath, path->m_name);
2144 strcpy(supath, path->u_name); /* this is for the cnid changing */
2145 p = absupath( vol, sdir, supath);
2147 /* pathname too long */
2148 return AFPERR_PARAM ;
2151 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2152 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2156 /* ***** from here we may have resource fork open **** */
2158 /* look for the source cnid. if it doesn't exist, don't worry about
2160 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2162 if (NULL == ( dir = dirlookup( vol, did )) ) {
2163 err = afp_errno; /* was AFPERR_PARAM */
2164 goto err_exchangefile;
2167 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2168 err = get_afp_errno(AFPERR_NOOBJ);
2169 goto err_exchangefile;
2172 if ( path_isadir(path) ) {
2173 err = AFPERR_BADTYPE;
2174 goto err_exchangefile;
2177 /* FPExchangeFiles is the only call that can return the SameObj
2179 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2180 err = AFPERR_SAMEOBJ;
2181 goto err_exchangefile;
2184 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2185 if (!(addp = find_adouble( path, &d_of, &add))) {
2187 goto err_exchangefile;
2191 /* they are not on the same device and at least one is open
2192 * FIXME broken for for crossdev and adouble v2
2195 crossdev = (srcst.st_dev != destst.st_dev);
2196 if (/* (d_of || s_of) && */ crossdev) {
2198 goto err_exchangefile;
2201 /* look for destination id. */
2202 upath = path->u_name;
2203 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2205 /* construct a temp name.
2206 * NOTE: the temp file will be in the dest file's directory. it
2207 * will also be inaccessible from AFP. */
2208 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2209 if (!mktemp(temp)) {
2211 goto err_exchangefile;
2215 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2216 ad_close(adsp, ADFLAGS_HF);
2217 ad_close(addp, ADFLAGS_HF);
2220 /* now, quickly rename the file. we error if we can't. */
2221 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2222 goto err_exchangefile;
2223 of_rename(vol, s_of, sdir, spath, curdir, temp);
2225 /* rename destination to source */
2226 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2227 goto err_src_to_tmp;
2228 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2230 /* rename temp to destination */
2231 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2232 goto err_dest_to_src;
2233 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2235 /* id's need switching. src -> dest and dest -> src.
2236 * we need to re-stat() if it was a cross device copy.
2239 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);