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"
37 #include "spotlight.h"
39 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
40 * field bytes subfield bytes
43 * ioFlFndrInfo 16 -> type 4 type field
44 * creator 4 creator field
45 * flags 2 finder flags:
47 * location 4 location in window
48 * folder 2 window that contains file
50 * ioFlXFndrInfo 16 -> iconID 2 icon id
52 * script 1 script system
54 * commentID 2 comment id
55 * putawayID 4 home directory id
58 const u_char ufinderi[ADEDLEN_FINDERI] = {
59 0, 0, 0, 0, 0, 0, 0, 0,
60 1, 0, 0, 0, 0, 0, 0, 0,
61 0, 0, 0, 0, 0, 0, 0, 0,
62 0, 0, 0, 0, 0, 0, 0, 0
65 static const u_char old_ufinderi[] = {
66 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
69 /* ----------------------
71 static int default_type(void *finder)
73 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
78 /* FIXME path : unix or mac name ? (for now it's unix name ) */
79 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
81 void *ad_finder = NULL;
85 ad_finder = ad_entry(adp, ADEID_FINDERI);
88 memcpy(data, ad_finder, ADEDLEN_FINDERI);
91 memcpy(data, ufinderi, ADEDLEN_FINDERI);
92 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
95 ashort = htons(FINDERINFO_INVISIBLE);
96 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
102 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
103 linkflag |= htons(FINDERINFO_ISALIAS);
104 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
105 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
106 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
112 /* ---------------------
114 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, uint32_t utf8)
119 aint = strlen( name );
123 if (utf8_encoding(vol->v_obj)) {
124 /* but name is an utf8 mac name */
127 /* global static variable... */
129 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
138 if (aint > MACFILELEN)
145 if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
146 aint = UTF8FILELEN_EARLY;
148 utf8 = vol->v_kTextEncoding;
149 memcpy(data, &utf8, sizeof(utf8));
150 data += sizeof(utf8);
153 memcpy(data, &temp, sizeof(temp));
154 data += sizeof(temp);
157 memcpy( data, src, aint );
167 * FIXME: PDINFO is UTF8 and doesn't need adp
169 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
170 (1 << FILPBIT_CDATE) |\
171 (1 << FILPBIT_MDATE) |\
172 (1 << FILPBIT_BDATE) |\
173 (1 << FILPBIT_FINFO) |\
174 (1 << FILPBIT_RFLEN) |\
175 (1 << FILPBIT_EXTRFLEN) |\
176 (1 << FILPBIT_PDINFO) |\
177 (1 << FILPBIT_FNUM) |\
178 (1 << FILPBIT_UNIXPR)))
181 * @brief Get CNID for did/upath args both from database and adouble file
183 * 1. Get the objects CNID as stored in its adouble file
184 * 2. Get the objects CNID from the database
185 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
186 * 4. In case 2 and 3 differ, store 3 in the adouble file
188 * @param vol (rw) volume
189 * @param adp (rw) adouble struct of object upath, might be NULL
190 * @param st (r) stat of upath, must NOT be NULL
191 * @param did (r) parent CNID of upath
192 * @param upath (r) name of object
193 * @param len (r) strlen of upath
195 uint32_t get_id(struct vol *vol,
197 const struct stat *st,
202 static int first = 1; /* mark if this func is called the first time */
204 uint32_t dbcnid = CNID_INVALID;
207 if (vol->v_cdb != NULL) {
208 /* prime aint with what we think is the cnid, set did to zero for
209 catching moved files */
210 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
212 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
213 /* Throw errors if cnid_add fails. */
214 if (dbcnid == CNID_INVALID) {
216 case CNID_ERR_CLOSE: /* the db is closed */
219 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
220 afp_errno = AFPERR_PARAM;
223 afp_errno = AFPERR_PARAM;
226 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
227 /* we have to do it here for "dbd" because it uses "lazy opening" */
228 /* In order to not end in a loop somehow with goto restart below */
230 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
231 cnid_close(vol->v_cdb);
232 free(vol->v_cnidscheme);
233 vol->v_cnidscheme = strdup("tdb");
235 int flags = CNID_FLAG_MEMORY;
236 if ((vol->v_flags & AFPVOL_NODEV)) {
237 flags |= CNID_FLAG_NODEV;
239 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
241 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
243 if (!(vol->v_flags & AFPVOL_TM)) {
244 vol->v_flags |= AFPVOL_RO;
245 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
246 "Check server messages for details. Switching to read-only mode.");
247 kill(getpid(), SIGUSR2);
249 goto restart; /* now try again with the temp CNID db */
251 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
252 "Check server messages for details, can't recover from this state!");
255 afp_errno = AFPERR_MISC;
259 else if (adp && adcnid && (adcnid != dbcnid)) { /* 4 */
260 /* Update the ressource fork. For a folder adp is always null */
261 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
262 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
263 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
264 if (ad_flush(adp) != 0)
265 LOG(log_error, logtype_afpd, "get_id(\"%s\"): can't flush", fullpathname(upath));
275 /* -------------------------- */
276 int getmetadata(const AFPObj *obj,
279 struct path *path, struct dir *dir,
280 char *buf, size_t *buflen, struct adouble *adp)
282 char *data, *l_nameoff = NULL, *upath;
283 char *utf_nameoff = NULL;
288 u_char achar, fdType[4];
293 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
295 upath = path->u_name;
299 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
300 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding(obj)) /* FIXME should be m_name utf8 filename */
301 || (bitmap & (1 << FILPBIT_FNUM))) {
304 struct dir *cachedfile;
305 int len = strlen(upath);
306 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
307 id = cachedfile->d_did;
309 id = get_id(vol, adp, st, dir->d_did, upath, len);
311 /* Add it to the cache */
312 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
313 ntohl(dir->d_did), upath, ntohl(id));
315 /* Get macname from unixname first */
316 if (path->m_name == NULL) {
317 if ((path->m_name = utompath(vol, upath, id, utf8_encoding(obj))) == NULL) {
318 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
324 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
325 || (bconchar(fullpath, '/') != BSTR_OK)
326 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
327 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
331 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
332 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
336 if ((dircache_add(vol, cachedfile)) != 0) {
337 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
345 if (id == CNID_INVALID)
349 path->m_name = utompath(vol, upath, id, utf8_encoding(vol->v_obj));
352 while ( bitmap != 0 ) {
353 while (( bitmap & 1 ) == 0 ) {
361 ad_getattr(adp, &ashort);
362 } else if (vol_inv_dots(vol) && *upath == '.') {
363 ashort = htons(ATTRBIT_INVISIBLE);
367 /* FIXME do we want a visual clue if the file is read only
370 accessmode(vol, ".", &ma, dir , NULL);
371 if ((ma.ma_user & AR_UWRITE)) {
372 accessmode(vol, upath, &ma, dir , st);
373 if (!(ma.ma_user & AR_UWRITE)) {
374 ashort |= htons(ATTRBIT_NOWRITE);
378 memcpy(data, &ashort, sizeof( ashort ));
379 data += sizeof( ashort );
380 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
381 path->u_name, ntohs(ashort));
385 memcpy(data, &dir->d_did, sizeof( uint32_t ));
386 data += sizeof( uint32_t );
387 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
388 path->u_name, ntohl(dir->d_did));
392 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
393 aint = AD_DATE_FROM_UNIX(st->st_mtime);
394 memcpy(data, &aint, sizeof( aint ));
395 data += sizeof( aint );
399 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
400 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
401 aint = AD_DATE_FROM_UNIX(st->st_mtime);
404 aint = AD_DATE_FROM_UNIX(st->st_mtime);
406 memcpy(data, &aint, sizeof( int ));
407 data += sizeof( int );
411 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
412 aint = AD_DATE_START;
413 memcpy(data, &aint, sizeof( int ));
414 data += sizeof( int );
418 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
419 data += ADEDLEN_FINDERI;
424 data += sizeof( uint16_t );
428 memset(data, 0, sizeof(uint16_t));
429 data += sizeof( uint16_t );
433 memcpy(data, &id, sizeof( id ));
434 data += sizeof( id );
435 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
436 path->u_name, ntohl(id));
440 if (st->st_size > 0xffffffff)
443 aint = htonl( st->st_size );
444 memcpy(data, &aint, sizeof( aint ));
445 data += sizeof( aint );
450 if (adp->ad_rlen > 0xffffffff)
453 aint = htonl( adp->ad_rlen);
457 memcpy(data, &aint, sizeof( aint ));
458 data += sizeof( aint );
461 /* Current client needs ProDOS info block for this file.
462 Use simple heuristic and let the Mac "type" string tell
463 us what the PD file code should be. Everything gets a
464 subtype of 0x0000 unless the original value was hashed
465 to "pXYZ" when we created it. See IA, Ver 2.
466 <shirsch@adelphia.net> */
467 case FILPBIT_PDINFO :
468 if (obj->afp_version >= 30) { /* UTF8 name */
469 utf8 = kTextEncodingUTF8;
471 data += sizeof( uint16_t );
473 memcpy(data, &aint, sizeof( aint ));
474 data += sizeof( aint );
478 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
480 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
484 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
488 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
492 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
496 else if ( fdType[0] == 'p' ) {
498 ashort = (fdType[2] * 256) + fdType[3];
512 memcpy(data, &ashort, sizeof( ashort ));
513 data += sizeof( ashort );
514 memset(data, 0, sizeof( ashort ));
515 data += sizeof( ashort );
518 case FILPBIT_EXTDFLEN:
519 aint = htonl(st->st_size >> 32);
520 memcpy(data, &aint, sizeof( aint ));
521 data += sizeof( aint );
522 aint = htonl(st->st_size);
523 memcpy(data, &aint, sizeof( aint ));
524 data += sizeof( aint );
526 case FILPBIT_EXTRFLEN:
529 aint = htonl(adp->ad_rlen >> 32);
530 memcpy(data, &aint, sizeof( aint ));
531 data += sizeof( aint );
533 aint = htonl(adp->ad_rlen);
534 memcpy(data, &aint, sizeof( aint ));
535 data += sizeof( aint );
537 case FILPBIT_UNIXPR :
538 /* accessmode may change st_mode with ACLs */
539 accessmode(obj, vol, upath, &ma, dir , st);
541 aint = htonl(st->st_uid);
542 memcpy( data, &aint, sizeof( aint ));
543 data += sizeof( aint );
544 aint = htonl(st->st_gid);
545 memcpy( data, &aint, sizeof( aint ));
546 data += sizeof( aint );
549 type == slnk indicates an OSX style symlink,
550 we have to add S_IFLNK to the mode, otherwise
551 10.3 clients freak out. */
555 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
556 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
562 memcpy( data, &aint, sizeof( aint ));
563 data += sizeof( aint );
565 *data++ = ma.ma_user;
566 *data++ = ma.ma_world;
567 *data++ = ma.ma_group;
568 *data++ = ma.ma_owner;
572 return( AFPERR_BITMAP );
578 ashort = htons( data - buf );
579 memcpy(l_nameoff, &ashort, sizeof( ashort ));
580 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
583 ashort = htons( data - buf );
584 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
585 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
587 *buflen = data - buf;
591 /* ----------------------- */
592 int getfilparams(const AFPObj *obj, struct vol *vol, uint16_t bitmap, struct path *path,
593 struct dir *dir, char *buf, size_t *buflen, int in_enumerate)
595 struct adouble ad, *adp;
600 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
602 opened = PARAM_NEED_ADP(bitmap);
608 * Dont check for and resturn open fork attributes when enumerating
609 * This saves a lot of syscalls, the client will hopefully only use the result
610 * in FPGetFileParms where we return the correct value
612 flags = (!in_enumerate &&(bitmap & (1 << FILPBIT_ATTR))) ? ADFLAGS_CHECK_OF : 0;
614 adp = of_ad(vol, path, &ad);
615 upath = path->u_name;
617 if ( ad_metadata( upath, flags, adp) < 0 ) {
620 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
621 upath, strerror(errno));
622 return AFPERR_ACCESS;
624 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
633 rc = getmetadata(obj, vol, bitmap, path, dir, buf, buflen, adp);
634 ad_close(adp, ADFLAGS_HF | flags);
639 /* ----------------------------- */
640 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
645 struct ofork *of = NULL;
647 int creatf, did, openf, retvalue = AFP_OK;
653 creatf = (unsigned char) *ibuf++;
655 memcpy(&vid, ibuf, sizeof( vid ));
656 ibuf += sizeof( vid );
658 if (NULL == ( vol = getvolbyvid( vid )) )
659 return( AFPERR_PARAM );
661 if (vol->v_flags & AFPVOL_RO)
664 memcpy(&did, ibuf, sizeof( did));
665 ibuf += sizeof( did );
667 if (NULL == ( dir = dirlookup( vol, did )) )
670 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
671 return get_afp_errno(AFPERR_PARAM);
672 if ( *s_path->m_name == '\0' )
673 return( AFPERR_BADTYPE );
675 upath = s_path->u_name;
678 /* if upath is deleted we already in trouble anyway */
679 if ((of = of_findname(s_path))) {
687 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
689 /* on a soft create, if the file is open then ad_open won't fail
690 because open syscall is not called */
691 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
693 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
697 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
698 return ( AFPERR_NOOBJ );
700 return( AFPERR_EXIST );
702 return( AFPERR_ACCESS );
705 LOG(log_info, logtype_afpd, "afp_createfile: DISK FULL");
706 return( AFPERR_DFULL );
708 return( AFPERR_PARAM );
711 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
712 /* FIXME with hard create on an existing file, we already
713 * corrupted the data file.
715 netatalk_unlink( upath );
716 ad_close( &ad, ADFLAGS_DF );
717 return AFPERR_ACCESS;
720 path = s_path->m_name;
721 ad_setname(&ad, path);
724 if (lstat(upath, &st) != 0) {
725 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
726 upath, strerror(errno));
727 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
732 if ((id = get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath))) == CNID_INVALID) {
733 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
734 goto createfile_iderr;
736 (void)ad_setid(&ad, st.st_dev, st.st_ino, id, dir->d_did, vol->v_stamp);
740 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
741 fce_register_new_file(s_path);
744 setvoltime(obj, vol );
749 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
755 uint16_t vid, bitmap;
760 memcpy(&vid, ibuf, sizeof( vid ));
761 ibuf += sizeof( vid );
762 if (NULL == ( vol = getvolbyvid( vid )) ) {
763 return( AFPERR_PARAM );
766 if (vol->v_flags & AFPVOL_RO)
769 memcpy(&did, ibuf, sizeof( did ));
770 ibuf += sizeof( did );
771 if (NULL == ( dir = dirlookup( vol, did )) ) {
772 return afp_errno; /* was AFPERR_NOOBJ */
775 memcpy(&bitmap, ibuf, sizeof( bitmap ));
776 bitmap = ntohs( bitmap );
777 ibuf += sizeof( bitmap );
779 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
780 return get_afp_errno(AFPERR_PARAM);
783 if (path_isadir(s_path)) {
784 return( AFPERR_BADTYPE ); /* it's a directory */
787 if ( s_path->st_errno != 0 ) {
788 return( AFPERR_NOOBJ );
791 if ((u_long)ibuf & 1 ) {
795 if (AFP_OK == ( rc = setfilparams(obj, vol, s_path, bitmap, ibuf )) ) {
796 setvoltime(obj, vol );
803 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
806 extern struct path Cur_Path;
808 int setfilparams(const AFPObj *obj, struct vol *vol,
809 struct path *path, uint16_t f_bitmap, char *buf )
811 struct adouble ad, *adp;
813 int bit, isad = 1, err = AFP_OK;
815 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
816 uint16_t ashort, bshort, oshort;
819 uint16_t upriv_bit = 0;
821 int change_mdate = 0;
822 int change_parent_mdate = 0;
827 uint16_t bitmap = f_bitmap;
828 uint32_t cdate,bdate;
829 u_char finder_buf[32];
833 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
836 adp = of_ad(vol, path, &ad);
837 upath = path->u_name;
839 if (!vol_unix_priv(vol) && check_access(obj, vol, upath, OPENACC_WR ) < 0) {
840 return AFPERR_ACCESS;
843 /* with unix priv maybe we have to change adouble file priv first */
845 while ( bitmap != 0 ) {
846 while (( bitmap & 1 ) == 0 ) {
853 memcpy(&ashort, buf, sizeof( ashort ));
854 buf += sizeof( ashort );
858 memcpy(&cdate, buf, sizeof(cdate));
859 buf += sizeof( cdate );
862 memcpy(&newdate, buf, sizeof( newdate ));
863 buf += sizeof( newdate );
867 memcpy(&bdate, buf, sizeof( bdate));
868 buf += sizeof( bdate );
872 memcpy(finder_buf, buf, 32 );
873 if (memcmp(buf, "slnkrhap", 8) == 0 && !S_ISLNK(path->st.st_mode)) {
877 char buf[PATH_MAX+1];
878 if ((fp = open(path->u_name, O_RDONLY)) >= 0) {
879 if ((len = read(fp, buf, PATH_MAX+1))) {
880 if (unlink(path->u_name) == 0) {
882 erc = symlink(buf, path->u_name);
891 goto setfilparam_done;
897 case FILPBIT_UNIXPR :
898 if (!vol_unix_priv(vol)) {
899 /* this volume doesn't use unix priv */
905 change_parent_mdate = 1;
907 memcpy( &aint, buf, sizeof( aint ));
908 f_uid = ntohl (aint);
909 buf += sizeof( aint );
910 memcpy( &aint, buf, sizeof( aint ));
911 f_gid = ntohl (aint);
912 buf += sizeof( aint );
913 setfilowner(vol, f_uid, f_gid, path);
915 memcpy( &upriv, buf, sizeof( upriv ));
916 buf += sizeof( upriv );
917 upriv = ntohl (upriv);
918 if ((upriv & S_IWUSR)) {
919 setfilunixmode(vol, path, upriv);
926 case FILPBIT_PDINFO :
927 if (obj->afp_version < 30) { /* else it's UTF8 name */
930 /* Keep special case to support crlf translations */
931 if ((unsigned int) achar == 0x04) {
932 fdType = (u_char *)"TEXT";
935 xyy[0] = ( u_char ) 'p';
946 /* break while loop */
955 /* second try with adouble open
957 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
958 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
960 * For some things, we don't need an adouble header:
961 * - change of modification date
962 * - UNIX privs (Bug-ID #2863424)
964 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
965 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
966 return AFPERR_ACCESS;
968 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
970 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
971 ad_setname(adp, path->m_name);
976 while ( bitmap != 0 ) {
977 while (( bitmap & 1 ) == 0 ) {
984 ad_getattr(adp, &bshort);
986 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
987 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
991 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
992 change_parent_mdate = 1;
993 ad_setattr(adp, bshort);
996 ad_setdate(adp, AD_DATE_CREATE, cdate);
1000 case FILPBIT_BDATE :
1001 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1003 case FILPBIT_FINFO :
1004 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1006 case FILPBIT_UNIXPR :
1008 setfilunixmode(vol, path, upriv);
1011 case FILPBIT_PDINFO :
1012 if (obj->afp_version < 30) { /* else it's UTF8 name */
1013 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1014 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1019 err = AFPERR_BITMAP;
1020 goto setfilparam_done;
1027 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1028 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1032 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1033 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1039 ad_close(adp, ADFLAGS_HF);
1042 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1043 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1044 bitmap = 1<<FILPBIT_MDATE;
1045 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1049 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1055 * renamefile and copyfile take the old and new unix pathnames
1056 * and the new mac name.
1058 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1059 * passing -1 means this is not used, src path is a full path
1060 * src the source path
1061 * dst the dest filename in current dir
1062 * newname the dest mac name
1063 * adp adouble struct of src file, if open, or & zeroed one
1066 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1070 LOG(log_debug, logtype_afpd,
1071 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1073 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1076 return( AFPERR_NOOBJ );
1079 return( AFPERR_ACCESS );
1081 return AFPERR_VLOCK;
1082 case EXDEV : /* Cross device move -- try copy */
1083 /* NOTE: with open file it's an error because after the copy we will
1084 * get two files, it's fixable for our process (eg reopen the new file, get the
1085 * locks, and so on. But it doesn't solve the case with a second process
1087 if (adp->ad_open_forks) {
1088 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1089 return AFPERR_OLOCK; /* little lie */
1091 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1092 /* on error copyfile delete dest */
1095 return deletefile(vol, sdir_fd, src, 0);
1097 return( AFPERR_PARAM );
1101 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1105 /* try to undo the data fork rename,
1106 * we know we are on the same device
1109 unix_rename(-1, dst, sdir_fd, src );
1110 /* return the first error */
1113 return AFPERR_NOOBJ;
1116 return AFPERR_ACCESS ;
1118 return AFPERR_VLOCK;
1120 return AFPERR_PARAM ;
1125 /* don't care if we can't open the newly renamed ressource fork */
1126 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1127 ad_setname(adp, newname);
1129 ad_close( adp, ADFLAGS_HF );
1136 convert a Mac long name to an utf8 name,
1138 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1142 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1148 /* ---------------- */
1149 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1156 if ( type != 2 && !(vol->v_obj->afp_version >= 30 && type == 3) ) {
1162 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1163 if (vol->v_obj->afp_version >= 30) {
1164 /* convert it to UTF8
1166 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1170 strncpy( newname, ibuf, plen );
1171 newname[ plen ] = '\0';
1173 if (strlen(newname) != plen) {
1174 /* there's \0 in newname, e.g. it's a pathname not
1182 memcpy(&hint, ibuf, sizeof(hint));
1183 ibuf += sizeof(hint);
1185 memcpy(&len16, ibuf, sizeof(len16));
1186 ibuf += sizeof(len16);
1187 plen = ntohs(len16);
1190 if (plen > AFPOBJ_TMPSIZ) {
1193 strncpy( newname, ibuf, plen );
1194 newname[ plen ] = '\0';
1195 if (strlen(newname) != plen) {
1204 /* -----------------------------------
1206 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1208 struct vol *s_vol, *d_vol;
1210 char *newname, *p, *upath;
1211 struct path *s_path;
1212 uint32_t sdid, ddid;
1213 int err, retvalue = AFP_OK;
1214 uint16_t svid, dvid;
1216 struct adouble ad, *adp;
1222 memcpy(&svid, ibuf, sizeof( svid ));
1223 ibuf += sizeof( svid );
1224 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1225 return( AFPERR_PARAM );
1228 memcpy(&sdid, ibuf, sizeof( sdid ));
1229 ibuf += sizeof( sdid );
1230 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1234 memcpy(&dvid, ibuf, sizeof( dvid ));
1235 ibuf += sizeof( dvid );
1236 memcpy(&ddid, ibuf, sizeof( ddid ));
1237 ibuf += sizeof( ddid );
1239 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1240 return get_afp_errno(AFPERR_PARAM);
1242 if ( path_isadir(s_path) ) {
1243 return( AFPERR_BADTYPE );
1246 /* don't allow copies when the file is open.
1247 * XXX: the spec only calls for read/deny write access.
1248 * however, copyfile doesn't have any of that info,
1249 * and locks need to stay coherent. as a result,
1250 * we just balk if the file is opened already. */
1252 adp = of_ad(s_vol, s_path, &ad);
1254 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1255 return AFPERR_DENYCONF;
1257 #ifdef HAVE_FSHARE_T
1259 shmd.f_access = F_RDACC;
1260 shmd.f_deny = F_NODNY;
1261 if (fcntl(ad_data_fileno(adp), F_SHARE, &shmd) != 0) {
1262 retvalue = AFPERR_DENYCONF;
1265 if (AD_RSRC_OPEN(adp) && fcntl(ad_reso_fileno(adp), F_SHARE, &shmd) != 0) {
1266 retvalue = AFPERR_DENYCONF;
1270 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1271 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1274 retvalue = AFPERR_DENYCONF;
1278 newname = obj->newtmp;
1279 strcpy( newname, s_path->m_name );
1281 p = ctoupath( s_vol, curdir, newname );
1283 retvalue = AFPERR_PARAM;
1287 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1288 retvalue = AFPERR_PARAM;
1292 if (d_vol->v_flags & AFPVOL_RO) {
1293 retvalue = AFPERR_VLOCK;
1297 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1298 retvalue = afp_errno;
1302 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1303 retvalue = get_afp_errno(AFPERR_NOOBJ);
1307 if ( *s_path->m_name != '\0' ) {
1308 retvalue =path_error(s_path, AFPERR_NOOBJ);
1312 /* one of the handful of places that knows about the path type */
1313 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1314 retvalue = AFPERR_PARAM;
1317 /* newname is always only a filename so curdir *is* its
1320 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding(d_vol->v_obj)))) {
1321 retvalue =AFPERR_PARAM;
1325 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1331 setvoltime(obj, d_vol );
1334 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1338 /* ----------------------------------
1339 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1340 * because we are doing it elsewhere.
1341 * currently if newname is NULL then adp is NULL.
1343 int copyfile(const struct vol *s_vol,
1344 const struct vol *d_vol,
1349 struct adouble *adp)
1351 struct adouble ads, add;
1358 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1359 sfd, src, dst, newname);
1362 ad_init(&ads, s_vol);
1366 adflags = ADFLAGS_DF | ADFLAGS_RF | ADFLAGS_NORF;
1368 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1373 if (!AD_RSRC_OPEN(adp))
1374 /* no resource fork, don't create one for dst file */
1375 adflags &= ~ADFLAGS_RF;
1377 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1379 if (stat_result < 0) {
1380 /* unlikely but if fstat fails, the default file mode will be 0666. */
1381 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1384 ad_init(&add, d_vol);
1385 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1387 ad_close( adp, adflags );
1388 if (EEXIST != ret_err) {
1389 deletefile(d_vol, -1, dst, 0);
1392 return AFPERR_EXIST;
1395 if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1396 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1399 if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1400 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1405 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1406 /* set the new name in the resource fork */
1407 ad_copy_header(&add, adp);
1408 ad_setname(&add, newname);
1411 ad_close( adp, adflags );
1413 if (ad_close( &add, adflags ) <0) {
1418 deletefile(d_vol, -1, dst, 0);
1420 else if (stat_result == 0) {
1421 /* set dest modification date to src date */
1424 ut.actime = ut.modtime = st.st_mtime;
1426 /* FIXME netatalk doesn't use resource fork file date
1427 * but maybe we should set its modtime too.
1432 switch ( ret_err ) {
1438 LOG(log_info, logtype_afpd, "copyfile: DISK FULL");
1439 return AFPERR_DFULL;
1441 return AFPERR_NOOBJ;
1443 return AFPERR_ACCESS;
1445 return AFPERR_VLOCK;
1447 return AFPERR_PARAM;
1451 /* -----------------------------------
1452 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1453 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1455 when deletefile is called we don't have lock on it, file is closed (for us)
1456 untrue if called by renamefile
1458 ad_open always try to open file RDWR first and ad_lock takes care of
1459 WRITE lock on read only file.
1462 static int check_attrib(struct adouble *adp)
1464 uint16_t bshort = 0;
1466 ad_getattr(adp, &bshort);
1468 * Does kFPDeleteInhibitBit (bit 8) set?
1470 if ((bshort & htons(ATTRBIT_NODELETE))) {
1471 return AFPERR_OLOCK;
1473 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1479 * dirfd can be used for unlinkat semantics
1481 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1484 struct adouble *adp = NULL;
1485 int adflags, err = AFP_OK;
1488 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1492 /* was EACCESS error try to get only metadata */
1493 /* we never want to create a resource fork here, we are going to delete it
1494 * moreover sometimes deletefile is called with a no existent file and
1495 * ad_open would create a 0 byte resource fork
1497 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1498 if ((err = check_attrib(&ad))) {
1499 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1506 /* try to open both forks at once */
1507 adflags = ADFLAGS_DF;
1508 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1513 case EACCES: /* maybe it's a file with no write mode for us */
1514 break; /* was return AFPERR_ACCESS;*/
1527 if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1528 adflags |= ADFLAGS_RF;
1529 /* FIXME we have a pb here because we want to know if a file is open
1530 * there's a 'priority inversion' if you can't open the ressource fork RW
1531 * you can delete it if it's open because you can't get a write lock.
1533 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1536 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1538 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1544 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1545 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1547 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1549 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1550 cnid_delete(vol->v_cdb, id);
1556 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1559 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1564 /* ------------------------------------ */
1565 /* return a file id */
1566 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1575 struct path *s_path;
1581 memcpy(&vid, ibuf, sizeof(vid));
1582 ibuf += sizeof(vid);
1584 if (NULL == ( vol = getvolbyvid( vid )) ) {
1585 return( AFPERR_PARAM);
1588 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1592 if (vol->v_flags & AFPVOL_RO)
1593 return AFPERR_VLOCK;
1595 memcpy(&did, ibuf, sizeof( did ));
1596 ibuf += sizeof(did);
1598 if (NULL == ( dir = dirlookup( vol, did )) ) {
1599 return afp_errno; /* was AFPERR_PARAM */
1602 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1603 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1606 if ( path_isadir(s_path) ) {
1607 return( AFPERR_BADTYPE );
1610 upath = s_path->u_name;
1611 switch (s_path->st_errno) {
1613 break; /* success */
1616 return AFPERR_ACCESS;
1618 return AFPERR_NOOBJ;
1620 return AFPERR_PARAM;
1623 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1624 memcpy(rbuf, &id, sizeof(id));
1625 *rbuflen = sizeof(id);
1626 return AFPERR_EXISTID;
1629 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1630 memcpy(rbuf, &id, sizeof(id));
1631 *rbuflen = sizeof(id);
1638 /* ------------------------------- */
1644 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1647 struct reenum *param = data;
1648 struct vol *vol = param->vol;
1649 cnid_t did = param->did;
1652 if ( lstat(de->d_name, &path.st) < 0 )
1655 /* update or add to cnid */
1656 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1661 /* --------------------
1662 * Ok the db is out of synch with the dir.
1663 * but if it's a deleted file we don't want to do it again and again.
1666 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1672 if (vol->v_cdb == NULL) {
1676 /* FIXME use of_statdir ? */
1677 if (lstat(name, &st)) {
1681 if (dirreenumerate(dir, &st)) {
1682 /* we already did it once and the dir haven't been modified */
1683 return dir->d_offcnt;
1687 data.did = dir->d_did;
1688 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1689 setdiroffcnt(curdir, &st, ret);
1690 dir->d_flags |= DIRF_CNID;
1696 /* ------------------------------
1697 resolve a file id */
1698 int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1707 uint16_t vid, bitmap;
1709 static char buffer[12 + MAXPATHLEN + 1];
1710 int len = 12 + MAXPATHLEN + 1;
1715 memcpy(&vid, ibuf, sizeof(vid));
1716 ibuf += sizeof(vid);
1718 if (NULL == ( vol = getvolbyvid( vid )) ) {
1719 return( AFPERR_PARAM);
1722 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1726 memcpy(&id, ibuf, sizeof( id ));
1731 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1735 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1736 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1739 if (NULL == ( dir = dirlookup( vol, id )) ) {
1740 return AFPERR_NOID; /* idem AFPERR_PARAM */
1742 if (movecwd(vol, dir) < 0) {
1746 return AFPERR_ACCESS;
1750 return AFPERR_PARAM;
1754 memset(&path, 0, sizeof(path));
1755 path.u_name = upath;
1756 if ( of_stat(&path) < 0 ) {
1758 /* with nfs and our working directory is deleted */
1759 if (errno == ESTALE) {
1763 if ( errno == ENOENT && !retry) {
1764 /* cnid db is out of sync, reenumerate the directory and update ids */
1765 reenumerate_id(vol, ".", dir);
1773 return AFPERR_ACCESS;
1777 return AFPERR_PARAM;
1781 /* directories are bad */
1782 if (S_ISDIR(path.st.st_mode)) {
1783 /* OS9 and OSX don't return the same error code */
1784 return (obj->afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1787 memcpy(&bitmap, ibuf, sizeof(bitmap));
1788 bitmap = ntohs( bitmap );
1789 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding(obj)))) {
1793 if (AFP_OK != (err = getfilparams(obj, vol, bitmap, &path , curdir,
1794 rbuf + sizeof(bitmap), &buflen, 0))) {
1797 *rbuflen = buflen + sizeof(bitmap);
1798 memcpy(rbuf, ibuf, sizeof(bitmap));
1803 /* ------------------------------ */
1804 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1814 static char buffer[12 + MAXPATHLEN + 1];
1815 int len = 12 + MAXPATHLEN + 1;
1820 memcpy(&vid, ibuf, sizeof(vid));
1821 ibuf += sizeof(vid);
1823 if (NULL == ( vol = getvolbyvid( vid )) ) {
1824 return( AFPERR_PARAM);
1827 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1831 if (vol->v_flags & AFPVOL_RO)
1832 return AFPERR_VLOCK;
1834 memcpy(&id, ibuf, sizeof( id ));
1838 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1842 if (NULL == ( dir = dirlookup( vol, id )) ) {
1843 if (afp_errno == AFPERR_NOOBJ) {
1847 return( AFPERR_PARAM );
1851 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1855 return AFPERR_ACCESS;
1860 /* still try to delete the id */
1864 return AFPERR_PARAM;
1867 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1868 return AFPERR_BADTYPE;
1871 if (cnid_delete(vol->v_cdb, fileid)) {
1874 return AFPERR_VLOCK;
1877 return AFPERR_ACCESS;
1879 return AFPERR_PARAM;
1886 /* ------------------------------ */
1887 static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
1891 if (path->st_errno) {
1892 switch (path->st_errno) {
1894 afp_errno = AFPERR_NOID;
1898 afp_errno = AFPERR_ACCESS;
1901 afp_errno = AFPERR_PARAM;
1906 /* we use file_access both for legacy Mac perm and
1907 * for unix privilege, rename will take care of folder perms
1909 if (file_access(obj, vol, path, OPENACC_WR ) < 0) {
1910 afp_errno = AFPERR_ACCESS;
1914 if ((*of = of_findname(path))) {
1915 /* reuse struct adouble so it won't break locks */
1919 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
1921 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1923 * The user must have the Read & Write privilege for both files in order to use this command.
1925 ad_close(adp, ADFLAGS_HF);
1926 afp_errno = AFPERR_ACCESS;
1933 #define APPLETEMP ".AppleTempXXXXXX"
1935 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1937 struct stat srcst, destst;
1939 struct dir *dir, *sdir;
1940 char *spath, temp[17], *p;
1941 char *supath, *upath;
1946 struct adouble *adsp = NULL;
1947 struct adouble *addp = NULL;
1948 struct ofork *s_of = NULL;
1949 struct ofork *d_of = NULL;
1962 memcpy(&vid, ibuf, sizeof(vid));
1963 ibuf += sizeof(vid);
1965 if (NULL == ( vol = getvolbyvid( vid )) ) {
1966 return( AFPERR_PARAM);
1969 if ((vol->v_flags & AFPVOL_RO))
1970 return AFPERR_VLOCK;
1972 /* source and destination dids */
1973 memcpy(&sid, ibuf, sizeof(sid));
1974 ibuf += sizeof(sid);
1975 memcpy(&did, ibuf, sizeof(did));
1976 ibuf += sizeof(did);
1979 if (NULL == (dir = dirlookup( vol, sid )) ) {
1980 return afp_errno; /* was AFPERR_PARAM */
1983 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1984 return get_afp_errno(AFPERR_NOOBJ);
1987 if ( path_isadir(path) ) {
1988 return AFPERR_BADTYPE; /* it's a dir */
1991 /* save some stuff */
1994 spath = obj->oldtmp;
1995 supath = obj->newtmp;
1996 strcpy(spath, path->m_name);
1997 strcpy(supath, path->u_name); /* this is for the cnid changing */
1998 p = absupath( vol, sdir, supath);
2000 /* pathname too long */
2001 return AFPERR_PARAM ;
2005 if (!(adsp = find_adouble(obj, vol, path, &s_of, &ads))) {
2009 /* ***** from here we may have resource fork open **** */
2011 /* look for the source cnid. if it doesn't exist, don't worry about
2013 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2015 if (NULL == ( dir = dirlookup( vol, did )) ) {
2016 err = afp_errno; /* was AFPERR_PARAM */
2017 goto err_exchangefile;
2020 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2021 err = get_afp_errno(AFPERR_NOOBJ);
2022 goto err_exchangefile;
2025 if ( path_isadir(path) ) {
2026 err = AFPERR_BADTYPE;
2027 goto err_exchangefile;
2030 /* FPExchangeFiles is the only call that can return the SameObj
2032 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2033 err = AFPERR_SAMEOBJ;
2034 goto err_exchangefile;
2038 if (!(addp = find_adouble(obj, vol, path, &d_of, &add))) {
2040 goto err_exchangefile;
2044 /* they are not on the same device and at least one is open
2045 * FIXME broken for for crossdev and adouble v2
2048 crossdev = (srcst.st_dev != destst.st_dev);
2049 if (/* (d_of || s_of) && */ crossdev) {
2051 goto err_exchangefile;
2054 /* look for destination id. */
2055 upath = path->u_name;
2056 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2058 /* construct a temp name.
2059 * NOTE: the temp file will be in the dest file's directory. it
2060 * will also be inaccessible from AFP. */
2061 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2062 if (!mktemp(temp)) {
2064 goto err_exchangefile;
2068 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2069 ad_close(adsp, ADFLAGS_HF);
2070 ad_close(addp, ADFLAGS_HF);
2073 /* now, quickly rename the file. we error if we can't. */
2074 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2075 goto err_exchangefile;
2076 of_rename(vol, s_of, sdir, spath, curdir, temp);
2078 /* rename destination to source */
2079 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2080 goto err_src_to_tmp;
2081 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2083 /* rename temp to destination */
2084 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2085 goto err_dest_to_src;
2086 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2088 /* id's need switching. src -> dest and dest -> src.
2089 * we need to re-stat() if it was a cross device copy.
2092 cnid_delete(vol->v_cdb, sid);
2094 cnid_delete(vol->v_cdb, did);
2096 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2097 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2099 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2100 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2105 err = AFPERR_ACCESS;
2110 goto err_temp_to_dest;
2113 /* here we need to reopen if crossdev */
2114 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2119 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2124 /* change perms, src gets dest perm and vice versa */
2129 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2130 err = AFP_OK; /* ignore error */
2131 goto err_temp_to_dest;
2135 * we need to exchange ACL entries as well
2137 /* exchange_acls(vol, p, upath); */
2142 path->m_name = NULL;
2143 path->u_name = upath;
2145 setfilunixmode(vol, path, destst.st_mode);
2146 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2153 setfilunixmode(vol, path, srcst.st_mode);
2154 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2156 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2157 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2162 goto err_exchangefile;
2164 /* all this stuff is so that we can unwind a failed operation
2167 /* rename dest to temp */
2168 renamefile(vol, -1, upath, temp, temp, adsp);
2169 of_rename(vol, s_of, curdir, upath, curdir, temp);
2172 /* rename source back to dest */
2173 renamefile(vol, -1, p, upath, path->m_name, addp);
2174 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2177 /* rename temp back to source */
2178 renamefile(vol, -1, temp, p, spath, adsp);
2179 of_rename(vol, s_of, curdir, temp, sdir, spath);
2182 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2183 ad_close(adsp, ADFLAGS_HF);
2185 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2186 ad_close(addp, ADFLAGS_HF);
2190 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2191 (void)dir_remove(vol, cached);
2192 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2193 (void)dir_remove(vol, cached);