2 * Copyright (c) 1990,1993 Regents of The University of Michigan.
3 * All Rights Reserved. See COPYRIGHT.
8 #endif /* HAVE_CONFIG_H */
16 #include <sys/param.h>
18 #include <atalk/adouble.h>
19 #include <atalk/vfs.h>
20 #include <atalk/logger.h>
21 #include <atalk/afp.h>
22 #include <atalk/util.h>
23 #include <atalk/cnid.h>
24 #include <atalk/unix.h>
26 #include "directory.h"
36 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
37 * field bytes subfield bytes
40 * ioFlFndrInfo 16 -> type 4 type field
41 * creator 4 creator field
42 * flags 2 finder flags:
44 * location 4 location in window
45 * folder 2 window that contains file
47 * ioFlXFndrInfo 16 -> iconID 2 icon id
49 * script 1 script system
51 * commentID 2 comment id
52 * putawayID 4 home directory id
55 const u_char ufinderi[ADEDLEN_FINDERI] = {
56 0, 0, 0, 0, 0, 0, 0, 0,
57 1, 0, 0, 0, 0, 0, 0, 0,
58 0, 0, 0, 0, 0, 0, 0, 0,
59 0, 0, 0, 0, 0, 0, 0, 0
62 static const u_char old_ufinderi[] = {
63 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
66 /* ----------------------
68 static int default_type(void *finder)
70 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
75 /* FIXME path : unix or mac name ? (for now it's unix name ) */
76 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
79 void *ad_finder = NULL;
83 ad_finder = ad_entry(adp, ADEID_FINDERI);
86 memcpy(data, ad_finder, ADEDLEN_FINDERI);
88 if (default_type(ad_finder))
92 memcpy(data, ufinderi, ADEDLEN_FINDERI);
94 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
97 ashort = htons(FINDERINFO_INVISIBLE);
98 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
104 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
105 linkflag |= htons(FINDERINFO_ISALIAS);
106 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
107 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
108 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
112 /** Only enter if no appledouble information and no finder information found. */
113 if (chk_ext && (em = getextmap( upath ))) {
114 memcpy(data, em->em_type, sizeof( em->em_type ));
115 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
120 /* ---------------------
122 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, u_int32_t utf8)
127 aint = strlen( name );
131 if (utf8_encoding()) {
132 /* but name is an utf8 mac name */
135 /* global static variable... */
137 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
146 if (aint > MACFILELEN)
153 if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
154 aint = UTF8FILELEN_EARLY;
156 utf8 = vol->v_kTextEncoding;
157 memcpy(data, &utf8, sizeof(utf8));
158 data += sizeof(utf8);
161 memcpy(data, &temp, sizeof(temp));
162 data += sizeof(temp);
165 memcpy( data, src, aint );
175 * FIXME: PDINFO is UTF8 and doesn't need adp
177 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
178 (1 << FILPBIT_CDATE) |\
179 (1 << FILPBIT_MDATE) |\
180 (1 << FILPBIT_BDATE) |\
181 (1 << FILPBIT_FINFO) |\
182 (1 << FILPBIT_RFLEN) |\
183 (1 << FILPBIT_EXTRFLEN) |\
184 (1 << FILPBIT_PDINFO) |\
185 (1 << FILPBIT_FNUM) |\
186 (1 << FILPBIT_UNIXPR)))
189 * @brief Get CNID for did/upath args both from database and adouble file
191 * 1. Get the objects CNID as stored in its adouble file
192 * 2. Get the objects CNID from the database
193 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
194 * 4. In case 2 and 3 differ, store 3 in the adouble file
196 * @param vol (rw) volume
197 * @param adp (rw) adouble struct of object upath, might be NULL
198 * @param st (r) stat of upath, must NOT be NULL
199 * @param did (r) parent CNID of upath
200 * @param upath (r) name of object
201 * @param len (r) strlen of upath
203 uint32_t get_id(struct vol *vol,
205 const struct stat *st,
210 static int first = 1; /* mark if this func is called the first time */
212 u_int32_t dbcnid = CNID_INVALID;
215 if (vol->v_cdb != NULL) {
216 /* prime aint with what we think is the cnid, set did to zero for
217 catching moved files */
218 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
220 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
221 /* Throw errors if cnid_add fails. */
222 if (dbcnid == CNID_INVALID) {
224 case CNID_ERR_CLOSE: /* the db is closed */
227 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
228 afp_errno = AFPERR_PARAM;
231 afp_errno = AFPERR_PARAM;
234 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
235 /* we have to do it here for "dbd" because it uses "lazy opening" */
236 /* In order to not end in a loop somehow with goto restart below */
238 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
239 cnid_close(vol->v_cdb);
240 free(vol->v_cnidscheme);
241 vol->v_cnidscheme = strdup("tdb");
243 int flags = CNID_FLAG_MEMORY;
244 if ((vol->v_flags & AFPVOL_NODEV)) {
245 flags |= CNID_FLAG_NODEV;
247 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
249 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
251 /* deactivate cnid caching/storing in AppleDouble files and set ro mode*/
252 vol->v_flags &= ~AFPVOL_CACHE;
253 vol->v_flags |= AFPVOL_RO;
255 /* kill ourself with SIGUSR2 aka msg pending */
256 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
257 "Check server messages for details. Switching to read-only mode.");
258 kill(getpid(), SIGUSR2);
260 goto restart; /* not try again with the temp CNID db */
263 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
264 "Check server messages for details, can't recover from this state!");
268 afp_errno = AFPERR_MISC;
272 else if (adp && (adcnid != dbcnid)) { /* 4 */
273 /* Update the ressource fork. For a folder adp is always null */
274 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
275 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
276 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
287 /* -------------------------- */
288 int getmetadata(struct vol *vol,
290 struct path *path, struct dir *dir,
291 char *buf, size_t *buflen, struct adouble *adp)
293 char *data, *l_nameoff = NULL, *upath;
294 char *utf_nameoff = NULL;
299 u_char achar, fdType[4];
304 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
306 upath = path->u_name;
310 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
311 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
312 || (bitmap & (1 << FILPBIT_FNUM))) {
315 struct dir *cachedfile;
316 int len = strlen(upath);
317 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
318 id = cachedfile->d_did;
320 id = get_id(vol, adp, st, dir->d_did, upath, len);
322 /* Add it to the cache */
323 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
324 ntohl(dir->d_did), upath, ntohl(id));
326 /* Get macname from unixname first */
327 if (path->m_name == NULL) {
328 if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
329 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
335 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
336 || (bconchar(fullpath, '/') != BSTR_OK)
337 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
338 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
342 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
343 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
347 if ((dircache_add(vol, cachedfile)) != 0) {
348 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
356 if (id == CNID_INVALID)
360 path->m_name = utompath(vol, upath, id, utf8_encoding());
363 while ( bitmap != 0 ) {
364 while (( bitmap & 1 ) == 0 ) {
372 ad_getattr(adp, &ashort);
373 } else if (vol_inv_dots(vol) && *upath == '.') {
374 ashort = htons(ATTRBIT_INVISIBLE);
378 /* FIXME do we want a visual clue if the file is read only
381 accessmode( ".", &ma, dir , NULL);
382 if ((ma.ma_user & AR_UWRITE)) {
383 accessmode( upath, &ma, dir , st);
384 if (!(ma.ma_user & AR_UWRITE)) {
385 ashort |= htons(ATTRBIT_NOWRITE);
389 memcpy(data, &ashort, sizeof( ashort ));
390 data += sizeof( ashort );
391 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
392 path->u_name, ntohs(ashort));
396 memcpy(data, &dir->d_did, sizeof( u_int32_t ));
397 data += sizeof( u_int32_t );
398 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
399 path->u_name, ntohl(dir->d_did));
403 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
404 aint = AD_DATE_FROM_UNIX(st->st_mtime);
405 memcpy(data, &aint, sizeof( aint ));
406 data += sizeof( aint );
410 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
411 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
412 aint = AD_DATE_FROM_UNIX(st->st_mtime);
415 aint = AD_DATE_FROM_UNIX(st->st_mtime);
417 memcpy(data, &aint, sizeof( int ));
418 data += sizeof( int );
422 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
423 aint = AD_DATE_START;
424 memcpy(data, &aint, sizeof( int ));
425 data += sizeof( int );
429 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
430 data += ADEDLEN_FINDERI;
435 data += sizeof( u_int16_t );
439 memset(data, 0, sizeof(u_int16_t));
440 data += sizeof( u_int16_t );
444 memcpy(data, &id, sizeof( id ));
445 data += sizeof( id );
446 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
447 path->u_name, ntohl(id));
451 if (st->st_size > 0xffffffff)
454 aint = htonl( st->st_size );
455 memcpy(data, &aint, sizeof( aint ));
456 data += sizeof( aint );
461 if (adp->ad_rlen > 0xffffffff)
464 aint = htonl( adp->ad_rlen);
468 memcpy(data, &aint, sizeof( aint ));
469 data += sizeof( aint );
472 /* Current client needs ProDOS info block for this file.
473 Use simple heuristic and let the Mac "type" string tell
474 us what the PD file code should be. Everything gets a
475 subtype of 0x0000 unless the original value was hashed
476 to "pXYZ" when we created it. See IA, Ver 2.
477 <shirsch@adelphia.net> */
478 case FILPBIT_PDINFO :
479 if (afp_version >= 30) { /* UTF8 name */
480 utf8 = kTextEncodingUTF8;
482 data += sizeof( u_int16_t );
484 memcpy(data, &aint, sizeof( aint ));
485 data += sizeof( aint );
489 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
491 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
495 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
499 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
503 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
507 else if ( fdType[0] == 'p' ) {
509 ashort = (fdType[2] * 256) + fdType[3];
523 memcpy(data, &ashort, sizeof( ashort ));
524 data += sizeof( ashort );
525 memset(data, 0, sizeof( ashort ));
526 data += sizeof( ashort );
529 case FILPBIT_EXTDFLEN:
530 aint = htonl(st->st_size >> 32);
531 memcpy(data, &aint, sizeof( aint ));
532 data += sizeof( aint );
533 aint = htonl(st->st_size);
534 memcpy(data, &aint, sizeof( aint ));
535 data += sizeof( aint );
537 case FILPBIT_EXTRFLEN:
540 aint = htonl(adp->ad_rlen >> 32);
541 memcpy(data, &aint, sizeof( aint ));
542 data += sizeof( aint );
544 aint = htonl(adp->ad_rlen);
545 memcpy(data, &aint, sizeof( aint ));
546 data += sizeof( aint );
548 case FILPBIT_UNIXPR :
549 /* accessmode may change st_mode with ACLs */
550 accessmode( upath, &ma, dir , st);
552 aint = htonl(st->st_uid);
553 memcpy( data, &aint, sizeof( aint ));
554 data += sizeof( aint );
555 aint = htonl(st->st_gid);
556 memcpy( data, &aint, sizeof( aint ));
557 data += sizeof( aint );
560 type == slnk indicates an OSX style symlink,
561 we have to add S_IFLNK to the mode, otherwise
562 10.3 clients freak out. */
566 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
567 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
573 memcpy( data, &aint, sizeof( aint ));
574 data += sizeof( aint );
576 *data++ = ma.ma_user;
577 *data++ = ma.ma_world;
578 *data++ = ma.ma_group;
579 *data++ = ma.ma_owner;
583 return( AFPERR_BITMAP );
589 ashort = htons( data - buf );
590 memcpy(l_nameoff, &ashort, sizeof( ashort ));
591 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
594 ashort = htons( data - buf );
595 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
596 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
598 *buflen = data - buf;
602 /* ----------------------- */
603 int getfilparams(struct vol *vol,
605 struct path *path, struct dir *dir,
606 char *buf, size_t *buflen )
608 struct adouble ad, *adp;
612 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
614 opened = PARAM_NEED_ADP(bitmap);
619 int flags = (bitmap & (1 << FILPBIT_ATTR)) ? ADFLAGS_CHECK_OF : 0;
621 adp = of_ad(vol, path, &ad);
622 upath = path->u_name;
624 if ( ad_metadata( upath, flags, adp) < 0 ) {
627 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
628 upath, strerror(errno));
629 return AFPERR_ACCESS;
631 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
640 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
641 ad_close_metadata( adp);
646 /* ----------------------------- */
647 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
652 struct ofork *of = NULL;
654 int creatf, did, openf, retvalue = AFP_OK;
660 creatf = (unsigned char) *ibuf++;
662 memcpy(&vid, ibuf, sizeof( vid ));
663 ibuf += sizeof( vid );
665 if (NULL == ( vol = getvolbyvid( vid )) )
666 return( AFPERR_PARAM );
668 if (vol->v_flags & AFPVOL_RO)
671 memcpy(&did, ibuf, sizeof( did));
672 ibuf += sizeof( did );
674 if (NULL == ( dir = dirlookup( vol, did )) )
677 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
678 return get_afp_errno(AFPERR_PARAM);
680 if ( *s_path->m_name == '\0' )
681 return( AFPERR_BADTYPE );
683 upath = s_path->u_name;
684 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
686 /* if upath is deleted we already in trouble anyway */
687 if ((of = of_findname(s_path))) {
695 openf = O_RDWR|O_CREAT|O_TRUNC;
697 /* on a soft create, if the file is open then ad_open won't fail
698 because open syscall is not called */
699 openf = O_RDWR|O_CREAT|O_EXCL;
701 if ( ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF,
702 openf, 0666, openf, 0666) < 0 ) {
706 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
707 return ( AFPERR_NOOBJ );
709 return( AFPERR_EXIST );
711 return( AFPERR_ACCESS );
714 return( AFPERR_DFULL );
716 return( AFPERR_PARAM );
719 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
720 /* on noadouble volumes, just creating the data fork is ok */
721 if (vol_noadouble(vol)) {
722 ad_close( &ad, ADFLAGS_DF );
723 goto createfile_done;
725 /* FIXME with hard create on an existing file, we already
726 * corrupted the data file.
728 netatalk_unlink( upath );
729 ad_close( &ad, ADFLAGS_DF );
730 return AFPERR_ACCESS;
733 path = s_path->m_name;
734 ad_setname(&ad, path);
737 if (lstat(upath, &st) != 0) {
738 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
739 upath, strerror(errno));
740 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
744 (void)get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath));
747 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
752 setvoltime(obj, vol );
757 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
763 u_int16_t vid, bitmap;
768 memcpy(&vid, ibuf, sizeof( vid ));
769 ibuf += sizeof( vid );
770 if (NULL == ( vol = getvolbyvid( vid )) ) {
771 return( AFPERR_PARAM );
774 if (vol->v_flags & AFPVOL_RO)
777 memcpy(&did, ibuf, sizeof( did ));
778 ibuf += sizeof( did );
779 if (NULL == ( dir = dirlookup( vol, did )) ) {
780 return afp_errno; /* was AFPERR_NOOBJ */
783 memcpy(&bitmap, ibuf, sizeof( bitmap ));
784 bitmap = ntohs( bitmap );
785 ibuf += sizeof( bitmap );
787 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
788 return get_afp_errno(AFPERR_PARAM);
791 if (path_isadir(s_path)) {
792 return( AFPERR_BADTYPE ); /* it's a directory */
795 if ( s_path->st_errno != 0 ) {
796 return( AFPERR_NOOBJ );
799 if ((u_long)ibuf & 1 ) {
803 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
804 setvoltime(obj, vol );
811 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
814 extern struct path Cur_Path;
816 int setfilparams(struct vol *vol,
817 struct path *path, u_int16_t f_bitmap, char *buf )
819 struct adouble ad, *adp;
821 int bit, isad = 1, err = AFP_OK;
823 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
824 u_int16_t ashort, bshort, oshort;
827 u_int16_t upriv_bit = 0;
831 int change_mdate = 0;
832 int change_parent_mdate = 0;
837 u_int16_t bitmap = f_bitmap;
838 u_int32_t cdate,bdate;
839 u_char finder_buf[32];
842 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
845 adp = of_ad(vol, path, &ad);
846 upath = path->u_name;
848 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
849 return AFPERR_ACCESS;
852 /* with unix priv maybe we have to change adouble file priv first */
854 while ( bitmap != 0 ) {
855 while (( bitmap & 1 ) == 0 ) {
862 memcpy(&ashort, buf, sizeof( ashort ));
863 buf += sizeof( ashort );
867 memcpy(&cdate, buf, sizeof(cdate));
868 buf += sizeof( cdate );
871 memcpy(&newdate, buf, sizeof( newdate ));
872 buf += sizeof( newdate );
876 memcpy(&bdate, buf, sizeof( bdate));
877 buf += sizeof( bdate );
881 memcpy(finder_buf, buf, 32 );
882 if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
887 char buf[PATH_MAX+1];
888 if ((fp=open(path->u_name,O_RDONLY))>=0){
889 if ((len=read(fp,buf,PATH_MAX+1))){
890 if (unlink(path->u_name)==0){
892 erc = symlink(buf, path->u_name);
901 goto setfilparam_done;
906 case FILPBIT_UNIXPR :
907 if (!vol_unix_priv(vol)) {
908 /* this volume doesn't use unix priv */
914 change_parent_mdate = 1;
916 memcpy( &aint, buf, sizeof( aint ));
917 f_uid = ntohl (aint);
918 buf += sizeof( aint );
919 memcpy( &aint, buf, sizeof( aint ));
920 f_gid = ntohl (aint);
921 buf += sizeof( aint );
922 setfilowner(vol, f_uid, f_gid, path);
924 memcpy( &upriv, buf, sizeof( upriv ));
925 buf += sizeof( upriv );
926 upriv = ntohl (upriv);
927 if ((upriv & S_IWUSR)) {
928 setfilunixmode(vol, path, upriv);
935 case FILPBIT_PDINFO :
936 if (afp_version < 30) { /* else it's UTF8 name */
939 /* Keep special case to support crlf translations */
940 if ((unsigned int) achar == 0x04) {
941 fdType = (u_char *)"TEXT";
944 xyy[0] = ( u_char ) 'p';
955 /* break while loop */
964 /* second try with adouble open
966 if ( ad_open(adp, upath, ADFLAGS_HF, O_RDWR | O_CREAT, 0666) < 0) {
967 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
969 * For some things, we don't need an adouble header:
970 * - change of modification date
971 * - UNIX privs (Bug-ID #2863424)
973 if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
974 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
975 return AFPERR_ACCESS;
977 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
979 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
980 ad_setname(adp, path->m_name);
985 while ( bitmap != 0 ) {
986 while (( bitmap & 1 ) == 0 ) {
993 ad_getattr(adp, &bshort);
995 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
996 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1000 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1001 change_parent_mdate = 1;
1002 ad_setattr(adp, bshort);
1004 case FILPBIT_CDATE :
1005 ad_setdate(adp, AD_DATE_CREATE, cdate);
1007 case FILPBIT_MDATE :
1009 case FILPBIT_BDATE :
1010 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1012 case FILPBIT_FINFO :
1013 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1015 ((em = getextmap( path->m_name )) &&
1016 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1017 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1018 || ((em = getdefextmap()) &&
1019 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1020 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1022 memcpy(finder_buf, ufinderi, 8 );
1024 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1026 case FILPBIT_UNIXPR :
1028 setfilunixmode(vol, path, upriv);
1031 case FILPBIT_PDINFO :
1032 if (afp_version < 30) { /* else it's UTF8 name */
1033 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1034 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1039 err = AFPERR_BITMAP;
1040 goto setfilparam_done;
1047 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1048 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1052 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1053 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1059 ad_close_metadata( adp);
1062 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1063 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1064 bitmap = 1<<FILPBIT_MDATE;
1065 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1069 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1075 * renamefile and copyfile take the old and new unix pathnames
1076 * and the new mac name.
1078 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1079 * passing -1 means this is not used, src path is a full path
1080 * src the source path
1081 * dst the dest filename in current dir
1082 * newname the dest mac name
1083 * adp adouble struct of src file, if open, or & zeroed one
1086 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1090 LOG(log_debug, logtype_afpd,
1091 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1093 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1096 return( AFPERR_NOOBJ );
1099 return( AFPERR_ACCESS );
1101 return AFPERR_VLOCK;
1102 case EXDEV : /* Cross device move -- try copy */
1103 /* NOTE: with open file it's an error because after the copy we will
1104 * get two files, it's fixable for our process (eg reopen the new file, get the
1105 * locks, and so on. But it doesn't solve the case with a second process
1107 if (adp->ad_open_forks) {
1108 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1109 return AFPERR_OLOCK; /* little lie */
1111 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1112 /* on error copyfile delete dest */
1115 return deletefile(vol, sdir_fd, src, 0);
1117 return( AFPERR_PARAM );
1121 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1125 /* try to undo the data fork rename,
1126 * we know we are on the same device
1129 unix_rename(-1, dst, sdir_fd, src );
1130 /* return the first error */
1133 return AFPERR_NOOBJ;
1136 return AFPERR_ACCESS ;
1138 return AFPERR_VLOCK;
1140 return AFPERR_PARAM ;
1145 /* don't care if we can't open the newly renamed ressource fork */
1146 if (ad_open(adp, dst, ADFLAGS_HF, O_RDWR) == 0) {
1147 ad_setname(adp, newname);
1149 ad_close( adp, ADFLAGS_HF );
1156 convert a Mac long name to an utf8 name,
1158 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1162 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1168 /* ---------------- */
1169 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1176 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1182 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1183 if (afp_version >= 30) {
1184 /* convert it to UTF8
1186 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1190 strncpy( newname, ibuf, plen );
1191 newname[ plen ] = '\0';
1193 if (strlen(newname) != plen) {
1194 /* there's \0 in newname, e.g. it's a pathname not
1202 memcpy(&hint, ibuf, sizeof(hint));
1203 ibuf += sizeof(hint);
1205 memcpy(&len16, ibuf, sizeof(len16));
1206 ibuf += sizeof(len16);
1207 plen = ntohs(len16);
1210 if (plen > AFPOBJ_TMPSIZ) {
1213 strncpy( newname, ibuf, plen );
1214 newname[ plen ] = '\0';
1215 if (strlen(newname) != plen) {
1224 /* -----------------------------------
1226 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1228 struct vol *s_vol, *d_vol;
1230 char *newname, *p, *upath;
1231 struct path *s_path;
1232 u_int32_t sdid, ddid;
1233 int err, retvalue = AFP_OK;
1234 u_int16_t svid, dvid;
1236 struct adouble ad, *adp;
1242 memcpy(&svid, ibuf, sizeof( svid ));
1243 ibuf += sizeof( svid );
1244 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1245 return( AFPERR_PARAM );
1248 memcpy(&sdid, ibuf, sizeof( sdid ));
1249 ibuf += sizeof( sdid );
1250 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1254 memcpy(&dvid, ibuf, sizeof( dvid ));
1255 ibuf += sizeof( dvid );
1256 memcpy(&ddid, ibuf, sizeof( ddid ));
1257 ibuf += sizeof( ddid );
1259 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1260 return get_afp_errno(AFPERR_PARAM);
1262 if ( path_isadir(s_path) ) {
1263 return( AFPERR_BADTYPE );
1266 /* don't allow copies when the file is open.
1267 * XXX: the spec only calls for read/deny write access.
1268 * however, copyfile doesn't have any of that info,
1269 * and locks need to stay coherent. as a result,
1270 * we just balk if the file is opened already. */
1272 adp = of_ad(s_vol, s_path, &ad);
1274 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0) {
1275 return AFPERR_DENYCONF;
1277 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1278 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1281 retvalue = AFPERR_DENYCONF;
1285 newname = obj->newtmp;
1286 strcpy( newname, s_path->m_name );
1288 p = ctoupath( s_vol, curdir, newname );
1290 retvalue = AFPERR_PARAM;
1294 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1295 retvalue = AFPERR_PARAM;
1299 if (d_vol->v_flags & AFPVOL_RO) {
1300 retvalue = AFPERR_VLOCK;
1304 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1305 retvalue = afp_errno;
1309 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1310 retvalue = get_afp_errno(AFPERR_NOOBJ);
1314 if ( *s_path->m_name != '\0' ) {
1315 retvalue =path_error(s_path, AFPERR_NOOBJ);
1319 /* one of the handful of places that knows about the path type */
1320 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1321 retvalue = AFPERR_PARAM;
1324 /* newname is always only a filename so curdir *is* its
1327 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1328 retvalue =AFPERR_PARAM;
1332 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1338 setvoltime(obj, d_vol );
1341 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1345 /* ----------------------- */
1346 static int copy_all(const int dfd, const void *buf,
1352 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1355 while (buflen > 0) {
1356 if ((cc = write(dfd, buf, buflen)) < 0) {
1368 LOG(log_debug9, logtype_afpd, "end copy_all:");
1374 /* --------------------------
1375 * copy only the fork data stream
1377 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1384 if (eid == ADEID_DFORK) {
1385 sfd = ad_data_fileno(ads);
1386 dfd = ad_data_fileno(add);
1389 sfd = ad_reso_fileno(ads);
1390 dfd = ad_reso_fileno(add);
1393 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1396 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1399 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1400 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1404 #define BUF 128*1024*1024
1406 if (fstat(sfd, &st) == 0) {
1409 if ( offset >= st.st_size) {
1412 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1413 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1416 case EINVAL: /* there's no guarantee that all fs support sendfile */
1425 lseek(sfd, offset, SEEK_SET);
1429 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1436 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1443 /* ----------------------------------
1444 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1445 * because we are doing it elsewhere.
1446 * currently if newname is NULL then adp is NULL.
1448 int copyfile(const struct vol *s_vol,
1449 const struct vol *d_vol,
1454 struct adouble *adp)
1456 struct adouble ads, add;
1463 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1464 sfd, src, dst, newname);
1467 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1471 adflags = ADFLAGS_DF;
1473 adflags |= ADFLAGS_HF;
1476 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0) {
1481 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1482 /* no resource fork, don't create one for dst file */
1483 adflags &= ~ADFLAGS_HF;
1486 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1488 if (stat_result < 0) {
1489 /* unlikely but if fstat fails, the default file mode will be 0666. */
1490 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1493 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1494 if (ad_open(&add, dst, adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, O_RDWR|O_CREAT|O_EXCL, st.st_mode) < 0) {
1496 ad_close( adp, adflags );
1497 if (EEXIST != ret_err) {
1498 deletefile(d_vol, -1, dst, 0);
1501 return AFPERR_EXIST;
1505 * XXX if the source and the dest don't use the same resource type it's broken
1507 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1508 /* copy the data fork */
1509 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1510 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1518 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1519 /* set the new name in the resource fork */
1520 ad_copy_header(&add, adp);
1521 ad_setname(&add, newname);
1524 ad_close( adp, adflags );
1526 if (ad_close( &add, adflags ) <0) {
1531 deletefile(d_vol, -1, dst, 0);
1533 else if (stat_result == 0) {
1534 /* set dest modification date to src date */
1537 ut.actime = ut.modtime = st.st_mtime;
1539 /* FIXME netatalk doesn't use resource fork file date
1540 * but maybe we should set its modtime too.
1545 switch ( ret_err ) {
1551 return AFPERR_DFULL;
1553 return AFPERR_NOOBJ;
1555 return AFPERR_ACCESS;
1557 return AFPERR_VLOCK;
1559 return AFPERR_PARAM;
1563 /* -----------------------------------
1564 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1565 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1567 when deletefile is called we don't have lock on it, file is closed (for us)
1568 untrue if called by renamefile
1570 ad_open always try to open file RDWR first and ad_lock takes care of
1571 WRITE lock on read only file.
1574 static int check_attrib(struct adouble *adp)
1576 u_int16_t bshort = 0;
1578 ad_getattr(adp, &bshort);
1580 * Does kFPDeleteInhibitBit (bit 8) set?
1582 if ((bshort & htons(ATTRBIT_NODELETE))) {
1583 return AFPERR_OLOCK;
1585 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1591 * dirfd can be used for unlinkat semantics
1593 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1596 struct adouble *adp = NULL;
1597 int adflags, err = AFP_OK;
1600 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1602 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1604 /* was EACCESS error try to get only metadata */
1605 /* we never want to create a resource fork here, we are going to delete it
1606 * moreover sometimes deletefile is called with a no existent file and
1607 * ad_open would create a 0 byte resource fork
1609 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1610 if ((err = check_attrib(&ad))) {
1611 ad_close_metadata(&ad);
1618 /* try to open both forks at once */
1619 adflags = ADFLAGS_DF;
1620 if ( ad_openat(&ad, dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0 ) {
1625 case EACCES: /* maybe it's a file with no write mode for us */
1626 break; /* was return AFPERR_ACCESS;*/
1639 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1640 adflags |= ADFLAGS_HF;
1641 /* FIXME we have a pb here because we want to know if a file is open
1642 * there's a 'priority inversion' if you can't open the ressource fork RW
1643 * you can delete it if it's open because you can't get a write lock.
1645 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1648 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1650 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1656 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1658 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1660 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1661 cnid_delete(vol->v_cdb, id);
1667 ad_close_metadata(&ad);
1670 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1675 /* ------------------------------------ */
1676 /* return a file id */
1677 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1686 struct path *s_path;
1692 memcpy(&vid, ibuf, sizeof(vid));
1693 ibuf += sizeof(vid);
1695 if (NULL == ( vol = getvolbyvid( vid )) ) {
1696 return( AFPERR_PARAM);
1699 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1703 if (vol->v_flags & AFPVOL_RO)
1704 return AFPERR_VLOCK;
1706 memcpy(&did, ibuf, sizeof( did ));
1707 ibuf += sizeof(did);
1709 if (NULL == ( dir = dirlookup( vol, did )) ) {
1710 return afp_errno; /* was AFPERR_PARAM */
1713 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1714 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1717 if ( path_isadir(s_path) ) {
1718 return( AFPERR_BADTYPE );
1721 upath = s_path->u_name;
1722 switch (s_path->st_errno) {
1724 break; /* success */
1727 return AFPERR_ACCESS;
1729 return AFPERR_NOOBJ;
1731 return AFPERR_PARAM;
1734 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1735 memcpy(rbuf, &id, sizeof(id));
1736 *rbuflen = sizeof(id);
1737 return AFPERR_EXISTID;
1740 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1741 memcpy(rbuf, &id, sizeof(id));
1742 *rbuflen = sizeof(id);
1749 /* ------------------------------- */
1755 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1758 struct reenum *param = data;
1759 struct vol *vol = param->vol;
1760 cnid_t did = param->did;
1763 if ( lstat(de->d_name, &path.st) < 0 )
1766 /* update or add to cnid */
1767 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1772 /* --------------------
1773 * Ok the db is out of synch with the dir.
1774 * but if it's a deleted file we don't want to do it again and again.
1777 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1783 if (vol->v_cdb == NULL) {
1787 /* FIXME use of_statdir ? */
1788 if (lstat(name, &st)) {
1792 if (dirreenumerate(dir, &st)) {
1793 /* we already did it once and the dir haven't been modified */
1794 return dir->d_offcnt;
1798 data.did = dir->d_did;
1799 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1800 setdiroffcnt(curdir, &st, ret);
1801 dir->d_flags |= DIRF_CNID;
1807 /* ------------------------------
1808 resolve a file id */
1809 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1818 u_int16_t vid, bitmap;
1820 static char buffer[12 + MAXPATHLEN + 1];
1821 int len = 12 + MAXPATHLEN + 1;
1826 memcpy(&vid, ibuf, sizeof(vid));
1827 ibuf += sizeof(vid);
1829 if (NULL == ( vol = getvolbyvid( vid )) ) {
1830 return( AFPERR_PARAM);
1833 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1837 memcpy(&id, ibuf, sizeof( id ));
1842 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1846 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1847 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1850 if (NULL == ( dir = dirlookup( vol, id )) ) {
1851 return AFPERR_NOID; /* idem AFPERR_PARAM */
1853 if (movecwd(vol, dir) < 0) {
1857 return AFPERR_ACCESS;
1861 return AFPERR_PARAM;
1865 memset(&path, 0, sizeof(path));
1866 path.u_name = upath;
1867 if ( of_stat(&path) < 0 ) {
1869 /* with nfs and our working directory is deleted */
1870 if (errno == ESTALE) {
1874 if ( errno == ENOENT && !retry) {
1875 /* cnid db is out of sync, reenumerate the directory and update ids */
1876 reenumerate_id(vol, ".", dir);
1884 return AFPERR_ACCESS;
1888 return AFPERR_PARAM;
1892 /* directories are bad */
1893 if (S_ISDIR(path.st.st_mode)) {
1894 /* OS9 and OSX don't return the same error code */
1895 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1898 memcpy(&bitmap, ibuf, sizeof(bitmap));
1899 bitmap = ntohs( bitmap );
1900 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1904 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1905 rbuf + sizeof(bitmap), &buflen))) {
1908 *rbuflen = buflen + sizeof(bitmap);
1909 memcpy(rbuf, ibuf, sizeof(bitmap));
1914 /* ------------------------------ */
1915 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1925 static char buffer[12 + MAXPATHLEN + 1];
1926 int len = 12 + MAXPATHLEN + 1;
1931 memcpy(&vid, ibuf, sizeof(vid));
1932 ibuf += sizeof(vid);
1934 if (NULL == ( vol = getvolbyvid( vid )) ) {
1935 return( AFPERR_PARAM);
1938 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1942 if (vol->v_flags & AFPVOL_RO)
1943 return AFPERR_VLOCK;
1945 memcpy(&id, ibuf, sizeof( id ));
1949 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1953 if (NULL == ( dir = dirlookup( vol, id )) ) {
1954 if (afp_errno == AFPERR_NOOBJ) {
1958 return( AFPERR_PARAM );
1962 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1966 return AFPERR_ACCESS;
1971 /* still try to delete the id */
1975 return AFPERR_PARAM;
1978 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1979 return AFPERR_BADTYPE;
1982 if (cnid_delete(vol->v_cdb, fileid)) {
1985 return AFPERR_VLOCK;
1988 return AFPERR_ACCESS;
1990 return AFPERR_PARAM;
1997 /* ------------------------------ */
1998 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
2002 if (path->st_errno) {
2003 switch (path->st_errno) {
2005 afp_errno = AFPERR_NOID;
2009 afp_errno = AFPERR_ACCESS;
2012 afp_errno = AFPERR_PARAM;
2017 /* we use file_access both for legacy Mac perm and
2018 * for unix privilege, rename will take care of folder perms
2020 if (file_access(path, OPENACC_WR ) < 0) {
2021 afp_errno = AFPERR_ACCESS;
2025 if ((*of = of_findname(path))) {
2026 /* reuse struct adouble so it won't break locks */
2030 ret = ad_open(adp, path->u_name, ADFLAGS_HF, O_RDONLY);
2032 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2034 * The user must have the Read & Write privilege for both files in order to use this command.
2036 ad_close(adp, ADFLAGS_HF);
2037 afp_errno = AFPERR_ACCESS;
2044 #define APPLETEMP ".AppleTempXXXXXX"
2046 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2048 struct stat srcst, destst;
2050 struct dir *dir, *sdir;
2051 char *spath, temp[17], *p;
2052 char *supath, *upath;
2057 struct adouble *adsp = NULL;
2058 struct adouble *addp = NULL;
2059 struct ofork *s_of = NULL;
2060 struct ofork *d_of = NULL;
2073 memcpy(&vid, ibuf, sizeof(vid));
2074 ibuf += sizeof(vid);
2076 if (NULL == ( vol = getvolbyvid( vid )) ) {
2077 return( AFPERR_PARAM);
2080 if ((vol->v_flags & AFPVOL_RO))
2081 return AFPERR_VLOCK;
2083 /* source and destination dids */
2084 memcpy(&sid, ibuf, sizeof(sid));
2085 ibuf += sizeof(sid);
2086 memcpy(&did, ibuf, sizeof(did));
2087 ibuf += sizeof(did);
2090 if (NULL == (dir = dirlookup( vol, sid )) ) {
2091 return afp_errno; /* was AFPERR_PARAM */
2094 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2095 return get_afp_errno(AFPERR_NOOBJ);
2098 if ( path_isadir(path) ) {
2099 return AFPERR_BADTYPE; /* it's a dir */
2102 /* save some stuff */
2105 spath = obj->oldtmp;
2106 supath = obj->newtmp;
2107 strcpy(spath, path->m_name);
2108 strcpy(supath, path->u_name); /* this is for the cnid changing */
2109 p = absupath( vol, sdir, supath);
2111 /* pathname too long */
2112 return AFPERR_PARAM ;
2115 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2116 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2120 /* ***** from here we may have resource fork open **** */
2122 /* look for the source cnid. if it doesn't exist, don't worry about
2124 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2126 if (NULL == ( dir = dirlookup( vol, did )) ) {
2127 err = afp_errno; /* was AFPERR_PARAM */
2128 goto err_exchangefile;
2131 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2132 err = get_afp_errno(AFPERR_NOOBJ);
2133 goto err_exchangefile;
2136 if ( path_isadir(path) ) {
2137 err = AFPERR_BADTYPE;
2138 goto err_exchangefile;
2141 /* FPExchangeFiles is the only call that can return the SameObj
2143 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2144 err = AFPERR_SAMEOBJ;
2145 goto err_exchangefile;
2148 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2149 if (!(addp = find_adouble( path, &d_of, &add))) {
2151 goto err_exchangefile;
2155 /* they are not on the same device and at least one is open
2156 * FIXME broken for for crossdev and adouble v2
2159 crossdev = (srcst.st_dev != destst.st_dev);
2160 if (/* (d_of || s_of) && */ crossdev) {
2162 goto err_exchangefile;
2165 /* look for destination id. */
2166 upath = path->u_name;
2167 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2169 /* construct a temp name.
2170 * NOTE: the temp file will be in the dest file's directory. it
2171 * will also be inaccessible from AFP. */
2172 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2173 if (!mktemp(temp)) {
2175 goto err_exchangefile;
2179 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2180 ad_close(adsp, ADFLAGS_HF);
2181 ad_close(addp, ADFLAGS_HF);
2184 /* now, quickly rename the file. we error if we can't. */
2185 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2186 goto err_exchangefile;
2187 of_rename(vol, s_of, sdir, spath, curdir, temp);
2189 /* rename destination to source */
2190 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2191 goto err_src_to_tmp;
2192 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2194 /* rename temp to destination */
2195 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2196 goto err_dest_to_src;
2197 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2199 /* id's need switching. src -> dest and dest -> src.
2200 * we need to re-stat() if it was a cross device copy.
2203 cnid_delete(vol->v_cdb, sid);
2205 cnid_delete(vol->v_cdb, did);
2207 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2208 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2210 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2211 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2216 err = AFPERR_ACCESS;
2221 goto err_temp_to_dest;
2224 /* here we need to reopen if crossdev */
2225 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2230 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2235 /* change perms, src gets dest perm and vice versa */
2240 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2241 err = AFP_OK; /* ignore error */
2242 goto err_temp_to_dest;
2246 * we need to exchange ACL entries as well
2248 /* exchange_acls(vol, p, upath); */
2253 path->m_name = NULL;
2254 path->u_name = upath;
2256 setfilunixmode(vol, path, destst.st_mode);
2257 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2264 setfilunixmode(vol, path, srcst.st_mode);
2265 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2267 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2268 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2273 goto err_exchangefile;
2275 /* all this stuff is so that we can unwind a failed operation
2278 /* rename dest to temp */
2279 renamefile(vol, -1, upath, temp, temp, adsp);
2280 of_rename(vol, s_of, curdir, upath, curdir, temp);
2283 /* rename source back to dest */
2284 renamefile(vol, -1, p, upath, path->m_name, addp);
2285 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2288 /* rename temp back to source */
2289 renamefile(vol, -1, temp, p, spath, adsp);
2290 of_rename(vol, s_of, curdir, temp, sdir, spath);
2293 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2294 ad_close(adsp, ADFLAGS_HF);
2296 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2297 ad_close(addp, ADFLAGS_HF);
2301 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2302 (void)dir_remove(vol, cached);
2303 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2304 (void)dir_remove(vol, cached);