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>
25 #include <atalk/globals.h>
26 #include <atalk/fce_api.h>
28 #include "directory.h"
37 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
38 * field bytes subfield bytes
41 * ioFlFndrInfo 16 -> type 4 type field
42 * creator 4 creator field
43 * flags 2 finder flags:
45 * location 4 location in window
46 * folder 2 window that contains file
48 * ioFlXFndrInfo 16 -> iconID 2 icon id
50 * script 1 script system
52 * commentID 2 comment id
53 * putawayID 4 home directory id
56 const u_char ufinderi[ADEDLEN_FINDERI] = {
57 0, 0, 0, 0, 0, 0, 0, 0,
58 1, 0, 0, 0, 0, 0, 0, 0,
59 0, 0, 0, 0, 0, 0, 0, 0,
60 0, 0, 0, 0, 0, 0, 0, 0
63 static const u_char old_ufinderi[] = {
64 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
67 /* ----------------------
69 static int default_type(void *finder)
71 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
76 /* FIXME path : unix or mac name ? (for now it's unix name ) */
77 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
80 void *ad_finder = NULL;
84 ad_finder = ad_entry(adp, ADEID_FINDERI);
87 memcpy(data, ad_finder, ADEDLEN_FINDERI);
89 if (default_type(ad_finder))
93 memcpy(data, ufinderi, ADEDLEN_FINDERI);
95 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
98 ashort = htons(FINDERINFO_INVISIBLE);
99 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
105 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
106 linkflag |= htons(FINDERINFO_ISALIAS);
107 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
108 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
109 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
113 /** Only enter if no appledouble information and no finder information found. */
114 if (chk_ext && (em = getextmap( upath ))) {
115 memcpy(data, em->em_type, sizeof( em->em_type ));
116 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
121 /* ---------------------
123 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, uint32_t utf8)
128 aint = strlen( name );
132 if (utf8_encoding()) {
133 /* but name is an utf8 mac name */
136 /* global static variable... */
138 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
147 if (aint > MACFILELEN)
154 if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
155 aint = UTF8FILELEN_EARLY;
157 utf8 = vol->v_kTextEncoding;
158 memcpy(data, &utf8, sizeof(utf8));
159 data += sizeof(utf8);
162 memcpy(data, &temp, sizeof(temp));
163 data += sizeof(temp);
166 memcpy( data, src, aint );
176 * FIXME: PDINFO is UTF8 and doesn't need adp
178 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
179 (1 << FILPBIT_CDATE) |\
180 (1 << FILPBIT_MDATE) |\
181 (1 << FILPBIT_BDATE) |\
182 (1 << FILPBIT_FINFO) |\
183 (1 << FILPBIT_RFLEN) |\
184 (1 << FILPBIT_EXTRFLEN) |\
185 (1 << FILPBIT_PDINFO) |\
186 (1 << FILPBIT_FNUM) |\
187 (1 << FILPBIT_UNIXPR)))
190 * @brief Get CNID for did/upath args both from database and adouble file
192 * 1. Get the objects CNID as stored in its adouble file
193 * 2. Get the objects CNID from the database
194 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
195 * 4. In case 2 and 3 differ, store 3 in the adouble file
197 * @param vol (rw) volume
198 * @param adp (rw) adouble struct of object upath, might be NULL
199 * @param st (r) stat of upath, must NOT be NULL
200 * @param did (r) parent CNID of upath
201 * @param upath (r) name of object
202 * @param len (r) strlen of upath
204 uint32_t get_id(struct vol *vol,
206 const struct stat *st,
211 static int first = 1; /* mark if this func is called the first time */
213 uint32_t dbcnid = CNID_INVALID;
216 if (vol->v_cdb != NULL) {
217 /* prime aint with what we think is the cnid, set did to zero for
218 catching moved files */
219 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
221 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
222 /* Throw errors if cnid_add fails. */
223 if (dbcnid == CNID_INVALID) {
225 case CNID_ERR_CLOSE: /* the db is closed */
228 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
229 afp_errno = AFPERR_PARAM;
232 afp_errno = AFPERR_PARAM;
235 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
236 /* we have to do it here for "dbd" because it uses "lazy opening" */
237 /* In order to not end in a loop somehow with goto restart below */
239 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
240 cnid_close(vol->v_cdb);
241 free(vol->v_cnidscheme);
242 vol->v_cnidscheme = strdup("tdb");
244 int flags = CNID_FLAG_MEMORY;
245 if ((vol->v_flags & AFPVOL_NODEV)) {
246 flags |= CNID_FLAG_NODEV;
248 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
250 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
252 /* deactivate cnid caching/storing in AppleDouble files and set ro mode*/
253 vol->v_flags &= ~AFPVOL_CACHE;
254 vol->v_flags |= AFPVOL_RO;
256 /* kill ourself with SIGUSR2 aka msg pending */
257 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
258 "Check server messages for details. Switching to read-only mode.");
259 kill(getpid(), SIGUSR2);
261 goto restart; /* not try again with the temp CNID db */
264 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
265 "Check server messages for details, can't recover from this state!");
269 afp_errno = AFPERR_MISC;
273 else if (adp && (adcnid != dbcnid)) { /* 4 */
274 /* Update the ressource fork. For a folder adp is always null */
275 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
276 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
277 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
288 /* -------------------------- */
289 int getmetadata(struct vol *vol,
291 struct path *path, struct dir *dir,
292 char *buf, size_t *buflen, struct adouble *adp)
294 char *data, *l_nameoff = NULL, *upath;
295 char *utf_nameoff = NULL;
300 u_char achar, fdType[4];
305 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
307 upath = path->u_name;
311 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
312 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
313 || (bitmap & (1 << FILPBIT_FNUM))) {
316 struct dir *cachedfile;
317 int len = strlen(upath);
318 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
319 id = cachedfile->d_did;
321 id = get_id(vol, adp, st, dir->d_did, upath, len);
323 /* Add it to the cache */
324 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
325 ntohl(dir->d_did), upath, ntohl(id));
327 /* Get macname from unixname first */
328 if (path->m_name == NULL) {
329 if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
330 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
336 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
337 || (bconchar(fullpath, '/') != BSTR_OK)
338 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
339 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
343 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
344 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
348 if ((dircache_add(vol, cachedfile)) != 0) {
349 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
357 if (id == CNID_INVALID)
361 path->m_name = utompath(vol, upath, id, utf8_encoding());
364 while ( bitmap != 0 ) {
365 while (( bitmap & 1 ) == 0 ) {
373 ad_getattr(adp, &ashort);
374 } else if (vol_inv_dots(vol) && *upath == '.') {
375 ashort = htons(ATTRBIT_INVISIBLE);
379 /* FIXME do we want a visual clue if the file is read only
382 accessmode(vol, ".", &ma, dir , NULL);
383 if ((ma.ma_user & AR_UWRITE)) {
384 accessmode(vol, upath, &ma, dir , st);
385 if (!(ma.ma_user & AR_UWRITE)) {
386 ashort |= htons(ATTRBIT_NOWRITE);
390 memcpy(data, &ashort, sizeof( ashort ));
391 data += sizeof( ashort );
392 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
393 path->u_name, ntohs(ashort));
397 memcpy(data, &dir->d_did, sizeof( uint32_t ));
398 data += sizeof( uint32_t );
399 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
400 path->u_name, ntohl(dir->d_did));
404 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
405 aint = AD_DATE_FROM_UNIX(st->st_mtime);
406 memcpy(data, &aint, sizeof( aint ));
407 data += sizeof( aint );
411 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
412 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
413 aint = AD_DATE_FROM_UNIX(st->st_mtime);
416 aint = AD_DATE_FROM_UNIX(st->st_mtime);
418 memcpy(data, &aint, sizeof( int ));
419 data += sizeof( int );
423 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
424 aint = AD_DATE_START;
425 memcpy(data, &aint, sizeof( int ));
426 data += sizeof( int );
430 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
431 data += ADEDLEN_FINDERI;
436 data += sizeof( uint16_t );
440 memset(data, 0, sizeof(uint16_t));
441 data += sizeof( uint16_t );
445 memcpy(data, &id, sizeof( id ));
446 data += sizeof( id );
447 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
448 path->u_name, ntohl(id));
452 if (st->st_size > 0xffffffff)
455 aint = htonl( st->st_size );
456 memcpy(data, &aint, sizeof( aint ));
457 data += sizeof( aint );
462 if (adp->ad_rlen > 0xffffffff)
465 aint = htonl( adp->ad_rlen);
469 memcpy(data, &aint, sizeof( aint ));
470 data += sizeof( aint );
473 /* Current client needs ProDOS info block for this file.
474 Use simple heuristic and let the Mac "type" string tell
475 us what the PD file code should be. Everything gets a
476 subtype of 0x0000 unless the original value was hashed
477 to "pXYZ" when we created it. See IA, Ver 2.
478 <shirsch@adelphia.net> */
479 case FILPBIT_PDINFO :
480 if (afp_version >= 30) { /* UTF8 name */
481 utf8 = kTextEncodingUTF8;
483 data += sizeof( uint16_t );
485 memcpy(data, &aint, sizeof( aint ));
486 data += sizeof( aint );
490 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
492 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
496 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
500 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
504 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
508 else if ( fdType[0] == 'p' ) {
510 ashort = (fdType[2] * 256) + fdType[3];
524 memcpy(data, &ashort, sizeof( ashort ));
525 data += sizeof( ashort );
526 memset(data, 0, sizeof( ashort ));
527 data += sizeof( ashort );
530 case FILPBIT_EXTDFLEN:
531 aint = htonl(st->st_size >> 32);
532 memcpy(data, &aint, sizeof( aint ));
533 data += sizeof( aint );
534 aint = htonl(st->st_size);
535 memcpy(data, &aint, sizeof( aint ));
536 data += sizeof( aint );
538 case FILPBIT_EXTRFLEN:
541 aint = htonl(adp->ad_rlen >> 32);
542 memcpy(data, &aint, sizeof( aint ));
543 data += sizeof( aint );
545 aint = htonl(adp->ad_rlen);
546 memcpy(data, &aint, sizeof( aint ));
547 data += sizeof( aint );
549 case FILPBIT_UNIXPR :
550 /* accessmode may change st_mode with ACLs */
551 accessmode(vol, upath, &ma, dir , st);
553 aint = htonl(st->st_uid);
554 memcpy( data, &aint, sizeof( aint ));
555 data += sizeof( aint );
556 aint = htonl(st->st_gid);
557 memcpy( data, &aint, sizeof( aint ));
558 data += sizeof( aint );
561 type == slnk indicates an OSX style symlink,
562 we have to add S_IFLNK to the mode, otherwise
563 10.3 clients freak out. */
567 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
568 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
574 memcpy( data, &aint, sizeof( aint ));
575 data += sizeof( aint );
577 *data++ = ma.ma_user;
578 *data++ = ma.ma_world;
579 *data++ = ma.ma_group;
580 *data++ = ma.ma_owner;
584 return( AFPERR_BITMAP );
590 ashort = htons( data - buf );
591 memcpy(l_nameoff, &ashort, sizeof( ashort ));
592 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
595 ashort = htons( data - buf );
596 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
597 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
599 *buflen = data - buf;
603 /* ----------------------- */
604 int getfilparams(struct vol *vol,
606 struct path *path, struct dir *dir,
607 char *buf, size_t *buflen )
609 struct adouble ad, *adp;
613 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
615 opened = PARAM_NEED_ADP(bitmap);
620 int flags = (bitmap & (1 << FILPBIT_ATTR)) ? ADFLAGS_CHECK_OF : 0;
622 adp = of_ad(vol, path, &ad);
623 upath = path->u_name;
625 if ( ad_metadata( upath, flags, adp) < 0 ) {
628 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
629 upath, strerror(errno));
630 return AFPERR_ACCESS;
632 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
641 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
642 ad_close(adp, ADFLAGS_HF);
647 /* ----------------------------- */
648 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
653 struct ofork *of = NULL;
655 int creatf, did, openf, retvalue = AFP_OK;
661 creatf = (unsigned char) *ibuf++;
663 memcpy(&vid, ibuf, sizeof( vid ));
664 ibuf += sizeof( vid );
666 if (NULL == ( vol = getvolbyvid( vid )) )
667 return( AFPERR_PARAM );
669 if (vol->v_flags & AFPVOL_RO)
672 memcpy(&did, ibuf, sizeof( did));
673 ibuf += sizeof( did );
675 if (NULL == ( dir = dirlookup( vol, did )) )
678 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
679 return get_afp_errno(AFPERR_PARAM);
681 if ( *s_path->m_name == '\0' )
682 return( AFPERR_BADTYPE );
684 upath = s_path->u_name;
687 /* if upath is deleted we already in trouble anyway */
688 if ((of = of_findname(s_path))) {
696 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
698 /* on a soft create, if the file is open then ad_open won't fail
699 because open syscall is not called */
700 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
702 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | 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 /* FIXME with hard create on an existing file, we already
721 * corrupted the data file.
723 netatalk_unlink( upath );
724 ad_close( &ad, ADFLAGS_DF );
725 return AFPERR_ACCESS;
728 path = s_path->m_name;
729 ad_setname(&ad, path);
732 if (lstat(upath, &st) != 0) {
733 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
734 upath, strerror(errno));
735 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
739 (void)get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath));
742 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
743 fce_register_new_file(s_path);
748 setvoltime(obj, vol );
753 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
759 uint16_t vid, bitmap;
764 memcpy(&vid, ibuf, sizeof( vid ));
765 ibuf += sizeof( vid );
766 if (NULL == ( vol = getvolbyvid( vid )) ) {
767 return( AFPERR_PARAM );
770 if (vol->v_flags & AFPVOL_RO)
773 memcpy(&did, ibuf, sizeof( did ));
774 ibuf += sizeof( did );
775 if (NULL == ( dir = dirlookup( vol, did )) ) {
776 return afp_errno; /* was AFPERR_NOOBJ */
779 memcpy(&bitmap, ibuf, sizeof( bitmap ));
780 bitmap = ntohs( bitmap );
781 ibuf += sizeof( bitmap );
783 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
784 return get_afp_errno(AFPERR_PARAM);
787 if (path_isadir(s_path)) {
788 return( AFPERR_BADTYPE ); /* it's a directory */
791 if ( s_path->st_errno != 0 ) {
792 return( AFPERR_NOOBJ );
795 if ((u_long)ibuf & 1 ) {
799 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
800 setvoltime(obj, vol );
807 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
810 extern struct path Cur_Path;
812 int setfilparams(struct vol *vol,
813 struct path *path, uint16_t f_bitmap, char *buf )
815 struct adouble ad, *adp;
817 int bit, isad = 1, err = AFP_OK;
819 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
820 uint16_t ashort, bshort, oshort;
823 uint16_t upriv_bit = 0;
825 int change_mdate = 0;
826 int change_parent_mdate = 0;
831 uint16_t bitmap = f_bitmap;
832 uint32_t cdate,bdate;
833 u_char finder_buf[32];
837 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
840 adp = of_ad(vol, path, &ad);
841 upath = path->u_name;
843 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
844 return AFPERR_ACCESS;
847 /* with unix priv maybe we have to change adouble file priv first */
849 while ( bitmap != 0 ) {
850 while (( bitmap & 1 ) == 0 ) {
857 memcpy(&ashort, buf, sizeof( ashort ));
858 buf += sizeof( ashort );
862 memcpy(&cdate, buf, sizeof(cdate));
863 buf += sizeof( cdate );
866 memcpy(&newdate, buf, sizeof( newdate ));
867 buf += sizeof( newdate );
871 memcpy(&bdate, buf, sizeof( bdate));
872 buf += sizeof( bdate );
876 memcpy(finder_buf, buf, 32 );
877 if (memcmp(buf, "slnkrhap", 8) == 0 && !S_ISLNK(path->st.st_mode)) {
881 char buf[PATH_MAX+1];
882 if ((fp = open(path->u_name, O_RDONLY)) >= 0) {
883 if ((len = read(fp, buf, PATH_MAX+1))) {
884 if (unlink(path->u_name) == 0) {
886 erc = symlink(buf, path->u_name);
895 goto setfilparam_done;
901 case FILPBIT_UNIXPR :
902 if (!vol_unix_priv(vol)) {
903 /* this volume doesn't use unix priv */
909 change_parent_mdate = 1;
911 memcpy( &aint, buf, sizeof( aint ));
912 f_uid = ntohl (aint);
913 buf += sizeof( aint );
914 memcpy( &aint, buf, sizeof( aint ));
915 f_gid = ntohl (aint);
916 buf += sizeof( aint );
917 setfilowner(vol, f_uid, f_gid, path);
919 memcpy( &upriv, buf, sizeof( upriv ));
920 buf += sizeof( upriv );
921 upriv = ntohl (upriv);
922 if ((upriv & S_IWUSR)) {
923 setfilunixmode(vol, path, upriv);
930 case FILPBIT_PDINFO :
931 if (afp_version < 30) { /* else it's UTF8 name */
934 /* Keep special case to support crlf translations */
935 if ((unsigned int) achar == 0x04) {
936 fdType = (u_char *)"TEXT";
939 xyy[0] = ( u_char ) 'p';
950 /* break while loop */
959 /* second try with adouble open
961 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
962 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
964 * For some things, we don't need an adouble header:
965 * - change of modification date
966 * - UNIX privs (Bug-ID #2863424)
968 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
969 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
970 return AFPERR_ACCESS;
972 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
974 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
975 ad_setname(adp, path->m_name);
980 while ( bitmap != 0 ) {
981 while (( bitmap & 1 ) == 0 ) {
988 ad_getattr(adp, &bshort);
990 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
991 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
995 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
996 change_parent_mdate = 1;
997 ad_setattr(adp, bshort);
1000 ad_setdate(adp, AD_DATE_CREATE, cdate);
1002 case FILPBIT_MDATE :
1004 case FILPBIT_BDATE :
1005 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1007 case FILPBIT_FINFO :
1008 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1010 ((em = getextmap( path->m_name )) &&
1011 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1012 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1013 || ((em = getdefextmap()) &&
1014 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1015 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1017 memcpy(finder_buf, ufinderi, 8 );
1019 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1021 case FILPBIT_UNIXPR :
1023 setfilunixmode(vol, path, upriv);
1026 case FILPBIT_PDINFO :
1027 if (afp_version < 30) { /* else it's UTF8 name */
1028 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1029 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1034 err = AFPERR_BITMAP;
1035 goto setfilparam_done;
1042 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1043 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1047 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1048 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1054 ad_close(adp, ADFLAGS_HF);
1057 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1058 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1059 bitmap = 1<<FILPBIT_MDATE;
1060 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1064 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1070 * renamefile and copyfile take the old and new unix pathnames
1071 * and the new mac name.
1073 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1074 * passing -1 means this is not used, src path is a full path
1075 * src the source path
1076 * dst the dest filename in current dir
1077 * newname the dest mac name
1078 * adp adouble struct of src file, if open, or & zeroed one
1081 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1085 LOG(log_debug, logtype_afpd,
1086 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1088 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1091 return( AFPERR_NOOBJ );
1094 return( AFPERR_ACCESS );
1096 return AFPERR_VLOCK;
1097 case EXDEV : /* Cross device move -- try copy */
1098 /* NOTE: with open file it's an error because after the copy we will
1099 * get two files, it's fixable for our process (eg reopen the new file, get the
1100 * locks, and so on. But it doesn't solve the case with a second process
1102 if (adp->ad_open_forks) {
1103 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1104 return AFPERR_OLOCK; /* little lie */
1106 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1107 /* on error copyfile delete dest */
1110 return deletefile(vol, sdir_fd, src, 0);
1112 return( AFPERR_PARAM );
1116 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1120 /* try to undo the data fork rename,
1121 * we know we are on the same device
1124 unix_rename(-1, dst, sdir_fd, src );
1125 /* return the first error */
1128 return AFPERR_NOOBJ;
1131 return AFPERR_ACCESS ;
1133 return AFPERR_VLOCK;
1135 return AFPERR_PARAM ;
1140 /* don't care if we can't open the newly renamed ressource fork */
1141 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1142 ad_setname(adp, newname);
1144 ad_close( adp, ADFLAGS_HF );
1151 convert a Mac long name to an utf8 name,
1153 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1157 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1163 /* ---------------- */
1164 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1171 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1177 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1178 if (afp_version >= 30) {
1179 /* convert it to UTF8
1181 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1185 strncpy( newname, ibuf, plen );
1186 newname[ plen ] = '\0';
1188 if (strlen(newname) != plen) {
1189 /* there's \0 in newname, e.g. it's a pathname not
1197 memcpy(&hint, ibuf, sizeof(hint));
1198 ibuf += sizeof(hint);
1200 memcpy(&len16, ibuf, sizeof(len16));
1201 ibuf += sizeof(len16);
1202 plen = ntohs(len16);
1205 if (plen > AFPOBJ_TMPSIZ) {
1208 strncpy( newname, ibuf, plen );
1209 newname[ plen ] = '\0';
1210 if (strlen(newname) != plen) {
1219 /* -----------------------------------
1221 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1223 struct vol *s_vol, *d_vol;
1225 char *newname, *p, *upath;
1226 struct path *s_path;
1227 uint32_t sdid, ddid;
1228 int err, retvalue = AFP_OK;
1229 uint16_t svid, dvid;
1231 struct adouble ad, *adp;
1237 memcpy(&svid, ibuf, sizeof( svid ));
1238 ibuf += sizeof( svid );
1239 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1240 return( AFPERR_PARAM );
1243 memcpy(&sdid, ibuf, sizeof( sdid ));
1244 ibuf += sizeof( sdid );
1245 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1249 memcpy(&dvid, ibuf, sizeof( dvid ));
1250 ibuf += sizeof( dvid );
1251 memcpy(&ddid, ibuf, sizeof( ddid ));
1252 ibuf += sizeof( ddid );
1254 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1255 return get_afp_errno(AFPERR_PARAM);
1257 if ( path_isadir(s_path) ) {
1258 return( AFPERR_BADTYPE );
1261 /* don't allow copies when the file is open.
1262 * XXX: the spec only calls for read/deny write access.
1263 * however, copyfile doesn't have any of that info,
1264 * and locks need to stay coherent. as a result,
1265 * we just balk if the file is opened already. */
1267 adp = of_ad(s_vol, s_path, &ad);
1269 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1270 return AFPERR_DENYCONF;
1272 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1273 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1276 retvalue = AFPERR_DENYCONF;
1280 newname = obj->newtmp;
1281 strcpy( newname, s_path->m_name );
1283 p = ctoupath( s_vol, curdir, newname );
1285 retvalue = AFPERR_PARAM;
1289 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1290 retvalue = AFPERR_PARAM;
1294 if (d_vol->v_flags & AFPVOL_RO) {
1295 retvalue = AFPERR_VLOCK;
1299 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1300 retvalue = afp_errno;
1304 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1305 retvalue = get_afp_errno(AFPERR_NOOBJ);
1309 if ( *s_path->m_name != '\0' ) {
1310 retvalue =path_error(s_path, AFPERR_NOOBJ);
1314 /* one of the handful of places that knows about the path type */
1315 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1316 retvalue = AFPERR_PARAM;
1319 /* newname is always only a filename so curdir *is* its
1322 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1323 retvalue =AFPERR_PARAM;
1327 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1333 setvoltime(obj, d_vol );
1336 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1340 /* ----------------------- */
1341 static int copy_all(const int dfd, const void *buf,
1347 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1350 while (buflen > 0) {
1351 if ((cc = write(dfd, buf, buflen)) < 0) {
1363 LOG(log_debug9, logtype_afpd, "end copy_all:");
1369 /* --------------------------
1370 * copy only the fork data stream
1372 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1380 if (eid == ADEID_DFORK) {
1381 sfd = ad_data_fileno(ads);
1382 dfd = ad_data_fileno(add);
1385 sfd = ad_reso_fileno(ads);
1386 dfd = ad_reso_fileno(add);
1389 if (add->ad_version == AD_VERSION2)
1390 soff = doff = ad_getentryoff(ads, eid);
1392 if (eid == ADEID_DFORK)
1393 soff = doff = ad_getentryoff(ads, eid);
1398 soff = doff = ADEDOFF_RFORK_OSX;
1403 if ((off_t)-1 == lseek(sfd, soff, SEEK_SET))
1406 if ((off_t)-1 == lseek(dfd, doff, SEEK_SET))
1409 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1410 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1414 #define BUF 128*1024*1024
1416 if (fstat(sfd, &st) == 0) {
1419 if ( offset >= st.st_size) {
1422 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1423 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1426 case EINVAL: /* there's no guarantee that all fs support sendfile */
1435 lseek(sfd, offset, SEEK_SET);
1439 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1446 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1453 /* ----------------------------------
1454 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1455 * because we are doing it elsewhere.
1456 * currently if newname is NULL then adp is NULL.
1458 int copyfile(const struct vol *s_vol,
1459 const struct vol *d_vol,
1464 struct adouble *adp)
1466 struct adouble ads, add;
1473 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1474 sfd, src, dst, newname);
1477 ad_init(&ads, s_vol);
1481 adflags = ADFLAGS_DF | ADFLAGS_RF | ADFLAGS_NORF;
1483 adflags |= ADFLAGS_HF;
1486 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_NOHF | ADFLAGS_RDONLY) < 0) {
1491 if (!AD_RSRC_OPEN(adp))
1492 /* no resource fork, don't create one for dst file */
1493 adflags &= ~ADFLAGS_RF;
1495 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1497 if (stat_result < 0) {
1498 /* unlikely but if fstat fails, the default file mode will be 0666. */
1499 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1502 ad_init(&add, d_vol);
1503 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode) < 0) {
1505 ad_close( adp, adflags );
1506 if (EEXIST != ret_err) {
1507 deletefile(d_vol, -1, dst, 0);
1510 return AFPERR_EXIST;
1513 if (AD_RSRC_OPEN(adp))
1514 err = copy_fork(ADEID_RFORK, &add, adp);
1517 err = copy_fork(ADEID_DFORK, &add, adp);
1519 if ((err == 0) && (ad_meta_fileno(adp) != -1))
1520 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1525 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1526 /* set the new name in the resource fork */
1527 ad_copy_header(&add, adp);
1528 ad_setname(&add, newname);
1531 ad_close( adp, adflags );
1533 if (ad_close( &add, adflags ) <0) {
1538 deletefile(d_vol, -1, dst, 0);
1540 else if (stat_result == 0) {
1541 /* set dest modification date to src date */
1544 ut.actime = ut.modtime = st.st_mtime;
1546 /* FIXME netatalk doesn't use resource fork file date
1547 * but maybe we should set its modtime too.
1552 switch ( ret_err ) {
1558 return AFPERR_DFULL;
1560 return AFPERR_NOOBJ;
1562 return AFPERR_ACCESS;
1564 return AFPERR_VLOCK;
1566 return AFPERR_PARAM;
1570 /* -----------------------------------
1571 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1572 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1574 when deletefile is called we don't have lock on it, file is closed (for us)
1575 untrue if called by renamefile
1577 ad_open always try to open file RDWR first and ad_lock takes care of
1578 WRITE lock on read only file.
1581 static int check_attrib(struct adouble *adp)
1583 uint16_t bshort = 0;
1585 ad_getattr(adp, &bshort);
1587 * Does kFPDeleteInhibitBit (bit 8) set?
1589 if ((bshort & htons(ATTRBIT_NODELETE))) {
1590 return AFPERR_OLOCK;
1592 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1598 * dirfd can be used for unlinkat semantics
1600 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1603 struct adouble *adp = NULL;
1604 int adflags, err = AFP_OK;
1607 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1611 /* was EACCESS error try to get only metadata */
1612 /* we never want to create a resource fork here, we are going to delete it
1613 * moreover sometimes deletefile is called with a no existent file and
1614 * ad_open would create a 0 byte resource fork
1616 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1617 if ((err = check_attrib(&ad))) {
1618 ad_close(&ad, ADFLAGS_HF);
1625 /* try to open both forks at once */
1626 adflags = ADFLAGS_DF;
1627 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1632 case EACCES: /* maybe it's a file with no write mode for us */
1633 break; /* was return AFPERR_ACCESS;*/
1646 if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1647 adflags |= ADFLAGS_RF;
1648 /* FIXME we have a pb here because we want to know if a file is open
1649 * there's a 'priority inversion' if you can't open the ressource fork RW
1650 * you can delete it if it's open because you can't get a write lock.
1652 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1655 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1657 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1663 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1664 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1666 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1668 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1669 cnid_delete(vol->v_cdb, id);
1675 ad_close(&ad, ADFLAGS_HF);
1678 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1683 /* ------------------------------------ */
1684 /* return a file id */
1685 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1694 struct path *s_path;
1700 memcpy(&vid, ibuf, sizeof(vid));
1701 ibuf += sizeof(vid);
1703 if (NULL == ( vol = getvolbyvid( vid )) ) {
1704 return( AFPERR_PARAM);
1707 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1711 if (vol->v_flags & AFPVOL_RO)
1712 return AFPERR_VLOCK;
1714 memcpy(&did, ibuf, sizeof( did ));
1715 ibuf += sizeof(did);
1717 if (NULL == ( dir = dirlookup( vol, did )) ) {
1718 return afp_errno; /* was AFPERR_PARAM */
1721 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1722 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1725 if ( path_isadir(s_path) ) {
1726 return( AFPERR_BADTYPE );
1729 upath = s_path->u_name;
1730 switch (s_path->st_errno) {
1732 break; /* success */
1735 return AFPERR_ACCESS;
1737 return AFPERR_NOOBJ;
1739 return AFPERR_PARAM;
1742 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1743 memcpy(rbuf, &id, sizeof(id));
1744 *rbuflen = sizeof(id);
1745 return AFPERR_EXISTID;
1748 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1749 memcpy(rbuf, &id, sizeof(id));
1750 *rbuflen = sizeof(id);
1757 /* ------------------------------- */
1763 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1766 struct reenum *param = data;
1767 struct vol *vol = param->vol;
1768 cnid_t did = param->did;
1771 if ( lstat(de->d_name, &path.st) < 0 )
1774 /* update or add to cnid */
1775 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1780 /* --------------------
1781 * Ok the db is out of synch with the dir.
1782 * but if it's a deleted file we don't want to do it again and again.
1785 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1791 if (vol->v_cdb == NULL) {
1795 /* FIXME use of_statdir ? */
1796 if (lstat(name, &st)) {
1800 if (dirreenumerate(dir, &st)) {
1801 /* we already did it once and the dir haven't been modified */
1802 return dir->d_offcnt;
1806 data.did = dir->d_did;
1807 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1808 setdiroffcnt(curdir, &st, ret);
1809 dir->d_flags |= DIRF_CNID;
1815 /* ------------------------------
1816 resolve a file id */
1817 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1826 uint16_t vid, bitmap;
1828 static char buffer[12 + MAXPATHLEN + 1];
1829 int len = 12 + MAXPATHLEN + 1;
1834 memcpy(&vid, ibuf, sizeof(vid));
1835 ibuf += sizeof(vid);
1837 if (NULL == ( vol = getvolbyvid( vid )) ) {
1838 return( AFPERR_PARAM);
1841 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1845 memcpy(&id, ibuf, sizeof( id ));
1850 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1854 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1855 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1858 if (NULL == ( dir = dirlookup( vol, id )) ) {
1859 return AFPERR_NOID; /* idem AFPERR_PARAM */
1861 if (movecwd(vol, dir) < 0) {
1865 return AFPERR_ACCESS;
1869 return AFPERR_PARAM;
1873 memset(&path, 0, sizeof(path));
1874 path.u_name = upath;
1875 if ( of_stat(&path) < 0 ) {
1877 /* with nfs and our working directory is deleted */
1878 if (errno == ESTALE) {
1882 if ( errno == ENOENT && !retry) {
1883 /* cnid db is out of sync, reenumerate the directory and update ids */
1884 reenumerate_id(vol, ".", dir);
1892 return AFPERR_ACCESS;
1896 return AFPERR_PARAM;
1900 /* directories are bad */
1901 if (S_ISDIR(path.st.st_mode)) {
1902 /* OS9 and OSX don't return the same error code */
1903 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1906 memcpy(&bitmap, ibuf, sizeof(bitmap));
1907 bitmap = ntohs( bitmap );
1908 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1912 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1913 rbuf + sizeof(bitmap), &buflen))) {
1916 *rbuflen = buflen + sizeof(bitmap);
1917 memcpy(rbuf, ibuf, sizeof(bitmap));
1922 /* ------------------------------ */
1923 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1933 static char buffer[12 + MAXPATHLEN + 1];
1934 int len = 12 + MAXPATHLEN + 1;
1939 memcpy(&vid, ibuf, sizeof(vid));
1940 ibuf += sizeof(vid);
1942 if (NULL == ( vol = getvolbyvid( vid )) ) {
1943 return( AFPERR_PARAM);
1946 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1950 if (vol->v_flags & AFPVOL_RO)
1951 return AFPERR_VLOCK;
1953 memcpy(&id, ibuf, sizeof( id ));
1957 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1961 if (NULL == ( dir = dirlookup( vol, id )) ) {
1962 if (afp_errno == AFPERR_NOOBJ) {
1966 return( AFPERR_PARAM );
1970 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1974 return AFPERR_ACCESS;
1979 /* still try to delete the id */
1983 return AFPERR_PARAM;
1986 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1987 return AFPERR_BADTYPE;
1990 if (cnid_delete(vol->v_cdb, fileid)) {
1993 return AFPERR_VLOCK;
1996 return AFPERR_ACCESS;
1998 return AFPERR_PARAM;
2005 /* ------------------------------ */
2006 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
2010 if (path->st_errno) {
2011 switch (path->st_errno) {
2013 afp_errno = AFPERR_NOID;
2017 afp_errno = AFPERR_ACCESS;
2020 afp_errno = AFPERR_PARAM;
2025 /* we use file_access both for legacy Mac perm and
2026 * for unix privilege, rename will take care of folder perms
2028 if (file_access(path, OPENACC_WR ) < 0) {
2029 afp_errno = AFPERR_ACCESS;
2033 if ((*of = of_findname(path))) {
2034 /* reuse struct adouble so it won't break locks */
2038 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
2040 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2042 * The user must have the Read & Write privilege for both files in order to use this command.
2044 ad_close(adp, ADFLAGS_HF);
2045 afp_errno = AFPERR_ACCESS;
2052 #define APPLETEMP ".AppleTempXXXXXX"
2054 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2056 struct stat srcst, destst;
2058 struct dir *dir, *sdir;
2059 char *spath, temp[17], *p;
2060 char *supath, *upath;
2065 struct adouble *adsp = NULL;
2066 struct adouble *addp = NULL;
2067 struct ofork *s_of = NULL;
2068 struct ofork *d_of = NULL;
2081 memcpy(&vid, ibuf, sizeof(vid));
2082 ibuf += sizeof(vid);
2084 if (NULL == ( vol = getvolbyvid( vid )) ) {
2085 return( AFPERR_PARAM);
2088 if ((vol->v_flags & AFPVOL_RO))
2089 return AFPERR_VLOCK;
2091 /* source and destination dids */
2092 memcpy(&sid, ibuf, sizeof(sid));
2093 ibuf += sizeof(sid);
2094 memcpy(&did, ibuf, sizeof(did));
2095 ibuf += sizeof(did);
2098 if (NULL == (dir = dirlookup( vol, sid )) ) {
2099 return afp_errno; /* was AFPERR_PARAM */
2102 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2103 return get_afp_errno(AFPERR_NOOBJ);
2106 if ( path_isadir(path) ) {
2107 return AFPERR_BADTYPE; /* it's a dir */
2110 /* save some stuff */
2113 spath = obj->oldtmp;
2114 supath = obj->newtmp;
2115 strcpy(spath, path->m_name);
2116 strcpy(supath, path->u_name); /* this is for the cnid changing */
2117 p = absupath( vol, sdir, supath);
2119 /* pathname too long */
2120 return AFPERR_PARAM ;
2124 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2128 /* ***** from here we may have resource fork open **** */
2130 /* look for the source cnid. if it doesn't exist, don't worry about
2132 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2134 if (NULL == ( dir = dirlookup( vol, did )) ) {
2135 err = afp_errno; /* was AFPERR_PARAM */
2136 goto err_exchangefile;
2139 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2140 err = get_afp_errno(AFPERR_NOOBJ);
2141 goto err_exchangefile;
2144 if ( path_isadir(path) ) {
2145 err = AFPERR_BADTYPE;
2146 goto err_exchangefile;
2149 /* FPExchangeFiles is the only call that can return the SameObj
2151 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2152 err = AFPERR_SAMEOBJ;
2153 goto err_exchangefile;
2157 if (!(addp = find_adouble( path, &d_of, &add))) {
2159 goto err_exchangefile;
2163 /* they are not on the same device and at least one is open
2164 * FIXME broken for for crossdev and adouble v2
2167 crossdev = (srcst.st_dev != destst.st_dev);
2168 if (/* (d_of || s_of) && */ crossdev) {
2170 goto err_exchangefile;
2173 /* look for destination id. */
2174 upath = path->u_name;
2175 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2177 /* construct a temp name.
2178 * NOTE: the temp file will be in the dest file's directory. it
2179 * will also be inaccessible from AFP. */
2180 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2181 if (!mktemp(temp)) {
2183 goto err_exchangefile;
2187 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2188 ad_close(adsp, ADFLAGS_HF);
2189 ad_close(addp, ADFLAGS_HF);
2192 /* now, quickly rename the file. we error if we can't. */
2193 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2194 goto err_exchangefile;
2195 of_rename(vol, s_of, sdir, spath, curdir, temp);
2197 /* rename destination to source */
2198 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2199 goto err_src_to_tmp;
2200 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2202 /* rename temp to destination */
2203 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2204 goto err_dest_to_src;
2205 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2207 /* id's need switching. src -> dest and dest -> src.
2208 * we need to re-stat() if it was a cross device copy.
2211 cnid_delete(vol->v_cdb, sid);
2213 cnid_delete(vol->v_cdb, did);
2215 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2216 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2218 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2219 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2224 err = AFPERR_ACCESS;
2229 goto err_temp_to_dest;
2232 /* here we need to reopen if crossdev */
2233 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2238 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2243 /* change perms, src gets dest perm and vice versa */
2248 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2249 err = AFP_OK; /* ignore error */
2250 goto err_temp_to_dest;
2254 * we need to exchange ACL entries as well
2256 /* exchange_acls(vol, p, upath); */
2261 path->m_name = NULL;
2262 path->u_name = upath;
2264 setfilunixmode(vol, path, destst.st_mode);
2265 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2272 setfilunixmode(vol, path, srcst.st_mode);
2273 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2275 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2276 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2281 goto err_exchangefile;
2283 /* all this stuff is so that we can unwind a failed operation
2286 /* rename dest to temp */
2287 renamefile(vol, -1, upath, temp, temp, adsp);
2288 of_rename(vol, s_of, curdir, upath, curdir, temp);
2291 /* rename source back to dest */
2292 renamefile(vol, -1, p, upath, path->m_name, addp);
2293 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2296 /* rename temp back to source */
2297 renamefile(vol, -1, temp, p, spath, adsp);
2298 of_rename(vol, s_of, curdir, temp, sdir, spath);
2301 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2302 ad_close(adsp, ADFLAGS_HF);
2304 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2305 ad_close(addp, ADFLAGS_HF);
2309 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2310 (void)dir_remove(vol, cached);
2311 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2312 (void)dir_remove(vol, cached);