2 * Copyright (c) 1990,1993 Regents of The University of Michigan.
3 * All Rights Reserved. See COPYRIGHT.
8 #endif /* HAVE_CONFIG_H */
16 #else /* STDC_HEADERS */
20 #endif /* HAVE_STRCHR */
21 char *strchr (), *strrchr ();
24 #define memcpy(d,s,n) bcopy ((s), (d), (n))
25 #define memmove(d,s,n) bcopy ((s), (d), (n))
26 #endif /* ! HAVE_MEMCPY */
27 #endif /* STDC_HEADERS */
31 #include <sys/param.h>
33 #include <atalk/adouble.h>
34 #include <atalk/vfs.h>
35 #include <atalk/logger.h>
36 #include <atalk/afp.h>
37 #include <atalk/util.h>
38 #include <atalk/cnid.h>
39 #include <atalk/unix.h>
40 #include <atalk/fce_api.h>
42 #include "directory.h"
52 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
53 * field bytes subfield bytes
56 * ioFlFndrInfo 16 -> type 4 type field
57 * creator 4 creator field
58 * flags 2 finder flags:
60 * location 4 location in window
61 * folder 2 window that contains file
63 * ioFlXFndrInfo 16 -> iconID 2 icon id
65 * script 1 script system
67 * commentID 2 comment id
68 * putawayID 4 home directory id
71 const u_char ufinderi[ADEDLEN_FINDERI] = {
72 0, 0, 0, 0, 0, 0, 0, 0,
73 1, 0, 0, 0, 0, 0, 0, 0,
74 0, 0, 0, 0, 0, 0, 0, 0,
75 0, 0, 0, 0, 0, 0, 0, 0
78 static const u_char old_ufinderi[] = {
79 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
82 /* ----------------------
84 static int default_type(void *finder)
86 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
91 /* FIXME path : unix or mac name ? (for now it's unix name ) */
92 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
95 void *ad_finder = NULL;
99 ad_finder = ad_entry(adp, ADEID_FINDERI);
102 memcpy(data, ad_finder, ADEDLEN_FINDERI);
104 if (default_type(ad_finder))
108 memcpy(data, ufinderi, ADEDLEN_FINDERI);
110 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
113 ashort = htons(FINDERINFO_INVISIBLE);
114 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
120 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
121 linkflag |= htons(FINDERINFO_ISALIAS);
122 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
123 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
124 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
128 /** Only enter if no appledouble information and no finder information found. */
129 if (chk_ext && (em = getextmap( upath ))) {
130 memcpy(data, em->em_type, sizeof( em->em_type ));
131 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
136 /* ---------------------
138 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, u_int32_t utf8)
143 aint = strlen( name );
147 if (utf8_encoding()) {
148 /* but name is an utf8 mac name */
151 /* global static variable... */
153 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
162 if (aint > MACFILELEN)
169 if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
170 aint = UTF8FILELEN_EARLY;
172 utf8 = vol->v_kTextEncoding;
173 memcpy(data, &utf8, sizeof(utf8));
174 data += sizeof(utf8);
177 memcpy(data, &temp, sizeof(temp));
178 data += sizeof(temp);
181 memcpy( data, src, aint );
191 * FIXME: PDINFO is UTF8 and doesn't need adp
193 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
194 (1 << FILPBIT_CDATE) |\
195 (1 << FILPBIT_MDATE) |\
196 (1 << FILPBIT_BDATE) |\
197 (1 << FILPBIT_FINFO) |\
198 (1 << FILPBIT_RFLEN) |\
199 (1 << FILPBIT_EXTRFLEN) |\
200 (1 << FILPBIT_PDINFO) |\
201 (1 << FILPBIT_FNUM) |\
202 (1 << FILPBIT_UNIXPR)))
205 * @brief Get CNID for did/upath args both from database and adouble file
207 * 1. Get the objects CNID as stored in its adouble file
208 * 2. Get the objects CNID from the database
209 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
210 * 4. In case 2 and 3 differ, store 3 in the adouble file
212 * @param vol (rw) volume
213 * @param adp (rw) adouble struct of object upath, might be NULL
214 * @param st (r) stat of upath, must NOT be NULL
215 * @param did (r) parent CNID of upath
216 * @param upath (r) name of object
217 * @param len (r) strlen of upath
219 uint32_t get_id(struct vol *vol,
221 const struct stat *st,
226 static int first = 1; /* mark if this func is called the first time */
228 u_int32_t dbcnid = CNID_INVALID;
231 if (vol->v_cdb != NULL) {
232 /* prime aint with what we think is the cnid, set did to zero for
233 catching moved files */
234 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
236 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
237 /* Throw errors if cnid_add fails. */
238 if (dbcnid == CNID_INVALID) {
240 case CNID_ERR_CLOSE: /* the db is closed */
243 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
244 afp_errno = AFPERR_PARAM;
247 afp_errno = AFPERR_PARAM;
250 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
251 /* we have to do it here for "dbd" because it uses "lazy opening" */
252 /* In order to not end in a loop somehow with goto restart below */
254 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
255 cnid_close(vol->v_cdb);
256 free(vol->v_cnidscheme);
257 vol->v_cnidscheme = strdup("tdb");
259 int flags = CNID_FLAG_MEMORY;
260 if ((vol->v_flags & AFPVOL_NODEV)) {
261 flags |= CNID_FLAG_NODEV;
263 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
265 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
267 /* deactivate cnid caching/storing in AppleDouble files and set ro mode*/
268 vol->v_flags &= ~AFPVOL_CACHE;
269 vol->v_flags |= AFPVOL_RO;
271 /* kill ourself with SIGUSR2 aka msg pending */
272 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
273 "Check server messages for details. Switching to read-only mode.");
274 kill(getpid(), SIGUSR2);
276 goto restart; /* not try again with the temp CNID db */
279 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
280 "Check server messages for details, can't recover from this state!");
284 afp_errno = AFPERR_MISC;
288 else if (adp && (adcnid != dbcnid)) { /* 4 */
289 /* Update the ressource fork. For a folder adp is always null */
290 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
291 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
292 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
303 /* -------------------------- */
304 int getmetadata(struct vol *vol,
306 struct path *path, struct dir *dir,
307 char *buf, size_t *buflen, struct adouble *adp)
309 char *data, *l_nameoff = NULL, *upath;
310 char *utf_nameoff = NULL;
315 u_char achar, fdType[4];
320 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
322 upath = path->u_name;
326 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
327 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
328 || (bitmap & (1 << FILPBIT_FNUM))) {
330 struct dir *cachedfile;
331 int len = strlen(upath);
332 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len, st->st_ctime)) != NULL)
333 id = cachedfile->d_did;
335 id = get_id(vol, adp, st, dir->d_did, upath, len);
337 /* Add it to the cache */
338 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
339 ntohl(dir->d_did), upath, ntohl(id));
341 /* Get macname from unixname first */
342 if (path->m_name == NULL) {
343 if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
344 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
349 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, NULL, st->st_ctime)) == NULL) {
350 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
354 if ((dircache_add(vol, cachedfile)) != 0) {
355 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
363 if (id == CNID_INVALID)
367 path->m_name = utompath(vol, upath, id, utf8_encoding());
370 while ( bitmap != 0 ) {
371 while (( bitmap & 1 ) == 0 ) {
379 ad_getattr(adp, &ashort);
380 } else if (vol_inv_dots(vol) && *upath == '.') {
381 ashort = htons(ATTRBIT_INVISIBLE);
385 /* FIXME do we want a visual clue if the file is read only
388 accessmode( ".", &ma, dir , NULL);
389 if ((ma.ma_user & AR_UWRITE)) {
390 accessmode( upath, &ma, dir , st);
391 if (!(ma.ma_user & AR_UWRITE)) {
392 ashort |= htons(ATTRBIT_NOWRITE);
396 memcpy(data, &ashort, sizeof( ashort ));
397 data += sizeof( ashort );
398 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
399 path->u_name, ntohs(ashort));
403 memcpy(data, &dir->d_did, sizeof( u_int32_t ));
404 data += sizeof( u_int32_t );
405 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
406 path->u_name, ntohl(dir->d_did));
410 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
411 aint = AD_DATE_FROM_UNIX(st->st_mtime);
412 memcpy(data, &aint, sizeof( aint ));
413 data += sizeof( aint );
417 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
418 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
419 aint = AD_DATE_FROM_UNIX(st->st_mtime);
422 aint = AD_DATE_FROM_UNIX(st->st_mtime);
424 memcpy(data, &aint, sizeof( int ));
425 data += sizeof( int );
429 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
430 aint = AD_DATE_START;
431 memcpy(data, &aint, sizeof( int ));
432 data += sizeof( int );
436 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
437 data += ADEDLEN_FINDERI;
442 data += sizeof( u_int16_t );
446 memset(data, 0, sizeof(u_int16_t));
447 data += sizeof( u_int16_t );
451 memcpy(data, &id, sizeof( id ));
452 data += sizeof( id );
453 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
454 path->u_name, ntohl(id));
458 if (st->st_size > 0xffffffff)
461 aint = htonl( st->st_size );
462 memcpy(data, &aint, sizeof( aint ));
463 data += sizeof( aint );
468 if (adp->ad_rlen > 0xffffffff)
471 aint = htonl( adp->ad_rlen);
475 memcpy(data, &aint, sizeof( aint ));
476 data += sizeof( aint );
479 /* Current client needs ProDOS info block for this file.
480 Use simple heuristic and let the Mac "type" string tell
481 us what the PD file code should be. Everything gets a
482 subtype of 0x0000 unless the original value was hashed
483 to "pXYZ" when we created it. See IA, Ver 2.
484 <shirsch@adelphia.net> */
485 case FILPBIT_PDINFO :
486 if (afp_version >= 30) { /* UTF8 name */
487 utf8 = kTextEncodingUTF8;
489 data += sizeof( u_int16_t );
491 memcpy(data, &aint, sizeof( aint ));
492 data += sizeof( aint );
496 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
498 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
502 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
506 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
510 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
514 else if ( fdType[0] == 'p' ) {
516 ashort = (fdType[2] * 256) + fdType[3];
530 memcpy(data, &ashort, sizeof( ashort ));
531 data += sizeof( ashort );
532 memset(data, 0, sizeof( ashort ));
533 data += sizeof( ashort );
536 case FILPBIT_EXTDFLEN:
537 aint = htonl(st->st_size >> 32);
538 memcpy(data, &aint, sizeof( aint ));
539 data += sizeof( aint );
540 aint = htonl(st->st_size);
541 memcpy(data, &aint, sizeof( aint ));
542 data += sizeof( aint );
544 case FILPBIT_EXTRFLEN:
547 aint = htonl(adp->ad_rlen >> 32);
548 memcpy(data, &aint, sizeof( aint ));
549 data += sizeof( aint );
551 aint = htonl(adp->ad_rlen);
552 memcpy(data, &aint, sizeof( aint ));
553 data += sizeof( aint );
555 case FILPBIT_UNIXPR :
556 /* accessmode may change st_mode with ACLs */
557 accessmode( upath, &ma, dir , st);
559 aint = htonl(st->st_uid);
560 memcpy( data, &aint, sizeof( aint ));
561 data += sizeof( aint );
562 aint = htonl(st->st_gid);
563 memcpy( data, &aint, sizeof( aint ));
564 data += sizeof( aint );
567 type == slnk indicates an OSX style symlink,
568 we have to add S_IFLNK to the mode, otherwise
569 10.3 clients freak out. */
573 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
574 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
580 memcpy( data, &aint, sizeof( aint ));
581 data += sizeof( aint );
583 *data++ = ma.ma_user;
584 *data++ = ma.ma_world;
585 *data++ = ma.ma_group;
586 *data++ = ma.ma_owner;
590 return( AFPERR_BITMAP );
596 ashort = htons( data - buf );
597 memcpy(l_nameoff, &ashort, sizeof( ashort ));
598 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
601 ashort = htons( data - buf );
602 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
603 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
605 *buflen = data - buf;
609 /* ----------------------- */
610 int getfilparams(struct vol *vol,
612 struct path *path, struct dir *dir,
613 char *buf, size_t *buflen )
615 struct adouble ad, *adp;
619 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
621 opened = PARAM_NEED_ADP(bitmap);
626 int flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0;
628 adp = of_ad(vol, path, &ad);
629 upath = path->u_name;
631 if ( ad_metadata( upath, flags|ADFLAGS_CREATE, adp) < 0 ) {
634 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
635 upath, strerror(errno));
636 return AFPERR_ACCESS;
638 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
647 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
649 ad_close_metadata( adp);
655 /* ----------------------------- */
656 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
658 struct adouble ad, *adp;
661 struct ofork *of = NULL;
663 int creatf, did, openf, retvalue = AFP_OK;
669 creatf = (unsigned char) *ibuf++;
671 memcpy(&vid, ibuf, sizeof( vid ));
672 ibuf += sizeof( vid );
674 if (NULL == ( vol = getvolbyvid( vid )) ) {
675 return( AFPERR_PARAM );
678 if (vol->v_flags & AFPVOL_RO)
681 memcpy(&did, ibuf, sizeof( did));
682 ibuf += sizeof( did );
684 if (NULL == ( dir = dirlookup( vol, did )) ) {
688 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
689 return get_afp_errno(AFPERR_PARAM);
692 if ( *s_path->m_name == '\0' ) {
693 return( AFPERR_BADTYPE );
696 upath = s_path->u_name;
698 /* if upath is deleted we already in trouble anyway */
699 if ((of = of_findname(s_path))) {
702 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
706 /* on a hard create, fail if file exists and is open */
709 openf = O_RDWR|O_CREAT|O_TRUNC;
711 /* on a soft create, if the file is open then ad_open won't fail
712 because open syscall is not called
717 openf = O_RDWR|O_CREAT|O_EXCL;
720 if ( ad_open( upath, ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF|ADFLAGS_CREATE,
721 openf, 0666, adp) < 0 ) {
725 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
726 return ( AFPERR_NOOBJ );
728 return( AFPERR_EXIST );
730 return( AFPERR_ACCESS );
733 return( AFPERR_DFULL );
735 return( AFPERR_PARAM );
738 if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */
739 /* on noadouble volumes, just creating the data fork is ok */
740 if (vol_noadouble(vol)) {
741 ad_close( adp, ADFLAGS_DF );
742 goto createfile_done;
744 /* FIXME with hard create on an existing file, we already
745 * corrupted the data file.
747 netatalk_unlink( upath );
748 ad_close( adp, ADFLAGS_DF );
749 return AFPERR_ACCESS;
752 path = s_path->m_name;
753 ad_setname(adp, path);
756 if (lstat(upath, &st) != 0) {
757 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
758 upath, strerror(errno));
759 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF);
763 (void)get_id(vol, adp, &st, dir->d_did, upath, strlen(upath));
767 fce_register_new_file(s_path);
769 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
775 if (vol->v_flags & AFPVOL_DROPBOX) {
776 retvalue = matchfile2dirperms(upath, vol, did);
778 #endif /* DROPKLUDGE */
780 setvoltime(obj, vol );
785 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
791 u_int16_t vid, bitmap;
796 memcpy(&vid, ibuf, sizeof( vid ));
797 ibuf += sizeof( vid );
798 if (NULL == ( vol = getvolbyvid( vid )) ) {
799 return( AFPERR_PARAM );
802 if (vol->v_flags & AFPVOL_RO)
805 memcpy(&did, ibuf, sizeof( did ));
806 ibuf += sizeof( did );
807 if (NULL == ( dir = dirlookup( vol, did )) ) {
808 return afp_errno; /* was AFPERR_NOOBJ */
811 memcpy(&bitmap, ibuf, sizeof( bitmap ));
812 bitmap = ntohs( bitmap );
813 ibuf += sizeof( bitmap );
815 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
816 return get_afp_errno(AFPERR_PARAM);
819 if (path_isadir(s_path)) {
820 return( AFPERR_BADTYPE ); /* it's a directory */
823 if ( s_path->st_errno != 0 ) {
824 return( AFPERR_NOOBJ );
827 if ((u_long)ibuf & 1 ) {
831 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
832 setvoltime(obj, vol );
839 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
842 extern struct path Cur_Path;
844 int setfilparams(struct vol *vol,
845 struct path *path, u_int16_t f_bitmap, char *buf )
847 struct adouble ad, *adp;
849 int bit, isad = 1, err = AFP_OK;
851 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
852 u_int16_t ashort, bshort, oshort;
855 u_int16_t upriv_bit = 0;
859 int change_mdate = 0;
860 int change_parent_mdate = 0;
865 u_int16_t bitmap = f_bitmap;
866 u_int32_t cdate,bdate;
867 u_char finder_buf[32];
870 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
873 adp = of_ad(vol, path, &ad);
874 upath = path->u_name;
876 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
877 return AFPERR_ACCESS;
880 /* with unix priv maybe we have to change adouble file priv first */
882 while ( bitmap != 0 ) {
883 while (( bitmap & 1 ) == 0 ) {
890 memcpy(&ashort, buf, sizeof( ashort ));
891 buf += sizeof( ashort );
895 memcpy(&cdate, buf, sizeof(cdate));
896 buf += sizeof( cdate );
899 memcpy(&newdate, buf, sizeof( newdate ));
900 buf += sizeof( newdate );
904 memcpy(&bdate, buf, sizeof( bdate));
905 buf += sizeof( bdate );
909 memcpy(finder_buf, buf, 32 );
910 if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
915 char buf[PATH_MAX+1];
916 if ((fp=open(path->u_name,O_RDONLY))>=0){
917 if ((len=read(fp,buf,PATH_MAX+1))){
918 if (unlink(path->u_name)==0){
920 erc = symlink(buf, path->u_name);
929 goto setfilparam_done;
934 case FILPBIT_UNIXPR :
935 if (!vol_unix_priv(vol)) {
936 /* this volume doesn't use unix priv */
942 change_parent_mdate = 1;
944 memcpy( &aint, buf, sizeof( aint ));
945 f_uid = ntohl (aint);
946 buf += sizeof( aint );
947 memcpy( &aint, buf, sizeof( aint ));
948 f_gid = ntohl (aint);
949 buf += sizeof( aint );
950 setfilowner(vol, f_uid, f_gid, path);
952 memcpy( &upriv, buf, sizeof( upriv ));
953 buf += sizeof( upriv );
954 upriv = ntohl (upriv);
955 if ((upriv & S_IWUSR)) {
956 setfilunixmode(vol, path, upriv);
963 case FILPBIT_PDINFO :
964 if (afp_version < 30) { /* else it's UTF8 name */
967 /* Keep special case to support crlf translations */
968 if ((unsigned int) achar == 0x04) {
969 fdType = (u_char *)"TEXT";
972 xyy[0] = ( u_char ) 'p';
983 /* break while loop */
992 /* second try with adouble open
994 if ( ad_open_metadata( upath, 0, O_CREAT, adp) < 0) {
995 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
997 * For some things, we don't need an adouble header:
998 * - change of modification date
999 * - UNIX privs (Bug-ID #2863424)
1001 if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
1002 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
1003 return AFPERR_ACCESS;
1005 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
1007 } else if ((ad_get_HF_flags( adp ) & O_CREAT) ) {
1008 ad_setname(adp, path->m_name);
1013 while ( bitmap != 0 ) {
1014 while (( bitmap & 1 ) == 0 ) {
1021 ad_getattr(adp, &bshort);
1023 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1024 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1028 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1029 change_parent_mdate = 1;
1030 ad_setattr(adp, bshort);
1032 case FILPBIT_CDATE :
1033 ad_setdate(adp, AD_DATE_CREATE, cdate);
1035 case FILPBIT_MDATE :
1037 case FILPBIT_BDATE :
1038 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1040 case FILPBIT_FINFO :
1041 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1043 ((em = getextmap( path->m_name )) &&
1044 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1045 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1046 || ((em = getdefextmap()) &&
1047 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1048 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1050 memcpy(finder_buf, ufinderi, 8 );
1052 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1054 case FILPBIT_UNIXPR :
1056 setfilunixmode(vol, path, upriv);
1059 case FILPBIT_PDINFO :
1060 if (afp_version < 30) { /* else it's UTF8 name */
1061 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1062 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1067 err = AFPERR_BITMAP;
1068 goto setfilparam_done;
1075 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1076 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1080 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1081 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1087 ad_close_metadata( adp);
1091 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1092 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1093 bitmap = 1<<FILPBIT_MDATE;
1094 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1098 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1104 * renamefile and copyfile take the old and new unix pathnames
1105 * and the new mac name.
1107 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1108 * passing -1 means this is not used, src path is a full path
1109 * src the source path
1110 * dst the dest filename in current dir
1111 * newname the dest mac name
1112 * adp adouble struct of src file, if open, or & zeroed one
1115 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1119 LOG(log_debug, logtype_afpd,
1120 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1122 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1125 return( AFPERR_NOOBJ );
1128 return( AFPERR_ACCESS );
1130 return AFPERR_VLOCK;
1131 case EXDEV : /* Cross device move -- try copy */
1132 /* NOTE: with open file it's an error because after the copy we will
1133 * get two files, it's fixable for our process (eg reopen the new file, get the
1134 * locks, and so on. But it doesn't solve the case with a second process
1136 if (adp->ad_open_forks) {
1137 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1138 return AFPERR_OLOCK; /* little lie */
1140 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1141 /* on error copyfile delete dest */
1144 return deletefile(vol, sdir_fd, src, 0);
1146 return( AFPERR_PARAM );
1150 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1154 /* try to undo the data fork rename,
1155 * we know we are on the same device
1158 unix_rename(-1, dst, sdir_fd, src );
1159 /* return the first error */
1162 return AFPERR_NOOBJ;
1165 return AFPERR_ACCESS ;
1167 return AFPERR_VLOCK;
1169 return AFPERR_PARAM ;
1174 /* don't care if we can't open the newly renamed ressource fork
1176 if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
1177 ad_setname(adp, newname);
1179 ad_close( adp, ADFLAGS_HF );
1186 convert a Mac long name to an utf8 name,
1188 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1192 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1198 /* ---------------- */
1199 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1206 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1212 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1213 if (afp_version >= 30) {
1214 /* convert it to UTF8
1216 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1220 strncpy( newname, ibuf, plen );
1221 newname[ plen ] = '\0';
1223 if (strlen(newname) != plen) {
1224 /* there's \0 in newname, e.g. it's a pathname not
1232 memcpy(&hint, ibuf, sizeof(hint));
1233 ibuf += sizeof(hint);
1235 memcpy(&len16, ibuf, sizeof(len16));
1236 ibuf += sizeof(len16);
1237 plen = ntohs(len16);
1240 if (plen > AFPOBJ_TMPSIZ) {
1243 strncpy( newname, ibuf, plen );
1244 newname[ plen ] = '\0';
1245 if (strlen(newname) != plen) {
1254 /* -----------------------------------
1256 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1258 struct vol *s_vol, *d_vol;
1260 char *newname, *p, *upath;
1261 struct path *s_path;
1262 u_int32_t sdid, ddid;
1263 int err, retvalue = AFP_OK;
1264 u_int16_t svid, dvid;
1266 struct adouble ad, *adp;
1272 memcpy(&svid, ibuf, sizeof( svid ));
1273 ibuf += sizeof( svid );
1274 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1275 return( AFPERR_PARAM );
1278 memcpy(&sdid, ibuf, sizeof( sdid ));
1279 ibuf += sizeof( sdid );
1280 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1284 memcpy(&dvid, ibuf, sizeof( dvid ));
1285 ibuf += sizeof( dvid );
1286 memcpy(&ddid, ibuf, sizeof( ddid ));
1287 ibuf += sizeof( ddid );
1289 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1290 return get_afp_errno(AFPERR_PARAM);
1292 if ( path_isadir(s_path) ) {
1293 return( AFPERR_BADTYPE );
1296 /* don't allow copies when the file is open.
1297 * XXX: the spec only calls for read/deny write access.
1298 * however, copyfile doesn't have any of that info,
1299 * and locks need to stay coherent. as a result,
1300 * we just balk if the file is opened already. */
1302 adp = of_ad(s_vol, s_path, &ad);
1304 if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1305 return AFPERR_DENYCONF;
1307 denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1308 getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1311 retvalue = AFPERR_DENYCONF;
1315 newname = obj->newtmp;
1316 strcpy( newname, s_path->m_name );
1318 p = ctoupath( s_vol, curdir, newname );
1320 retvalue = AFPERR_PARAM;
1325 /* FIXME svid != dvid && dvid's user can't read svid */
1327 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1328 retvalue = AFPERR_PARAM;
1332 if (d_vol->v_flags & AFPVOL_RO) {
1333 retvalue = AFPERR_VLOCK;
1337 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1338 retvalue = afp_errno;
1342 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1343 retvalue = get_afp_errno(AFPERR_NOOBJ);
1347 if ( *s_path->m_name != '\0' ) {
1348 retvalue =path_error(s_path, AFPERR_NOOBJ);
1352 /* one of the handful of places that knows about the path type */
1353 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1354 retvalue = AFPERR_PARAM;
1357 /* newname is always only a filename so curdir *is* its
1360 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1361 retvalue =AFPERR_PARAM;
1365 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1372 if (vol->v_flags & AFPVOL_DROPBOX) {
1373 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1375 #endif /* DROPKLUDGE */
1377 setvoltime(obj, d_vol );
1380 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1384 /* ----------------------- */
1385 static int copy_all(const int dfd, const void *buf,
1391 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1394 while (buflen > 0) {
1395 if ((cc = write(dfd, buf, buflen)) < 0) {
1407 LOG(log_debug9, logtype_afpd, "end copy_all:");
1413 /* --------------------------
1414 * copy only the fork data stream
1416 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1423 if (eid == ADEID_DFORK) {
1424 sfd = ad_data_fileno(ads);
1425 dfd = ad_data_fileno(add);
1428 sfd = ad_reso_fileno(ads);
1429 dfd = ad_reso_fileno(add);
1432 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1435 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1438 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1439 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1443 #define BUF 128*1024*1024
1445 if (fstat(sfd, &st) == 0) {
1448 if ( offset >= st.st_size) {
1451 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1452 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1455 case EINVAL: /* there's no guarantee that all fs support sendfile */
1464 lseek(sfd, offset, SEEK_SET);
1468 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1475 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1482 /* ----------------------------------
1483 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1484 * because we are doing it elsewhere.
1485 * currently if newname is NULL then adp is NULL.
1487 int copyfile(const struct vol *s_vol,
1488 const struct vol *d_vol,
1493 struct adouble *adp)
1495 struct adouble ads, add;
1502 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1503 sfd, src, dst, newname);
1506 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1510 adflags = ADFLAGS_DF;
1512 adflags |= ADFLAGS_HF;
1515 if (ad_openat(sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1520 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1521 /* no resource fork, don't create one for dst file */
1522 adflags &= ~ADFLAGS_HF;
1525 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1527 if (stat_result < 0) {
1528 /* unlikely but if fstat fails, the default file mode will be 0666. */
1529 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1532 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1533 if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1535 ad_close( adp, adflags );
1536 if (EEXIST != ret_err) {
1537 deletefile(d_vol, -1, dst, 0);
1540 return AFPERR_EXIST;
1544 * XXX if the source and the dest don't use the same resource type it's broken
1546 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1547 /* copy the data fork */
1548 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1549 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1557 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1558 /* set the new name in the resource fork */
1559 ad_copy_header(&add, adp);
1560 ad_setname(&add, newname);
1563 ad_close( adp, adflags );
1565 if (ad_close( &add, adflags ) <0) {
1570 deletefile(d_vol, -1, dst, 0);
1572 else if (stat_result == 0) {
1573 /* set dest modification date to src date */
1576 ut.actime = ut.modtime = st.st_mtime;
1578 /* FIXME netatalk doesn't use resource fork file date
1579 * but maybe we should set its modtime too.
1584 switch ( ret_err ) {
1590 return AFPERR_DFULL;
1592 return AFPERR_NOOBJ;
1594 return AFPERR_ACCESS;
1596 return AFPERR_VLOCK;
1598 return AFPERR_PARAM;
1602 /* -----------------------------------
1603 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1604 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1606 when deletefile is called we don't have lock on it, file is closed (for us)
1607 untrue if called by renamefile
1609 ad_open always try to open file RDWR first and ad_lock takes care of
1610 WRITE lock on read only file.
1613 static int check_attrib(struct adouble *adp)
1615 u_int16_t bshort = 0;
1617 ad_getattr(adp, &bshort);
1619 * Does kFPDeleteInhibitBit (bit 8) set?
1621 if ((bshort & htons(ATTRBIT_NODELETE))) {
1622 return AFPERR_OLOCK;
1624 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1630 * dirfd can be used for unlinkat semantics
1632 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1635 struct adouble *adp = NULL;
1636 int adflags, err = AFP_OK;
1639 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1641 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1643 /* was EACCESS error try to get only metadata */
1644 /* we never want to create a resource fork here, we are going to delete it
1645 * moreover sometimes deletefile is called with a no existent file and
1646 * ad_open would create a 0 byte resource fork
1648 if ( ad_metadataat(dirfd, file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
1649 if ((err = check_attrib(&ad))) {
1650 ad_close_metadata(&ad);
1657 /* try to open both forks at once */
1658 adflags = ADFLAGS_DF;
1659 if ( ad_openat(dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, 0, &ad ) < 0 ) {
1664 case EACCES: /* maybe it's a file with no write mode for us */
1665 break; /* was return AFPERR_ACCESS;*/
1678 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1679 adflags |= ADFLAGS_HF;
1680 /* FIXME we have a pb here because we want to know if a file is open
1681 * there's a 'priority inversion' if you can't open the ressource fork RW
1682 * you can delete it if it's open because you can't get a write lock.
1684 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1687 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1689 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1695 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1697 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1699 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1700 cnid_delete(vol->v_cdb, id);
1706 ad_close_metadata(&ad);
1709 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1714 /* ------------------------------------ */
1715 /* return a file id */
1716 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1725 struct path *s_path;
1731 memcpy(&vid, ibuf, sizeof(vid));
1732 ibuf += sizeof(vid);
1734 if (NULL == ( vol = getvolbyvid( vid )) ) {
1735 return( AFPERR_PARAM);
1738 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1742 if (vol->v_flags & AFPVOL_RO)
1743 return AFPERR_VLOCK;
1745 memcpy(&did, ibuf, sizeof( did ));
1746 ibuf += sizeof(did);
1748 if (NULL == ( dir = dirlookup( vol, did )) ) {
1749 return afp_errno; /* was AFPERR_PARAM */
1752 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1753 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1756 if ( path_isadir(s_path) ) {
1757 return( AFPERR_BADTYPE );
1760 upath = s_path->u_name;
1761 switch (s_path->st_errno) {
1763 break; /* success */
1766 return AFPERR_ACCESS;
1768 return AFPERR_NOOBJ;
1770 return AFPERR_PARAM;
1773 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1774 memcpy(rbuf, &id, sizeof(id));
1775 *rbuflen = sizeof(id);
1776 return AFPERR_EXISTID;
1779 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1780 memcpy(rbuf, &id, sizeof(id));
1781 *rbuflen = sizeof(id);
1788 /* ------------------------------- */
1794 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1797 struct reenum *param = data;
1798 struct vol *vol = param->vol;
1799 cnid_t did = param->did;
1802 if ( lstat(de->d_name, &path.st)<0 )
1805 /* update or add to cnid */
1806 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1808 #if AD_VERSION > AD_VERSION1
1809 if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1810 struct adouble ad, *adp;
1814 path.u_name = de->d_name;
1816 adp = of_ad(vol, &path, &ad);
1818 if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1821 if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1824 ad_close_metadata(adp);
1826 #endif /* AD_VERSION > AD_VERSION1 */
1831 /* --------------------
1832 * Ok the db is out of synch with the dir.
1833 * but if it's a deleted file we don't want to do it again and again.
1836 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1842 if (vol->v_cdb == NULL) {
1846 /* FIXME use of_statdir ? */
1847 if (lstat(name, &st)) {
1851 if (dirreenumerate(dir, &st)) {
1852 /* we already did it once and the dir haven't been modified */
1857 data.did = dir->d_did;
1858 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1859 setdiroffcnt(curdir, &st, ret);
1860 dir->d_flags |= DIRF_CNID;
1866 /* ------------------------------
1867 resolve a file id */
1868 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1877 u_int16_t vid, bitmap;
1879 static char buffer[12 + MAXPATHLEN + 1];
1880 int len = 12 + MAXPATHLEN + 1;
1885 memcpy(&vid, ibuf, sizeof(vid));
1886 ibuf += sizeof(vid);
1888 if (NULL == ( vol = getvolbyvid( vid )) ) {
1889 return( AFPERR_PARAM);
1892 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1896 memcpy(&id, ibuf, sizeof( id ));
1901 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1905 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1906 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1909 if (NULL == ( dir = dirlookup( vol, id )) ) {
1910 return AFPERR_NOID; /* idem AFPERR_PARAM */
1912 if (movecwd(vol, dir) < 0) {
1916 return AFPERR_ACCESS;
1920 return AFPERR_PARAM;
1924 memset(&path, 0, sizeof(path));
1925 path.u_name = upath;
1926 if ( of_stat(&path) < 0 ) {
1928 /* with nfs and our working directory is deleted */
1929 if (errno == ESTALE) {
1933 if ( errno == ENOENT && !retry) {
1934 /* cnid db is out of sync, reenumerate the directory and update ids */
1935 reenumerate_id(vol, ".", dir);
1943 return AFPERR_ACCESS;
1947 return AFPERR_PARAM;
1951 /* directories are bad */
1952 if (S_ISDIR(path.st.st_mode)) {
1953 /* OS9 and OSX don't return the same error code */
1954 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1957 memcpy(&bitmap, ibuf, sizeof(bitmap));
1958 bitmap = ntohs( bitmap );
1959 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1963 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1964 rbuf + sizeof(bitmap), &buflen))) {
1967 *rbuflen = buflen + sizeof(bitmap);
1968 memcpy(rbuf, ibuf, sizeof(bitmap));
1973 /* ------------------------------ */
1974 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1984 static char buffer[12 + MAXPATHLEN + 1];
1985 int len = 12 + MAXPATHLEN + 1;
1990 memcpy(&vid, ibuf, sizeof(vid));
1991 ibuf += sizeof(vid);
1993 if (NULL == ( vol = getvolbyvid( vid )) ) {
1994 return( AFPERR_PARAM);
1997 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
2001 if (vol->v_flags & AFPVOL_RO)
2002 return AFPERR_VLOCK;
2004 memcpy(&id, ibuf, sizeof( id ));
2008 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
2012 if (NULL == ( dir = dirlookup( vol, id )) ) {
2013 if (afp_errno == AFPERR_NOOBJ) {
2017 return( AFPERR_PARAM );
2021 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
2025 return AFPERR_ACCESS;
2030 /* still try to delete the id */
2034 return AFPERR_PARAM;
2037 else if (S_ISDIR(st.st_mode)) /* directories are bad */
2038 return AFPERR_BADTYPE;
2041 if (cnid_delete(vol->v_cdb, fileid)) {
2044 return AFPERR_VLOCK;
2047 return AFPERR_ACCESS;
2049 return AFPERR_PARAM;
2056 /* ------------------------------ */
2057 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
2061 if (path->st_errno) {
2062 switch (path->st_errno) {
2064 afp_errno = AFPERR_NOID;
2068 afp_errno = AFPERR_ACCESS;
2071 afp_errno = AFPERR_PARAM;
2076 /* we use file_access both for legacy Mac perm and
2077 * for unix privilege, rename will take care of folder perms
2079 if (file_access(path, OPENACC_WR ) < 0) {
2080 afp_errno = AFPERR_ACCESS;
2084 if ((*of = of_findname(path))) {
2085 /* reuse struct adouble so it won't break locks */
2089 ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
2091 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2093 * The user must have the Read & Write privilege for both files in order to use this command.
2095 ad_close(adp, ADFLAGS_HF);
2096 afp_errno = AFPERR_ACCESS;
2103 #define APPLETEMP ".AppleTempXXXXXX"
2105 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2107 struct stat srcst, destst;
2109 struct dir *dir, *sdir;
2110 char *spath, temp[17], *p;
2111 char *supath, *upath;
2116 struct adouble *adsp = NULL;
2117 struct adouble *addp = NULL;
2118 struct ofork *s_of = NULL;
2119 struct ofork *d_of = NULL;
2132 memcpy(&vid, ibuf, sizeof(vid));
2133 ibuf += sizeof(vid);
2135 if (NULL == ( vol = getvolbyvid( vid )) ) {
2136 return( AFPERR_PARAM);
2139 if ((vol->v_flags & AFPVOL_RO))
2140 return AFPERR_VLOCK;
2142 /* source and destination dids */
2143 memcpy(&sid, ibuf, sizeof(sid));
2144 ibuf += sizeof(sid);
2145 memcpy(&did, ibuf, sizeof(did));
2146 ibuf += sizeof(did);
2149 if (NULL == (dir = dirlookup( vol, sid )) ) {
2150 return afp_errno; /* was AFPERR_PARAM */
2153 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2154 return get_afp_errno(AFPERR_NOOBJ);
2157 if ( path_isadir(path) ) {
2158 return AFPERR_BADTYPE; /* it's a dir */
2161 /* save some stuff */
2164 spath = obj->oldtmp;
2165 supath = obj->newtmp;
2166 strcpy(spath, path->m_name);
2167 strcpy(supath, path->u_name); /* this is for the cnid changing */
2168 p = absupath( vol, sdir, supath);
2170 /* pathname too long */
2171 return AFPERR_PARAM ;
2174 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2175 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2179 /* ***** from here we may have resource fork open **** */
2181 /* look for the source cnid. if it doesn't exist, don't worry about
2183 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2185 if (NULL == ( dir = dirlookup( vol, did )) ) {
2186 err = afp_errno; /* was AFPERR_PARAM */
2187 goto err_exchangefile;
2190 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2191 err = get_afp_errno(AFPERR_NOOBJ);
2192 goto err_exchangefile;
2195 if ( path_isadir(path) ) {
2196 err = AFPERR_BADTYPE;
2197 goto err_exchangefile;
2200 /* FPExchangeFiles is the only call that can return the SameObj
2202 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2203 err = AFPERR_SAMEOBJ;
2204 goto err_exchangefile;
2207 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2208 if (!(addp = find_adouble( path, &d_of, &add))) {
2210 goto err_exchangefile;
2214 /* they are not on the same device and at least one is open
2215 * FIXME broken for for crossdev and adouble v2
2218 crossdev = (srcst.st_dev != destst.st_dev);
2219 if (/* (d_of || s_of) && */ crossdev) {
2221 goto err_exchangefile;
2224 /* look for destination id. */
2225 upath = path->u_name;
2226 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2228 /* construct a temp name.
2229 * NOTE: the temp file will be in the dest file's directory. it
2230 * will also be inaccessible from AFP. */
2231 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2232 if (!mktemp(temp)) {
2234 goto err_exchangefile;
2238 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2239 ad_close(adsp, ADFLAGS_HF);
2240 ad_close(addp, ADFLAGS_HF);
2243 /* now, quickly rename the file. we error if we can't. */
2244 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2245 goto err_exchangefile;
2246 of_rename(vol, s_of, sdir, spath, curdir, temp);
2248 /* rename destination to source */
2249 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2250 goto err_src_to_tmp;
2251 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2253 /* rename temp to destination */
2254 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2255 goto err_dest_to_src;
2256 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2258 /* id's need switching. src -> dest and dest -> src.
2259 * we need to re-stat() if it was a cross device copy.
2262 cnid_delete(vol->v_cdb, sid);
2264 cnid_delete(vol->v_cdb, did);
2266 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2267 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2269 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2270 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2275 err = AFPERR_ACCESS;
2280 goto err_temp_to_dest;
2283 /* here we need to reopen if crossdev */
2284 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2289 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2294 /* change perms, src gets dest perm and vice versa */
2299 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2300 err = AFP_OK; /* ignore error */
2301 goto err_temp_to_dest;
2305 * we need to exchange ACL entries as well
2307 /* exchange_acls(vol, p, upath); */
2312 path->m_name = NULL;
2313 path->u_name = upath;
2315 setfilunixmode(vol, path, destst.st_mode);
2316 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2323 setfilunixmode(vol, path, srcst.st_mode);
2324 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2326 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2327 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2332 goto err_exchangefile;
2334 /* all this stuff is so that we can unwind a failed operation
2337 /* rename dest to temp */
2338 renamefile(vol, -1, upath, temp, temp, adsp);
2339 of_rename(vol, s_of, curdir, upath, curdir, temp);
2342 /* rename source back to dest */
2343 renamefile(vol, -1, p, upath, path->m_name, addp);
2344 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2347 /* rename temp back to source */
2348 renamefile(vol, -1, temp, p, spath, adsp);
2349 of_rename(vol, s_of, curdir, temp, sdir, spath);
2352 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2353 ad_close(adsp, ADFLAGS_HF);
2355 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2356 ad_close(addp, ADFLAGS_HF);
2360 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2361 (void)dir_remove(vol, cached);
2362 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2363 (void)dir_remove(vol, cached);