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>
27 #include <atalk/netatalk_conf.h>
29 #include "directory.h"
38 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
39 * field bytes subfield bytes
42 * ioFlFndrInfo 16 -> type 4 type field
43 * creator 4 creator field
44 * flags 2 finder flags:
46 * location 4 location in window
47 * folder 2 window that contains file
49 * ioFlXFndrInfo 16 -> iconID 2 icon id
51 * script 1 script system
53 * commentID 2 comment id
54 * putawayID 4 home directory id
57 const u_char ufinderi[ADEDLEN_FINDERI] = {
58 0, 0, 0, 0, 0, 0, 0, 0,
59 1, 0, 0, 0, 0, 0, 0, 0,
60 0, 0, 0, 0, 0, 0, 0, 0,
61 0, 0, 0, 0, 0, 0, 0, 0
64 static const u_char old_ufinderi[] = {
65 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
68 /* ----------------------
70 static int default_type(void *finder)
72 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
77 /* FIXME path : unix or mac name ? (for now it's unix name ) */
78 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);
90 memcpy(data, ufinderi, ADEDLEN_FINDERI);
91 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
94 ashort = htons(FINDERINFO_INVISIBLE);
95 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
101 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
102 linkflag |= htons(FINDERINFO_ISALIAS);
103 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
104 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
105 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
111 /* ---------------------
113 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, uint32_t utf8)
118 aint = strlen( name );
122 if (utf8_encoding(vol->v_obj)) {
123 /* but name is an utf8 mac name */
126 /* global static variable... */
128 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
137 if (aint > MACFILELEN)
144 if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
145 aint = UTF8FILELEN_EARLY;
147 utf8 = vol->v_kTextEncoding;
148 memcpy(data, &utf8, sizeof(utf8));
149 data += sizeof(utf8);
152 memcpy(data, &temp, sizeof(temp));
153 data += sizeof(temp);
156 memcpy( data, src, aint );
166 * FIXME: PDINFO is UTF8 and doesn't need adp
168 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
169 (1 << FILPBIT_CDATE) |\
170 (1 << FILPBIT_MDATE) |\
171 (1 << FILPBIT_BDATE) |\
172 (1 << FILPBIT_FINFO) |\
173 (1 << FILPBIT_RFLEN) |\
174 (1 << FILPBIT_EXTRFLEN) |\
175 (1 << FILPBIT_PDINFO) |\
176 (1 << FILPBIT_FNUM) |\
177 (1 << FILPBIT_UNIXPR)))
180 * @brief Get CNID for did/upath args both from database and adouble file
182 * 1. Get the objects CNID as stored in its adouble file
183 * 2. Get the objects CNID from the database
184 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
185 * 4. In case 2 and 3 differ, store 3 in the adouble file
187 * @param vol (rw) volume
188 * @param adp (rw) adouble struct of object upath, might be NULL
189 * @param st (r) stat of upath, must NOT be NULL
190 * @param did (r) parent CNID of upath
191 * @param upath (r) name of object
192 * @param len (r) strlen of upath
194 uint32_t get_id(struct vol *vol,
196 const struct stat *st,
201 static int first = 1; /* mark if this func is called the first time */
203 uint32_t dbcnid = CNID_INVALID;
206 if (vol->v_cdb != NULL) {
207 /* prime aint with what we think is the cnid, set did to zero for
208 catching moved files */
209 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
211 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
212 /* Throw errors if cnid_add fails. */
213 if (dbcnid == CNID_INVALID) {
215 case CNID_ERR_CLOSE: /* the db is closed */
218 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
219 afp_errno = AFPERR_PARAM;
222 afp_errno = AFPERR_PARAM;
225 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
226 /* we have to do it here for "dbd" because it uses "lazy opening" */
227 /* In order to not end in a loop somehow with goto restart below */
229 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
230 cnid_close(vol->v_cdb);
231 free(vol->v_cnidscheme);
232 vol->v_cnidscheme = strdup("tdb");
234 int flags = CNID_FLAG_MEMORY;
235 if ((vol->v_flags & AFPVOL_NODEV)) {
236 flags |= CNID_FLAG_NODEV;
238 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
240 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
242 if (!(vol->v_flags & AFPVOL_TM)) {
243 vol->v_flags |= AFPVOL_RO;
244 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
245 "Check server messages for details. Switching to read-only mode.");
246 kill(getpid(), SIGUSR2);
248 goto restart; /* now try again with the temp CNID db */
250 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
251 "Check server messages for details, can't recover from this state!");
254 afp_errno = AFPERR_MISC;
258 else if (adp && adcnid && (adcnid != dbcnid)) { /* 4 */
259 /* Update the ressource fork. For a folder adp is always null */
260 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
261 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
262 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
263 if (ad_flush(adp) != 0)
264 LOG(log_error, logtype_afpd, "get_id(\"%s\"): can't flush", fullpathname(upath));
274 /* -------------------------- */
275 int getmetadata(const AFPObj *obj,
278 struct path *path, struct dir *dir,
279 char *buf, size_t *buflen, struct adouble *adp)
281 char *data, *l_nameoff = NULL, *upath;
282 char *utf_nameoff = NULL;
287 u_char achar, fdType[4];
292 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
294 upath = path->u_name;
298 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
299 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding(obj)) /* FIXME should be m_name utf8 filename */
300 || (bitmap & (1 << FILPBIT_FNUM))) {
303 struct dir *cachedfile;
304 int len = strlen(upath);
305 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
306 id = cachedfile->d_did;
308 id = get_id(vol, adp, st, dir->d_did, upath, len);
310 /* Add it to the cache */
311 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
312 ntohl(dir->d_did), upath, ntohl(id));
314 /* Get macname from unixname first */
315 if (path->m_name == NULL) {
316 if ((path->m_name = utompath(vol, upath, id, utf8_encoding(obj))) == NULL) {
317 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
323 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
324 || (bconchar(fullpath, '/') != BSTR_OK)
325 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
326 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
330 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
331 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
335 if ((dircache_add(vol, cachedfile)) != 0) {
336 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
344 if (id == CNID_INVALID)
348 path->m_name = utompath(vol, upath, id, utf8_encoding(vol->v_obj));
351 while ( bitmap != 0 ) {
352 while (( bitmap & 1 ) == 0 ) {
360 ad_getattr(adp, &ashort);
361 } else if (vol_inv_dots(vol) && *upath == '.') {
362 ashort = htons(ATTRBIT_INVISIBLE);
366 /* FIXME do we want a visual clue if the file is read only
369 accessmode(vol, ".", &ma, dir , NULL);
370 if ((ma.ma_user & AR_UWRITE)) {
371 accessmode(vol, upath, &ma, dir , st);
372 if (!(ma.ma_user & AR_UWRITE)) {
373 ashort |= htons(ATTRBIT_NOWRITE);
377 memcpy(data, &ashort, sizeof( ashort ));
378 data += sizeof( ashort );
379 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
380 path->u_name, ntohs(ashort));
384 memcpy(data, &dir->d_did, sizeof( uint32_t ));
385 data += sizeof( uint32_t );
386 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
387 path->u_name, ntohl(dir->d_did));
391 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
392 aint = AD_DATE_FROM_UNIX(st->st_mtime);
393 memcpy(data, &aint, sizeof( aint ));
394 data += sizeof( aint );
398 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
399 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
400 aint = AD_DATE_FROM_UNIX(st->st_mtime);
403 aint = AD_DATE_FROM_UNIX(st->st_mtime);
405 memcpy(data, &aint, sizeof( int ));
406 data += sizeof( int );
410 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
411 aint = AD_DATE_START;
412 memcpy(data, &aint, sizeof( int ));
413 data += sizeof( int );
417 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
418 data += ADEDLEN_FINDERI;
423 data += sizeof( uint16_t );
427 memset(data, 0, sizeof(uint16_t));
428 data += sizeof( uint16_t );
432 memcpy(data, &id, sizeof( id ));
433 data += sizeof( id );
434 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
435 path->u_name, ntohl(id));
439 if (st->st_size > 0xffffffff)
442 aint = htonl( st->st_size );
443 memcpy(data, &aint, sizeof( aint ));
444 data += sizeof( aint );
449 if (adp->ad_rlen > 0xffffffff)
452 aint = htonl( adp->ad_rlen);
456 memcpy(data, &aint, sizeof( aint ));
457 data += sizeof( aint );
460 /* Current client needs ProDOS info block for this file.
461 Use simple heuristic and let the Mac "type" string tell
462 us what the PD file code should be. Everything gets a
463 subtype of 0x0000 unless the original value was hashed
464 to "pXYZ" when we created it. See IA, Ver 2.
465 <shirsch@adelphia.net> */
466 case FILPBIT_PDINFO :
467 if (obj->afp_version >= 30) { /* UTF8 name */
468 utf8 = kTextEncodingUTF8;
470 data += sizeof( uint16_t );
472 memcpy(data, &aint, sizeof( aint ));
473 data += sizeof( aint );
477 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
479 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
483 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
487 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
491 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
495 else if ( fdType[0] == 'p' ) {
497 ashort = (fdType[2] * 256) + fdType[3];
511 memcpy(data, &ashort, sizeof( ashort ));
512 data += sizeof( ashort );
513 memset(data, 0, sizeof( ashort ));
514 data += sizeof( ashort );
517 case FILPBIT_EXTDFLEN:
518 aint = htonl(st->st_size >> 32);
519 memcpy(data, &aint, sizeof( aint ));
520 data += sizeof( aint );
521 aint = htonl(st->st_size);
522 memcpy(data, &aint, sizeof( aint ));
523 data += sizeof( aint );
525 case FILPBIT_EXTRFLEN:
528 aint = htonl(adp->ad_rlen >> 32);
529 memcpy(data, &aint, sizeof( aint ));
530 data += sizeof( aint );
532 aint = htonl(adp->ad_rlen);
533 memcpy(data, &aint, sizeof( aint ));
534 data += sizeof( aint );
536 case FILPBIT_UNIXPR :
537 /* accessmode may change st_mode with ACLs */
538 accessmode(obj, vol, upath, &ma, dir , st);
540 aint = htonl(st->st_uid);
541 memcpy( data, &aint, sizeof( aint ));
542 data += sizeof( aint );
543 aint = htonl(st->st_gid);
544 memcpy( data, &aint, sizeof( aint ));
545 data += sizeof( aint );
548 type == slnk indicates an OSX style symlink,
549 we have to add S_IFLNK to the mode, otherwise
550 10.3 clients freak out. */
554 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
555 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
561 memcpy( data, &aint, sizeof( aint ));
562 data += sizeof( aint );
564 *data++ = ma.ma_user;
565 *data++ = ma.ma_world;
566 *data++ = ma.ma_group;
567 *data++ = ma.ma_owner;
571 return( AFPERR_BITMAP );
577 ashort = htons( data - buf );
578 memcpy(l_nameoff, &ashort, sizeof( ashort ));
579 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
582 ashort = htons( data - buf );
583 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
584 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
586 *buflen = data - buf;
590 /* ----------------------- */
591 int getfilparams(const AFPObj *obj, struct vol *vol, uint16_t bitmap, struct path *path,
592 struct dir *dir, char *buf, size_t *buflen, int in_enumerate)
594 struct adouble ad, *adp;
599 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
601 opened = PARAM_NEED_ADP(bitmap);
607 * Dont check for and resturn open fork attributes when enumerating
608 * This saves a lot of syscalls, the client will hopefully only use the result
609 * in FPGetFileParms where we return the correct value
611 flags = (!in_enumerate &&(bitmap & (1 << FILPBIT_ATTR))) ? ADFLAGS_CHECK_OF : 0;
613 adp = of_ad(vol, path, &ad);
614 upath = path->u_name;
616 if ( ad_metadata( upath, flags, adp) < 0 ) {
619 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
620 upath, strerror(errno));
621 return AFPERR_ACCESS;
623 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
632 rc = getmetadata(obj, vol, bitmap, path, dir, buf, buflen, adp);
633 ad_close(adp, ADFLAGS_HF | flags);
638 /* ----------------------------- */
639 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
644 struct ofork *of = NULL;
646 int creatf, did, openf, retvalue = AFP_OK;
652 creatf = (unsigned char) *ibuf++;
654 memcpy(&vid, ibuf, sizeof( vid ));
655 ibuf += sizeof( vid );
657 if (NULL == ( vol = getvolbyvid( vid )) )
658 return( AFPERR_PARAM );
660 if (vol->v_flags & AFPVOL_RO)
663 memcpy(&did, ibuf, sizeof( did));
664 ibuf += sizeof( did );
666 if (NULL == ( dir = dirlookup( vol, did )) )
669 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
670 return get_afp_errno(AFPERR_PARAM);
671 if ( *s_path->m_name == '\0' )
672 return( AFPERR_BADTYPE );
674 upath = s_path->u_name;
677 /* if upath is deleted we already in trouble anyway */
678 if ((of = of_findname(s_path))) {
686 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
688 /* on a soft create, if the file is open then ad_open won't fail
689 because open syscall is not called */
690 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
692 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
696 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
697 return ( AFPERR_NOOBJ );
699 return( AFPERR_EXIST );
701 return( AFPERR_ACCESS );
704 LOG(log_info, logtype_afpd, "afp_createfile: DISK FULL");
705 return( AFPERR_DFULL );
707 return( AFPERR_PARAM );
710 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
711 /* FIXME with hard create on an existing file, we already
712 * corrupted the data file.
714 netatalk_unlink( upath );
715 ad_close( &ad, ADFLAGS_DF );
716 return AFPERR_ACCESS;
719 path = s_path->m_name;
720 ad_setname(&ad, path);
723 if (lstat(upath, &st) != 0) {
724 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
725 upath, strerror(errno));
726 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
731 if ((id = get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath))) == CNID_INVALID) {
732 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
733 goto createfile_iderr;
735 (void)ad_setid(&ad, st.st_dev, st.st_ino, id, dir->d_did, vol->v_stamp);
739 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
740 fce_register_new_file(s_path);
745 setvoltime(obj, vol );
750 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
756 uint16_t vid, bitmap;
761 memcpy(&vid, ibuf, sizeof( vid ));
762 ibuf += sizeof( vid );
763 if (NULL == ( vol = getvolbyvid( vid )) ) {
764 return( AFPERR_PARAM );
767 if (vol->v_flags & AFPVOL_RO)
770 memcpy(&did, ibuf, sizeof( did ));
771 ibuf += sizeof( did );
772 if (NULL == ( dir = dirlookup( vol, did )) ) {
773 return afp_errno; /* was AFPERR_NOOBJ */
776 memcpy(&bitmap, ibuf, sizeof( bitmap ));
777 bitmap = ntohs( bitmap );
778 ibuf += sizeof( bitmap );
780 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
781 return get_afp_errno(AFPERR_PARAM);
784 if (path_isadir(s_path)) {
785 return( AFPERR_BADTYPE ); /* it's a directory */
788 if ( s_path->st_errno != 0 ) {
789 return( AFPERR_NOOBJ );
792 if ((u_long)ibuf & 1 ) {
796 if (AFP_OK == ( rc = setfilparams(obj, vol, s_path, bitmap, ibuf )) ) {
797 setvoltime(obj, vol );
804 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
807 extern struct path Cur_Path;
809 int setfilparams(const AFPObj *obj, struct vol *vol,
810 struct path *path, uint16_t f_bitmap, char *buf )
812 struct adouble ad, *adp;
814 int bit, isad = 1, err = AFP_OK;
816 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
817 uint16_t ashort, bshort, oshort;
820 uint16_t upriv_bit = 0;
822 int change_mdate = 0;
823 int change_parent_mdate = 0;
828 uint16_t bitmap = f_bitmap;
829 uint32_t cdate,bdate;
830 u_char finder_buf[32];
834 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
837 adp = of_ad(vol, path, &ad);
838 upath = path->u_name;
840 if (!vol_unix_priv(vol) && check_access(obj, vol, upath, OPENACC_WR ) < 0) {
841 return AFPERR_ACCESS;
844 /* with unix priv maybe we have to change adouble file priv first */
846 while ( bitmap != 0 ) {
847 while (( bitmap & 1 ) == 0 ) {
854 memcpy(&ashort, buf, sizeof( ashort ));
855 buf += sizeof( ashort );
859 memcpy(&cdate, buf, sizeof(cdate));
860 buf += sizeof( cdate );
863 memcpy(&newdate, buf, sizeof( newdate ));
864 buf += sizeof( newdate );
868 memcpy(&bdate, buf, sizeof( bdate));
869 buf += sizeof( bdate );
873 memcpy(finder_buf, buf, 32 );
874 if (memcmp(buf, "slnkrhap", 8) == 0 && !S_ISLNK(path->st.st_mode)) {
878 char buf[PATH_MAX+1];
879 if ((fp = open(path->u_name, O_RDONLY)) >= 0) {
880 if ((len = read(fp, buf, PATH_MAX+1))) {
881 if (unlink(path->u_name) == 0) {
883 erc = symlink(buf, path->u_name);
892 goto setfilparam_done;
898 case FILPBIT_UNIXPR :
899 if (!vol_unix_priv(vol)) {
900 /* this volume doesn't use unix priv */
906 change_parent_mdate = 1;
908 memcpy( &aint, buf, sizeof( aint ));
909 f_uid = ntohl (aint);
910 buf += sizeof( aint );
911 memcpy( &aint, buf, sizeof( aint ));
912 f_gid = ntohl (aint);
913 buf += sizeof( aint );
914 setfilowner(vol, f_uid, f_gid, path);
916 memcpy( &upriv, buf, sizeof( upriv ));
917 buf += sizeof( upriv );
918 upriv = ntohl (upriv);
919 if ((upriv & S_IWUSR)) {
920 setfilunixmode(vol, path, upriv);
927 case FILPBIT_PDINFO :
928 if (obj->afp_version < 30) { /* else it's UTF8 name */
931 /* Keep special case to support crlf translations */
932 if ((unsigned int) achar == 0x04) {
933 fdType = (u_char *)"TEXT";
936 xyy[0] = ( u_char ) 'p';
947 /* break while loop */
956 /* second try with adouble open
958 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
959 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
961 * For some things, we don't need an adouble header:
962 * - change of modification date
963 * - UNIX privs (Bug-ID #2863424)
965 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
966 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
967 return AFPERR_ACCESS;
969 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
971 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
972 ad_setname(adp, path->m_name);
977 while ( bitmap != 0 ) {
978 while (( bitmap & 1 ) == 0 ) {
985 ad_getattr(adp, &bshort);
987 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
988 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
992 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
993 change_parent_mdate = 1;
994 ad_setattr(adp, bshort);
997 ad_setdate(adp, AD_DATE_CREATE, cdate);
1001 case FILPBIT_BDATE :
1002 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1004 case FILPBIT_FINFO :
1005 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1007 case FILPBIT_UNIXPR :
1009 setfilunixmode(vol, path, upriv);
1012 case FILPBIT_PDINFO :
1013 if (obj->afp_version < 30) { /* else it's UTF8 name */
1014 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1015 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1020 err = AFPERR_BITMAP;
1021 goto setfilparam_done;
1028 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1029 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1033 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1034 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1040 ad_close(adp, ADFLAGS_HF);
1043 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1044 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1045 bitmap = 1<<FILPBIT_MDATE;
1046 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1050 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1056 * renamefile and copyfile take the old and new unix pathnames
1057 * and the new mac name.
1059 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1060 * passing -1 means this is not used, src path is a full path
1061 * src the source path
1062 * dst the dest filename in current dir
1063 * newname the dest mac name
1064 * adp adouble struct of src file, if open, or & zeroed one
1067 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1071 LOG(log_debug, logtype_afpd,
1072 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1074 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1077 return( AFPERR_NOOBJ );
1080 return( AFPERR_ACCESS );
1082 return AFPERR_VLOCK;
1083 case EXDEV : /* Cross device move -- try copy */
1084 /* NOTE: with open file it's an error because after the copy we will
1085 * get two files, it's fixable for our process (eg reopen the new file, get the
1086 * locks, and so on. But it doesn't solve the case with a second process
1088 if (adp->ad_open_forks) {
1089 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1090 return AFPERR_OLOCK; /* little lie */
1092 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1093 /* on error copyfile delete dest */
1096 return deletefile(vol, sdir_fd, src, 0);
1098 return( AFPERR_PARAM );
1102 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1106 /* try to undo the data fork rename,
1107 * we know we are on the same device
1110 unix_rename(-1, dst, sdir_fd, src );
1111 /* return the first error */
1114 return AFPERR_NOOBJ;
1117 return AFPERR_ACCESS ;
1119 return AFPERR_VLOCK;
1121 return AFPERR_PARAM ;
1126 /* don't care if we can't open the newly renamed ressource fork */
1127 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1128 ad_setname(adp, newname);
1130 ad_close( adp, ADFLAGS_HF );
1137 convert a Mac long name to an utf8 name,
1139 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1143 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1149 /* ---------------- */
1150 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1157 if ( type != 2 && !(vol->v_obj->afp_version >= 30 && type == 3) ) {
1163 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1164 if (vol->v_obj->afp_version >= 30) {
1165 /* convert it to UTF8
1167 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1171 strncpy( newname, ibuf, plen );
1172 newname[ plen ] = '\0';
1174 if (strlen(newname) != plen) {
1175 /* there's \0 in newname, e.g. it's a pathname not
1183 memcpy(&hint, ibuf, sizeof(hint));
1184 ibuf += sizeof(hint);
1186 memcpy(&len16, ibuf, sizeof(len16));
1187 ibuf += sizeof(len16);
1188 plen = ntohs(len16);
1191 if (plen > AFPOBJ_TMPSIZ) {
1194 strncpy( newname, ibuf, plen );
1195 newname[ plen ] = '\0';
1196 if (strlen(newname) != plen) {
1205 /* -----------------------------------
1207 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1209 struct vol *s_vol, *d_vol;
1211 char *newname, *p, *upath;
1212 struct path *s_path;
1213 uint32_t sdid, ddid;
1214 int err, retvalue = AFP_OK;
1215 uint16_t svid, dvid;
1217 struct adouble ad, *adp;
1223 memcpy(&svid, ibuf, sizeof( svid ));
1224 ibuf += sizeof( svid );
1225 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1226 return( AFPERR_PARAM );
1229 memcpy(&sdid, ibuf, sizeof( sdid ));
1230 ibuf += sizeof( sdid );
1231 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1235 memcpy(&dvid, ibuf, sizeof( dvid ));
1236 ibuf += sizeof( dvid );
1237 memcpy(&ddid, ibuf, sizeof( ddid ));
1238 ibuf += sizeof( ddid );
1240 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1241 return get_afp_errno(AFPERR_PARAM);
1243 if ( path_isadir(s_path) ) {
1244 return( AFPERR_BADTYPE );
1247 /* don't allow copies when the file is open.
1248 * XXX: the spec only calls for read/deny write access.
1249 * however, copyfile doesn't have any of that info,
1250 * and locks need to stay coherent. as a result,
1251 * we just balk if the file is opened already. */
1253 adp = of_ad(s_vol, s_path, &ad);
1255 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1256 return AFPERR_DENYCONF;
1258 #ifdef HAVE_FSHARE_T
1260 shmd.f_access = F_RDACC;
1261 shmd.f_deny = F_NODNY;
1262 if (fcntl(ad_data_fileno(adp), F_SHARE, &shmd) != 0) {
1263 retvalue = AFPERR_DENYCONF;
1266 if (AD_RSRC_OPEN(adp) && fcntl(ad_reso_fileno(adp), F_SHARE, &shmd) != 0) {
1267 retvalue = AFPERR_DENYCONF;
1271 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1272 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1275 retvalue = AFPERR_DENYCONF;
1279 newname = obj->newtmp;
1280 strcpy( newname, s_path->m_name );
1282 p = ctoupath( s_vol, curdir, newname );
1284 retvalue = AFPERR_PARAM;
1288 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1289 retvalue = AFPERR_PARAM;
1293 if (d_vol->v_flags & AFPVOL_RO) {
1294 retvalue = AFPERR_VLOCK;
1298 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1299 retvalue = afp_errno;
1303 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1304 retvalue = get_afp_errno(AFPERR_NOOBJ);
1308 if ( *s_path->m_name != '\0' ) {
1309 retvalue =path_error(s_path, AFPERR_NOOBJ);
1313 /* one of the handful of places that knows about the path type */
1314 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1315 retvalue = AFPERR_PARAM;
1318 /* newname is always only a filename so curdir *is* its
1321 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding(d_vol->v_obj)))) {
1322 retvalue =AFPERR_PARAM;
1326 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1332 setvoltime(obj, d_vol );
1335 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1339 /* ----------------------------------
1340 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1341 * because we are doing it elsewhere.
1342 * currently if newname is NULL then adp is NULL.
1344 int copyfile(const struct vol *s_vol,
1345 const struct vol *d_vol,
1350 struct adouble *adp)
1352 struct adouble ads, add;
1359 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1360 sfd, src, dst, newname);
1363 ad_init(&ads, s_vol);
1367 adflags = ADFLAGS_DF | ADFLAGS_RF | ADFLAGS_NORF;
1369 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1374 if (!AD_RSRC_OPEN(adp))
1375 /* no resource fork, don't create one for dst file */
1376 adflags &= ~ADFLAGS_RF;
1378 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1380 if (stat_result < 0) {
1381 /* unlikely but if fstat fails, the default file mode will be 0666. */
1382 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1385 ad_init(&add, d_vol);
1386 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1388 ad_close( adp, adflags );
1389 if (EEXIST != ret_err) {
1390 deletefile(d_vol, -1, dst, 0);
1393 return AFPERR_EXIST;
1396 if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1397 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1400 if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1401 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1406 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1407 /* set the new name in the resource fork */
1408 ad_copy_header(&add, adp);
1409 ad_setname(&add, newname);
1412 ad_close( adp, adflags );
1414 if (ad_close( &add, adflags ) <0) {
1419 deletefile(d_vol, -1, dst, 0);
1421 else if (stat_result == 0) {
1422 /* set dest modification date to src date */
1425 ut.actime = ut.modtime = st.st_mtime;
1427 /* FIXME netatalk doesn't use resource fork file date
1428 * but maybe we should set its modtime too.
1433 switch ( ret_err ) {
1439 LOG(log_info, logtype_afpd, "copyfile: DISK FULL");
1440 return AFPERR_DFULL;
1442 return AFPERR_NOOBJ;
1444 return AFPERR_ACCESS;
1446 return AFPERR_VLOCK;
1448 return AFPERR_PARAM;
1452 /* -----------------------------------
1453 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1454 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1456 when deletefile is called we don't have lock on it, file is closed (for us)
1457 untrue if called by renamefile
1459 ad_open always try to open file RDWR first and ad_lock takes care of
1460 WRITE lock on read only file.
1463 static int check_attrib(struct adouble *adp)
1465 uint16_t bshort = 0;
1467 ad_getattr(adp, &bshort);
1469 * Does kFPDeleteInhibitBit (bit 8) set?
1471 if ((bshort & htons(ATTRBIT_NODELETE))) {
1472 return AFPERR_OLOCK;
1474 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1480 * dirfd can be used for unlinkat semantics
1482 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1485 struct adouble *adp = NULL;
1486 int adflags, err = AFP_OK;
1489 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1493 /* was EACCESS error try to get only metadata */
1494 /* we never want to create a resource fork here, we are going to delete it
1495 * moreover sometimes deletefile is called with a no existent file and
1496 * ad_open would create a 0 byte resource fork
1498 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1499 if ((err = check_attrib(&ad))) {
1500 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1507 /* try to open both forks at once */
1508 adflags = ADFLAGS_DF;
1509 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1514 case EACCES: /* maybe it's a file with no write mode for us */
1515 break; /* was return AFPERR_ACCESS;*/
1528 if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1529 adflags |= ADFLAGS_RF;
1530 /* FIXME we have a pb here because we want to know if a file is open
1531 * there's a 'priority inversion' if you can't open the ressource fork RW
1532 * you can delete it if it's open because you can't get a write lock.
1534 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1537 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1539 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1545 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1546 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1548 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1550 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1551 cnid_delete(vol->v_cdb, id);
1557 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1560 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1565 /* ------------------------------------ */
1566 /* return a file id */
1567 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1576 struct path *s_path;
1582 memcpy(&vid, ibuf, sizeof(vid));
1583 ibuf += sizeof(vid);
1585 if (NULL == ( vol = getvolbyvid( vid )) ) {
1586 return( AFPERR_PARAM);
1589 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1593 if (vol->v_flags & AFPVOL_RO)
1594 return AFPERR_VLOCK;
1596 memcpy(&did, ibuf, sizeof( did ));
1597 ibuf += sizeof(did);
1599 if (NULL == ( dir = dirlookup( vol, did )) ) {
1600 return afp_errno; /* was AFPERR_PARAM */
1603 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1604 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1607 if ( path_isadir(s_path) ) {
1608 return( AFPERR_BADTYPE );
1611 upath = s_path->u_name;
1612 switch (s_path->st_errno) {
1614 break; /* success */
1617 return AFPERR_ACCESS;
1619 return AFPERR_NOOBJ;
1621 return AFPERR_PARAM;
1624 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1625 memcpy(rbuf, &id, sizeof(id));
1626 *rbuflen = sizeof(id);
1627 return AFPERR_EXISTID;
1630 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1631 memcpy(rbuf, &id, sizeof(id));
1632 *rbuflen = sizeof(id);
1639 /* ------------------------------- */
1645 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1648 struct reenum *param = data;
1649 struct vol *vol = param->vol;
1650 cnid_t did = param->did;
1653 if ( lstat(de->d_name, &path.st) < 0 )
1656 /* update or add to cnid */
1657 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1662 /* --------------------
1663 * Ok the db is out of synch with the dir.
1664 * but if it's a deleted file we don't want to do it again and again.
1667 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1673 if (vol->v_cdb == NULL) {
1677 /* FIXME use of_statdir ? */
1678 if (lstat(name, &st)) {
1682 if (dirreenumerate(dir, &st)) {
1683 /* we already did it once and the dir haven't been modified */
1684 return dir->d_offcnt;
1688 data.did = dir->d_did;
1689 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1690 setdiroffcnt(curdir, &st, ret);
1691 dir->d_flags |= DIRF_CNID;
1697 /* ------------------------------
1698 resolve a file id */
1699 int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1708 uint16_t vid, bitmap;
1710 static char buffer[12 + MAXPATHLEN + 1];
1711 int len = 12 + MAXPATHLEN + 1;
1716 memcpy(&vid, ibuf, sizeof(vid));
1717 ibuf += sizeof(vid);
1719 if (NULL == ( vol = getvolbyvid( vid )) ) {
1720 return( AFPERR_PARAM);
1723 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1727 memcpy(&id, ibuf, sizeof( id ));
1732 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1736 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1737 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1740 if (NULL == ( dir = dirlookup( vol, id )) ) {
1741 return AFPERR_NOID; /* idem AFPERR_PARAM */
1743 if (movecwd(vol, dir) < 0) {
1747 return AFPERR_ACCESS;
1751 return AFPERR_PARAM;
1755 memset(&path, 0, sizeof(path));
1756 path.u_name = upath;
1757 if ( of_stat(&path) < 0 ) {
1759 /* with nfs and our working directory is deleted */
1760 if (errno == ESTALE) {
1764 if ( errno == ENOENT && !retry) {
1765 /* cnid db is out of sync, reenumerate the directory and update ids */
1766 reenumerate_id(vol, ".", dir);
1774 return AFPERR_ACCESS;
1778 return AFPERR_PARAM;
1782 /* directories are bad */
1783 if (S_ISDIR(path.st.st_mode)) {
1784 /* OS9 and OSX don't return the same error code */
1785 return (obj->afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1788 memcpy(&bitmap, ibuf, sizeof(bitmap));
1789 bitmap = ntohs( bitmap );
1790 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding(obj)))) {
1794 if (AFP_OK != (err = getfilparams(obj, vol, bitmap, &path , curdir,
1795 rbuf + sizeof(bitmap), &buflen, 0))) {
1798 *rbuflen = buflen + sizeof(bitmap);
1799 memcpy(rbuf, ibuf, sizeof(bitmap));
1804 /* ------------------------------ */
1805 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1815 static char buffer[12 + MAXPATHLEN + 1];
1816 int len = 12 + MAXPATHLEN + 1;
1821 memcpy(&vid, ibuf, sizeof(vid));
1822 ibuf += sizeof(vid);
1824 if (NULL == ( vol = getvolbyvid( vid )) ) {
1825 return( AFPERR_PARAM);
1828 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1832 if (vol->v_flags & AFPVOL_RO)
1833 return AFPERR_VLOCK;
1835 memcpy(&id, ibuf, sizeof( id ));
1839 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1843 if (NULL == ( dir = dirlookup( vol, id )) ) {
1844 if (afp_errno == AFPERR_NOOBJ) {
1848 return( AFPERR_PARAM );
1852 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1856 return AFPERR_ACCESS;
1861 /* still try to delete the id */
1865 return AFPERR_PARAM;
1868 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1869 return AFPERR_BADTYPE;
1872 if (cnid_delete(vol->v_cdb, fileid)) {
1875 return AFPERR_VLOCK;
1878 return AFPERR_ACCESS;
1880 return AFPERR_PARAM;
1887 /* ------------------------------ */
1888 static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
1892 if (path->st_errno) {
1893 switch (path->st_errno) {
1895 afp_errno = AFPERR_NOID;
1899 afp_errno = AFPERR_ACCESS;
1902 afp_errno = AFPERR_PARAM;
1907 /* we use file_access both for legacy Mac perm and
1908 * for unix privilege, rename will take care of folder perms
1910 if (file_access(obj, vol, path, OPENACC_WR ) < 0) {
1911 afp_errno = AFPERR_ACCESS;
1915 if ((*of = of_findname(path))) {
1916 /* reuse struct adouble so it won't break locks */
1920 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
1922 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1924 * The user must have the Read & Write privilege for both files in order to use this command.
1926 ad_close(adp, ADFLAGS_HF);
1927 afp_errno = AFPERR_ACCESS;
1934 #define APPLETEMP ".AppleTempXXXXXX"
1936 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1938 struct stat srcst, destst;
1940 struct dir *dir, *sdir;
1941 char *spath, temp[17], *p;
1942 char *supath, *upath;
1947 struct adouble *adsp = NULL;
1948 struct adouble *addp = NULL;
1949 struct ofork *s_of = NULL;
1950 struct ofork *d_of = NULL;
1963 memcpy(&vid, ibuf, sizeof(vid));
1964 ibuf += sizeof(vid);
1966 if (NULL == ( vol = getvolbyvid( vid )) ) {
1967 return( AFPERR_PARAM);
1970 if ((vol->v_flags & AFPVOL_RO))
1971 return AFPERR_VLOCK;
1973 /* source and destination dids */
1974 memcpy(&sid, ibuf, sizeof(sid));
1975 ibuf += sizeof(sid);
1976 memcpy(&did, ibuf, sizeof(did));
1977 ibuf += sizeof(did);
1980 if (NULL == (dir = dirlookup( vol, sid )) ) {
1981 return afp_errno; /* was AFPERR_PARAM */
1984 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1985 return get_afp_errno(AFPERR_NOOBJ);
1988 if ( path_isadir(path) ) {
1989 return AFPERR_BADTYPE; /* it's a dir */
1992 /* save some stuff */
1995 spath = obj->oldtmp;
1996 supath = obj->newtmp;
1997 strcpy(spath, path->m_name);
1998 strcpy(supath, path->u_name); /* this is for the cnid changing */
1999 p = absupath( vol, sdir, supath);
2001 /* pathname too long */
2002 return AFPERR_PARAM ;
2006 if (!(adsp = find_adouble(obj, vol, path, &s_of, &ads))) {
2010 /* ***** from here we may have resource fork open **** */
2012 /* look for the source cnid. if it doesn't exist, don't worry about
2014 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2016 if (NULL == ( dir = dirlookup( vol, did )) ) {
2017 err = afp_errno; /* was AFPERR_PARAM */
2018 goto err_exchangefile;
2021 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2022 err = get_afp_errno(AFPERR_NOOBJ);
2023 goto err_exchangefile;
2026 if ( path_isadir(path) ) {
2027 err = AFPERR_BADTYPE;
2028 goto err_exchangefile;
2031 /* FPExchangeFiles is the only call that can return the SameObj
2033 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2034 err = AFPERR_SAMEOBJ;
2035 goto err_exchangefile;
2039 if (!(addp = find_adouble(obj, vol, path, &d_of, &add))) {
2041 goto err_exchangefile;
2045 /* they are not on the same device and at least one is open
2046 * FIXME broken for for crossdev and adouble v2
2049 crossdev = (srcst.st_dev != destst.st_dev);
2050 if (/* (d_of || s_of) && */ crossdev) {
2052 goto err_exchangefile;
2055 /* look for destination id. */
2056 upath = path->u_name;
2057 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2059 /* construct a temp name.
2060 * NOTE: the temp file will be in the dest file's directory. it
2061 * will also be inaccessible from AFP. */
2062 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2063 if (!mktemp(temp)) {
2065 goto err_exchangefile;
2069 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2070 ad_close(adsp, ADFLAGS_HF);
2071 ad_close(addp, ADFLAGS_HF);
2074 /* now, quickly rename the file. we error if we can't. */
2075 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2076 goto err_exchangefile;
2077 of_rename(vol, s_of, sdir, spath, curdir, temp);
2079 /* rename destination to source */
2080 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2081 goto err_src_to_tmp;
2082 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2084 /* rename temp to destination */
2085 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2086 goto err_dest_to_src;
2087 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2089 /* id's need switching. src -> dest and dest -> src.
2090 * we need to re-stat() if it was a cross device copy.
2093 cnid_delete(vol->v_cdb, sid);
2095 cnid_delete(vol->v_cdb, did);
2097 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2098 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2100 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2101 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2106 err = AFPERR_ACCESS;
2111 goto err_temp_to_dest;
2114 /* here we need to reopen if crossdev */
2115 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2120 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2125 /* change perms, src gets dest perm and vice versa */
2130 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2131 err = AFP_OK; /* ignore error */
2132 goto err_temp_to_dest;
2136 * we need to exchange ACL entries as well
2138 /* exchange_acls(vol, p, upath); */
2143 path->m_name = NULL;
2144 path->u_name = upath;
2146 setfilunixmode(vol, path, destst.st_mode);
2147 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2154 setfilunixmode(vol, path, srcst.st_mode);
2155 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2157 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2158 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2163 goto err_exchangefile;
2165 /* all this stuff is so that we can unwind a failed operation
2168 /* rename dest to temp */
2169 renamefile(vol, -1, upath, temp, temp, adsp);
2170 of_rename(vol, s_of, curdir, upath, curdir, temp);
2173 /* rename source back to dest */
2174 renamefile(vol, -1, p, upath, path->m_name, addp);
2175 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2178 /* rename temp back to source */
2179 renamefile(vol, -1, temp, p, spath, adsp);
2180 of_rename(vol, s_of, curdir, temp, sdir, spath);
2183 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2184 ad_close(adsp, ADFLAGS_HF);
2186 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2187 ad_close(addp, ADFLAGS_HF);
2191 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2192 (void)dir_remove(vol, cached);
2193 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2194 (void)dir_remove(vol, cached);