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)
82 void *ad_finder = NULL;
86 ad_finder = ad_entry(adp, ADEID_FINDERI);
89 memcpy(data, ad_finder, ADEDLEN_FINDERI);
91 if (default_type(ad_finder))
95 memcpy(data, ufinderi, ADEDLEN_FINDERI);
96 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
99 ashort = htons(FINDERINFO_INVISIBLE);
100 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
106 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
107 linkflag |= htons(FINDERINFO_ISALIAS);
108 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
109 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
110 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));
122 /* ---------------------
124 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, uint32_t utf8)
129 aint = strlen( name );
133 if (utf8_encoding(vol->v_obj)) {
134 /* but name is an utf8 mac name */
137 /* global static variable... */
139 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
148 if (aint > MACFILELEN)
155 if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
156 aint = UTF8FILELEN_EARLY;
158 utf8 = vol->v_kTextEncoding;
159 memcpy(data, &utf8, sizeof(utf8));
160 data += sizeof(utf8);
163 memcpy(data, &temp, sizeof(temp));
164 data += sizeof(temp);
167 memcpy( data, src, aint );
177 * FIXME: PDINFO is UTF8 and doesn't need adp
179 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
180 (1 << FILPBIT_CDATE) |\
181 (1 << FILPBIT_MDATE) |\
182 (1 << FILPBIT_BDATE) |\
183 (1 << FILPBIT_FINFO) |\
184 (1 << FILPBIT_RFLEN) |\
185 (1 << FILPBIT_EXTRFLEN) |\
186 (1 << FILPBIT_PDINFO) |\
187 (1 << FILPBIT_FNUM) |\
188 (1 << FILPBIT_UNIXPR)))
191 * @brief Get CNID for did/upath args both from database and adouble file
193 * 1. Get the objects CNID as stored in its adouble file
194 * 2. Get the objects CNID from the database
195 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
196 * 4. In case 2 and 3 differ, store 3 in the adouble file
198 * @param vol (rw) volume
199 * @param adp (rw) adouble struct of object upath, might be NULL
200 * @param st (r) stat of upath, must NOT be NULL
201 * @param did (r) parent CNID of upath
202 * @param upath (r) name of object
203 * @param len (r) strlen of upath
205 uint32_t get_id(struct vol *vol,
207 const struct stat *st,
212 static int first = 1; /* mark if this func is called the first time */
214 uint32_t dbcnid = CNID_INVALID;
217 if (vol->v_cdb != NULL) {
218 /* prime aint with what we think is the cnid, set did to zero for
219 catching moved files */
220 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
222 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
223 /* Throw errors if cnid_add fails. */
224 if (dbcnid == CNID_INVALID) {
226 case CNID_ERR_CLOSE: /* the db is closed */
229 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
230 afp_errno = AFPERR_PARAM;
233 afp_errno = AFPERR_PARAM;
236 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
237 /* we have to do it here for "dbd" because it uses "lazy opening" */
238 /* In order to not end in a loop somehow with goto restart below */
240 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
241 cnid_close(vol->v_cdb);
242 free(vol->v_cnidscheme);
243 vol->v_cnidscheme = strdup("tdb");
245 int flags = CNID_FLAG_MEMORY;
246 if ((vol->v_flags & AFPVOL_NODEV)) {
247 flags |= CNID_FLAG_NODEV;
249 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
251 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
253 if (!(vol->v_flags & AFPVOL_TM)) {
254 vol->v_flags |= AFPVOL_RO;
255 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
256 "Check server messages for details. Switching to read-only mode.");
257 kill(getpid(), SIGUSR2);
259 goto restart; /* now try again with the temp CNID db */
261 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
262 "Check server messages for details, can't recover from this state!");
265 afp_errno = AFPERR_MISC;
269 else if (adp && adcnid && (adcnid != dbcnid)) { /* 4 */
270 /* Update the ressource fork. For a folder adp is always null */
271 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
272 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
273 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
274 if (ad_flush(adp) != 0)
275 LOG(log_error, logtype_afpd, "get_id(\"%s\"): can't flush", fullpathname(upath));
285 /* -------------------------- */
286 int getmetadata(const AFPObj *obj,
289 struct path *path, struct dir *dir,
290 char *buf, size_t *buflen, struct adouble *adp)
292 char *data, *l_nameoff = NULL, *upath;
293 char *utf_nameoff = NULL;
298 u_char achar, fdType[4];
303 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
305 upath = path->u_name;
309 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
310 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding(obj)) /* FIXME should be m_name utf8 filename */
311 || (bitmap & (1 << FILPBIT_FNUM))) {
314 struct dir *cachedfile;
315 int len = strlen(upath);
316 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
317 id = cachedfile->d_did;
319 id = get_id(vol, adp, st, dir->d_did, upath, len);
321 /* Add it to the cache */
322 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
323 ntohl(dir->d_did), upath, ntohl(id));
325 /* Get macname from unixname first */
326 if (path->m_name == NULL) {
327 if ((path->m_name = utompath(vol, upath, id, utf8_encoding(obj))) == NULL) {
328 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
334 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
335 || (bconchar(fullpath, '/') != BSTR_OK)
336 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
337 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
341 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
342 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
346 if ((dircache_add(vol, cachedfile)) != 0) {
347 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
355 if (id == CNID_INVALID)
359 path->m_name = utompath(vol, upath, id, utf8_encoding(vol->v_obj));
362 while ( bitmap != 0 ) {
363 while (( bitmap & 1 ) == 0 ) {
371 ad_getattr(adp, &ashort);
372 } else if (vol_inv_dots(vol) && *upath == '.') {
373 ashort = htons(ATTRBIT_INVISIBLE);
377 /* FIXME do we want a visual clue if the file is read only
380 accessmode(vol, ".", &ma, dir , NULL);
381 if ((ma.ma_user & AR_UWRITE)) {
382 accessmode(vol, upath, &ma, dir , st);
383 if (!(ma.ma_user & AR_UWRITE)) {
384 ashort |= htons(ATTRBIT_NOWRITE);
388 memcpy(data, &ashort, sizeof( ashort ));
389 data += sizeof( ashort );
390 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
391 path->u_name, ntohs(ashort));
395 memcpy(data, &dir->d_did, sizeof( uint32_t ));
396 data += sizeof( uint32_t );
397 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
398 path->u_name, ntohl(dir->d_did));
402 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
403 aint = AD_DATE_FROM_UNIX(st->st_mtime);
404 memcpy(data, &aint, sizeof( aint ));
405 data += sizeof( aint );
409 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
410 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
411 aint = AD_DATE_FROM_UNIX(st->st_mtime);
414 aint = AD_DATE_FROM_UNIX(st->st_mtime);
416 memcpy(data, &aint, sizeof( int ));
417 data += sizeof( int );
421 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
422 aint = AD_DATE_START;
423 memcpy(data, &aint, sizeof( int ));
424 data += sizeof( int );
428 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
429 data += ADEDLEN_FINDERI;
434 data += sizeof( uint16_t );
438 memset(data, 0, sizeof(uint16_t));
439 data += sizeof( uint16_t );
443 memcpy(data, &id, sizeof( id ));
444 data += sizeof( id );
445 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
446 path->u_name, ntohl(id));
450 if (st->st_size > 0xffffffff)
453 aint = htonl( st->st_size );
454 memcpy(data, &aint, sizeof( aint ));
455 data += sizeof( aint );
460 if (adp->ad_rlen > 0xffffffff)
463 aint = htonl( adp->ad_rlen);
467 memcpy(data, &aint, sizeof( aint ));
468 data += sizeof( aint );
471 /* Current client needs ProDOS info block for this file.
472 Use simple heuristic and let the Mac "type" string tell
473 us what the PD file code should be. Everything gets a
474 subtype of 0x0000 unless the original value was hashed
475 to "pXYZ" when we created it. See IA, Ver 2.
476 <shirsch@adelphia.net> */
477 case FILPBIT_PDINFO :
478 if (obj->afp_version >= 30) { /* UTF8 name */
479 utf8 = kTextEncodingUTF8;
481 data += sizeof( uint16_t );
483 memcpy(data, &aint, sizeof( aint ));
484 data += sizeof( aint );
488 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
490 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
494 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
498 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
502 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
506 else if ( fdType[0] == 'p' ) {
508 ashort = (fdType[2] * 256) + fdType[3];
522 memcpy(data, &ashort, sizeof( ashort ));
523 data += sizeof( ashort );
524 memset(data, 0, sizeof( ashort ));
525 data += sizeof( ashort );
528 case FILPBIT_EXTDFLEN:
529 aint = htonl(st->st_size >> 32);
530 memcpy(data, &aint, sizeof( aint ));
531 data += sizeof( aint );
532 aint = htonl(st->st_size);
533 memcpy(data, &aint, sizeof( aint ));
534 data += sizeof( aint );
536 case FILPBIT_EXTRFLEN:
539 aint = htonl(adp->ad_rlen >> 32);
540 memcpy(data, &aint, sizeof( aint ));
541 data += sizeof( aint );
543 aint = htonl(adp->ad_rlen);
544 memcpy(data, &aint, sizeof( aint ));
545 data += sizeof( aint );
547 case FILPBIT_UNIXPR :
548 /* accessmode may change st_mode with ACLs */
549 accessmode(obj, vol, upath, &ma, dir , st);
551 aint = htonl(st->st_uid);
552 memcpy( data, &aint, sizeof( aint ));
553 data += sizeof( aint );
554 aint = htonl(st->st_gid);
555 memcpy( data, &aint, sizeof( aint ));
556 data += sizeof( aint );
559 type == slnk indicates an OSX style symlink,
560 we have to add S_IFLNK to the mode, otherwise
561 10.3 clients freak out. */
565 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
566 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
572 memcpy( data, &aint, sizeof( aint ));
573 data += sizeof( aint );
575 *data++ = ma.ma_user;
576 *data++ = ma.ma_world;
577 *data++ = ma.ma_group;
578 *data++ = ma.ma_owner;
582 return( AFPERR_BITMAP );
588 ashort = htons( data - buf );
589 memcpy(l_nameoff, &ashort, sizeof( ashort ));
590 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
593 ashort = htons( data - buf );
594 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
595 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
597 *buflen = data - buf;
601 /* ----------------------- */
602 int getfilparams(const AFPObj *obj, struct vol *vol, uint16_t bitmap, struct path *path,
603 struct dir *dir, char *buf, size_t *buflen, int in_enumerate)
605 struct adouble ad, *adp;
610 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
612 opened = PARAM_NEED_ADP(bitmap);
618 * Dont check for and resturn open fork attributes when enumerating
619 * This saves a lot of syscalls, the client will hopefully only use the result
620 * in FPGetFileParms where we return the correct value
622 flags = (!in_enumerate &&(bitmap & (1 << FILPBIT_ATTR))) ? ADFLAGS_CHECK_OF : 0;
624 adp = of_ad(vol, path, &ad);
625 upath = path->u_name;
627 if ( ad_metadata( upath, flags, adp) < 0 ) {
630 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
631 upath, strerror(errno));
632 return AFPERR_ACCESS;
634 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
643 rc = getmetadata(obj, vol, bitmap, path, dir, buf, buflen, adp);
644 ad_close(adp, ADFLAGS_HF | flags);
649 /* ----------------------------- */
650 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
655 struct ofork *of = NULL;
657 int creatf, did, openf, retvalue = AFP_OK;
663 creatf = (unsigned char) *ibuf++;
665 memcpy(&vid, ibuf, sizeof( vid ));
666 ibuf += sizeof( vid );
668 if (NULL == ( vol = getvolbyvid( vid )) )
669 return( AFPERR_PARAM );
671 if (vol->v_flags & AFPVOL_RO)
674 memcpy(&did, ibuf, sizeof( did));
675 ibuf += sizeof( did );
677 if (NULL == ( dir = dirlookup( vol, did )) )
680 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
681 return get_afp_errno(AFPERR_PARAM);
682 if ( *s_path->m_name == '\0' )
683 return( AFPERR_BADTYPE );
685 upath = s_path->u_name;
688 /* if upath is deleted we already in trouble anyway */
689 if ((of = of_findname(s_path))) {
697 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
699 /* on a soft create, if the file is open then ad_open won't fail
700 because open syscall is not called */
701 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
703 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
707 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
708 return ( AFPERR_NOOBJ );
710 return( AFPERR_EXIST );
712 return( AFPERR_ACCESS );
715 LOG(log_info, logtype_afpd, "afp_createfile: DISK FULL");
716 return( AFPERR_DFULL );
718 return( AFPERR_PARAM );
721 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
722 /* FIXME with hard create on an existing file, we already
723 * corrupted the data file.
725 netatalk_unlink( upath );
726 ad_close( &ad, ADFLAGS_DF );
727 return AFPERR_ACCESS;
730 path = s_path->m_name;
731 ad_setname(&ad, path);
734 if (lstat(upath, &st) != 0) {
735 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
736 upath, strerror(errno));
737 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
742 if ((id = get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath))) == CNID_INVALID) {
743 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
744 goto createfile_iderr;
746 (void)ad_setid(&ad, st.st_dev, st.st_ino, id, dir->d_did, vol->v_stamp);
750 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
751 fce_register(FCE_FILE_CREATE, fullpathname(upath), NULL, fce_file);
756 setvoltime(obj, vol );
761 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
767 uint16_t vid, bitmap;
772 memcpy(&vid, ibuf, sizeof( vid ));
773 ibuf += sizeof( vid );
774 if (NULL == ( vol = getvolbyvid( vid )) ) {
775 return( AFPERR_PARAM );
778 if (vol->v_flags & AFPVOL_RO)
781 memcpy(&did, ibuf, sizeof( did ));
782 ibuf += sizeof( did );
783 if (NULL == ( dir = dirlookup( vol, did )) ) {
784 return afp_errno; /* was AFPERR_NOOBJ */
787 memcpy(&bitmap, ibuf, sizeof( bitmap ));
788 bitmap = ntohs( bitmap );
789 ibuf += sizeof( bitmap );
791 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
792 return get_afp_errno(AFPERR_PARAM);
795 if (path_isadir(s_path)) {
796 return( AFPERR_BADTYPE ); /* it's a directory */
799 if ( s_path->st_errno != 0 ) {
800 return( AFPERR_NOOBJ );
803 if ((u_long)ibuf & 1 ) {
807 if (AFP_OK == ( rc = setfilparams(obj, vol, s_path, bitmap, ibuf )) ) {
808 setvoltime(obj, vol );
815 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
818 extern struct path Cur_Path;
820 int setfilparams(const AFPObj *obj, struct vol *vol,
821 struct path *path, uint16_t f_bitmap, char *buf )
823 struct adouble ad, *adp;
825 int bit, isad = 1, err = AFP_OK;
827 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
828 uint16_t ashort, bshort, oshort;
831 uint16_t upriv_bit = 0;
833 int change_mdate = 0;
834 int change_parent_mdate = 0;
839 uint16_t bitmap = f_bitmap;
840 uint32_t cdate,bdate;
841 u_char finder_buf[32];
842 int symlinked = S_ISLNK(path->st.st_mode);
845 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
848 adp = of_ad(vol, path, &ad);
849 upath = path->u_name;
851 if (!vol_unix_priv(vol) && check_access(obj, vol, upath, OPENACC_WR ) < 0) {
852 return AFPERR_ACCESS;
855 /* with unix priv maybe we have to change adouble file priv first */
857 while ( bitmap != 0 ) {
858 while (( bitmap & 1 ) == 0 ) {
865 memcpy(&ashort, buf, sizeof( ashort ));
866 buf += sizeof( ashort );
870 memcpy(&cdate, buf, sizeof(cdate));
871 buf += sizeof( cdate );
874 memcpy(&newdate, buf, sizeof( newdate ));
875 buf += sizeof( newdate );
879 memcpy(&bdate, buf, sizeof( bdate));
880 buf += sizeof( bdate );
884 memcpy(finder_buf, buf, 32 );
885 if (memcmp(buf, "slnkrhap", 8) == 0 && !S_ISLNK(path->st.st_mode)) {
889 char buf[PATH_MAX+1];
890 if ((fp = open(path->u_name, O_RDONLY)) >= 0) {
891 if ((len = read(fp, buf, PATH_MAX+1))) {
892 if (unlink(path->u_name) == 0) {
894 erc = symlink(buf, path->u_name);
903 goto setfilparam_done;
909 case FILPBIT_UNIXPR :
910 if (!vol_unix_priv(vol)) {
911 /* this volume doesn't use unix priv */
917 change_parent_mdate = 1;
919 memcpy( &aint, buf, sizeof( aint ));
920 f_uid = ntohl (aint);
921 buf += sizeof( aint );
922 memcpy( &aint, buf, sizeof( aint ));
923 f_gid = ntohl (aint);
924 buf += sizeof( aint );
925 setfilowner(vol, f_uid, f_gid, path);
927 memcpy( &upriv, buf, sizeof( upriv ));
928 buf += sizeof( upriv );
929 upriv = ntohl (upriv);
930 if ((upriv & S_IWUSR)) {
931 setfilunixmode(vol, path, upriv);
938 case FILPBIT_PDINFO :
939 if (obj->afp_version < 30) { /* else it's UTF8 name */
942 /* Keep special case to support crlf translations */
943 if ((unsigned int) achar == 0x04) {
944 fdType = (u_char *)"TEXT";
947 xyy[0] = ( u_char ) 'p';
958 /* break while loop */
967 /* second try with adouble open
969 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
970 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
972 * For some things, we don't need an adouble header:
973 * - change of modification date
974 * - UNIX privs (Bug-ID #2863424)
976 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
977 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
978 return AFPERR_ACCESS;
980 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
982 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
983 ad_setname(adp, path->m_name);
985 if ((id = get_id(vol, adp, &path->st, curdir->d_did, upath, strlen(upath))) == CNID_INVALID) {
986 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
989 (void)ad_setid(adp, path->st.st_dev, path->st.st_ino, id, curdir->d_did, vol->v_stamp);
994 while ( bitmap != 0 ) {
995 while (( bitmap & 1 ) == 0 ) {
1002 ad_getattr(adp, &bshort);
1004 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1005 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1009 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1010 change_parent_mdate = 1;
1011 ad_setattr(adp, bshort);
1013 case FILPBIT_CDATE :
1014 ad_setdate(adp, AD_DATE_CREATE, cdate);
1016 case FILPBIT_MDATE :
1018 case FILPBIT_BDATE :
1019 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1021 case FILPBIT_FINFO :
1022 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1024 ((em = getextmap( path->m_name )) &&
1025 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1026 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1027 || ((em = getdefextmap()) &&
1028 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1029 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1031 memcpy(finder_buf, ufinderi, 8 );
1033 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1035 case FILPBIT_UNIXPR :
1037 setfilunixmode(vol, path, upriv);
1040 case FILPBIT_PDINFO :
1041 if (obj->afp_version < 30) { /* else it's UTF8 name */
1042 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1043 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1048 err = AFPERR_BITMAP;
1049 goto setfilparam_done;
1056 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1057 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1061 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1062 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1068 ad_close(adp, ADFLAGS_HF);
1071 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1072 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1073 bitmap = 1<<FILPBIT_MDATE;
1074 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1078 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1084 * renamefile and copyfile take the old and new unix pathnames
1085 * and the new mac name.
1087 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1088 * passing -1 means this is not used, src path is a full path
1089 * src the source path
1090 * dst the dest filename in current dir
1091 * newname the dest mac name
1092 * adp adouble struct of src file, if open, or & zeroed one
1095 int renamefile(struct vol *vol, struct dir *ddir, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1099 LOG(log_debug, logtype_afpd,
1100 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1102 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1105 return( AFPERR_NOOBJ );
1108 return( AFPERR_ACCESS );
1110 return AFPERR_VLOCK;
1111 case EXDEV : /* Cross device move -- try copy */
1112 /* NOTE: with open file it's an error because after the copy we will
1113 * get two files, it's fixable for our process (eg reopen the new file, get the
1114 * locks, and so on. But it doesn't solve the case with a second process
1116 if (adp->ad_open_forks) {
1117 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1118 return AFPERR_OLOCK; /* little lie */
1120 if (AFP_OK != ( rc = copyfile(vol, vol, ddir, sdir_fd, src, dst, newname, NULL )) ) {
1121 /* on error copyfile delete dest */
1124 return deletefile(vol, sdir_fd, src, 0);
1126 return( AFPERR_PARAM );
1130 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1134 /* try to undo the data fork rename,
1135 * we know we are on the same device
1138 unix_rename(-1, dst, sdir_fd, src );
1139 /* return the first error */
1142 return AFPERR_NOOBJ;
1145 return AFPERR_ACCESS ;
1147 return AFPERR_VLOCK;
1149 return AFPERR_PARAM ;
1154 /* don't care if we can't open the newly renamed ressource fork */
1155 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1156 ad_setname(adp, newname);
1158 ad_close( adp, ADFLAGS_HF );
1165 convert a Mac long name to an utf8 name,
1167 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1171 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1177 /* ---------------- */
1178 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1185 if ( type != 2 && !(vol->v_obj->afp_version >= 30 && type == 3) ) {
1191 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1192 if (vol->v_obj->afp_version >= 30) {
1193 /* convert it to UTF8
1195 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1199 strncpy( newname, ibuf, plen );
1200 newname[ plen ] = '\0';
1202 if (strlen(newname) != plen) {
1203 /* there's \0 in newname, e.g. it's a pathname not
1211 memcpy(&hint, ibuf, sizeof(hint));
1212 ibuf += sizeof(hint);
1214 memcpy(&len16, ibuf, sizeof(len16));
1215 ibuf += sizeof(len16);
1216 plen = ntohs(len16);
1219 if (plen > AFPOBJ_TMPSIZ) {
1222 strncpy( newname, ibuf, plen );
1223 newname[ plen ] = '\0';
1224 if (strlen(newname) != plen) {
1233 /* -----------------------------------
1235 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1237 struct vol *s_vol, *d_vol;
1239 char *newname, *p, *upath;
1240 struct path *s_path;
1241 uint32_t sdid, ddid;
1242 int err, retvalue = AFP_OK;
1243 uint16_t svid, dvid;
1245 struct adouble ad, *adp;
1251 memcpy(&svid, ibuf, sizeof( svid ));
1252 ibuf += sizeof( svid );
1253 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1254 return( AFPERR_PARAM );
1257 memcpy(&sdid, ibuf, sizeof( sdid ));
1258 ibuf += sizeof( sdid );
1259 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1263 memcpy(&dvid, ibuf, sizeof( dvid ));
1264 ibuf += sizeof( dvid );
1265 memcpy(&ddid, ibuf, sizeof( ddid ));
1266 ibuf += sizeof( ddid );
1268 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1269 return get_afp_errno(AFPERR_PARAM);
1271 if ( path_isadir(s_path) ) {
1272 return( AFPERR_BADTYPE );
1275 /* don't allow copies when the file is open.
1276 * XXX: the spec only calls for read/deny write access.
1277 * however, copyfile doesn't have any of that info,
1278 * and locks need to stay coherent. as a result,
1279 * we just balk if the file is opened already. */
1281 adp = of_ad(s_vol, s_path, &ad);
1283 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1284 return AFPERR_DENYCONF;
1286 #ifdef HAVE_FSHARE_T
1288 shmd.f_access = F_RDACC;
1289 shmd.f_deny = F_NODNY;
1290 if (fcntl(ad_data_fileno(adp), F_SHARE, &shmd) != 0) {
1291 retvalue = AFPERR_DENYCONF;
1294 if (AD_RSRC_OPEN(adp) && fcntl(ad_reso_fileno(adp), F_SHARE, &shmd) != 0) {
1295 retvalue = AFPERR_DENYCONF;
1299 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1300 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1303 retvalue = AFPERR_DENYCONF;
1307 newname = obj->newtmp;
1308 strcpy( newname, s_path->m_name );
1310 p = ctoupath( s_vol, curdir, newname );
1312 retvalue = AFPERR_PARAM;
1316 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1317 retvalue = AFPERR_PARAM;
1321 if (d_vol->v_flags & AFPVOL_RO) {
1322 retvalue = AFPERR_VLOCK;
1326 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1327 retvalue = afp_errno;
1331 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1332 retvalue = get_afp_errno(AFPERR_NOOBJ);
1336 if ( *s_path->m_name != '\0' ) {
1337 retvalue =path_error(s_path, AFPERR_NOOBJ);
1341 /* one of the handful of places that knows about the path type */
1342 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1343 retvalue = AFPERR_PARAM;
1346 /* newname is always only a filename so curdir *is* its
1349 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding(d_vol->v_obj)))) {
1350 retvalue =AFPERR_PARAM;
1354 if ( (err = copyfile(s_vol, d_vol, curdir, -1, p, upath , newname, adp)) < 0 ) {
1360 setvoltime(obj, d_vol );
1363 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1367 /* ----------------------------------
1368 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1369 * because we are doing it elsewhere.
1370 * currently if newname is NULL then adp is NULL.
1372 int copyfile(struct vol *s_vol,
1379 struct adouble *adp)
1381 struct adouble ads, add;
1388 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1389 sfd, src, dst, newname);
1392 ad_init(&ads, s_vol);
1396 adflags = ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RF | ADFLAGS_NORF;
1398 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1403 if (!AD_META_OPEN(adp))
1404 /* no resource fork, don't create one for dst file */
1405 adflags &= ~ADFLAGS_HF;
1407 if (!AD_RSRC_OPEN(adp))
1408 /* no resource fork, don't create one for dst file */
1409 adflags &= ~ADFLAGS_RF;
1411 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1413 if (stat_result < 0) {
1414 /* unlikely but if fstat fails, the default file mode will be 0666. */
1415 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1418 ad_init(&add, d_vol);
1419 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1421 ad_close( adp, adflags );
1422 if (EEXIST != ret_err) {
1423 deletefile(d_vol, -1, dst, 0);
1426 return AFPERR_EXIST;
1429 if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1430 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1433 if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1434 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1439 if (AD_META_OPEN(&add)) {
1440 if (AD_META_OPEN(adp))
1441 ad_copy_header(&add, adp);
1442 ad_setname(&add, dst);
1445 if (fstat(ad_meta_fileno(&add), &stdest) != 0) {
1449 if ((id = get_id(d_vol, &add, &stdest, d_dir->d_did, dst, strlen(dst))) == CNID_INVALID) {
1453 (void)ad_setid(&add, stdest.st_dev, stdest.st_ino, id, d_dir->d_did, d_vol->v_stamp);
1458 ad_close( adp, adflags );
1460 if (ad_close( &add, adflags ) <0) {
1465 deletefile(d_vol, -1, dst, 0);
1467 else if (stat_result == 0) {
1468 /* set dest modification date to src date */
1471 ut.actime = ut.modtime = st.st_mtime;
1473 /* FIXME netatalk doesn't use resource fork file date
1474 * but maybe we should set its modtime too.
1479 switch ( ret_err ) {
1485 LOG(log_info, logtype_afpd, "copyfile: DISK FULL");
1486 return AFPERR_DFULL;
1488 return AFPERR_NOOBJ;
1490 return AFPERR_ACCESS;
1492 return AFPERR_VLOCK;
1494 return AFPERR_PARAM;
1498 /* -----------------------------------
1499 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1500 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1502 when deletefile is called we don't have lock on it, file is closed (for us)
1503 untrue if called by renamefile
1505 ad_open always try to open file RDWR first and ad_lock takes care of
1506 WRITE lock on read only file.
1509 static int check_attrib(struct adouble *adp)
1511 uint16_t bshort = 0;
1513 ad_getattr(adp, &bshort);
1515 * Does kFPDeleteInhibitBit (bit 8) set?
1517 if ((bshort & htons(ATTRBIT_NODELETE))) {
1518 return AFPERR_OLOCK;
1520 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1526 * dirfd can be used for unlinkat semantics
1528 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1531 struct adouble *adp = NULL;
1532 int adflags, err = AFP_OK;
1535 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1539 /* was EACCESS error try to get only metadata */
1540 /* we never want to create a resource fork here, we are going to delete it
1541 * moreover sometimes deletefile is called with a no existent file and
1542 * ad_open would create a 0 byte resource fork
1544 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1545 if ((err = check_attrib(&ad))) {
1546 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1553 /* try to open both forks at once */
1554 adflags = ADFLAGS_DF;
1555 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1560 case EACCES: /* maybe it's a file with no write mode for us */
1561 break; /* was return AFPERR_ACCESS;*/
1574 if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1575 adflags |= ADFLAGS_RF;
1576 /* FIXME we have a pb here because we want to know if a file is open
1577 * there's a 'priority inversion' if you can't open the ressource fork RW
1578 * you can delete it if it's open because you can't get a write lock.
1580 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1583 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1585 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1591 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1592 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1594 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1596 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1597 cnid_delete(vol->v_cdb, id);
1603 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1606 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1611 /* ------------------------------------ */
1612 /* return a file id */
1613 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1622 struct path *s_path;
1628 memcpy(&vid, ibuf, sizeof(vid));
1629 ibuf += sizeof(vid);
1631 if (NULL == ( vol = getvolbyvid( vid )) ) {
1632 return( AFPERR_PARAM);
1635 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1639 if (vol->v_flags & AFPVOL_RO)
1640 return AFPERR_VLOCK;
1642 memcpy(&did, ibuf, sizeof( did ));
1643 ibuf += sizeof(did);
1645 if (NULL == ( dir = dirlookup( vol, did )) ) {
1646 return afp_errno; /* was AFPERR_PARAM */
1649 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1650 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1653 if ( path_isadir(s_path) ) {
1654 return( AFPERR_BADTYPE );
1657 upath = s_path->u_name;
1658 switch (s_path->st_errno) {
1660 break; /* success */
1663 return AFPERR_ACCESS;
1665 return AFPERR_NOOBJ;
1667 return AFPERR_PARAM;
1670 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1671 memcpy(rbuf, &id, sizeof(id));
1672 *rbuflen = sizeof(id);
1673 return AFPERR_EXISTID;
1676 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1677 memcpy(rbuf, &id, sizeof(id));
1678 *rbuflen = sizeof(id);
1685 /* ------------------------------- */
1691 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1694 struct reenum *param = data;
1695 struct vol *vol = param->vol;
1696 cnid_t did = param->did;
1699 if ( lstat(de->d_name, &path.st) < 0 )
1702 /* update or add to cnid */
1703 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1708 /* --------------------
1709 * Ok the db is out of synch with the dir.
1710 * but if it's a deleted file we don't want to do it again and again.
1713 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1719 if (vol->v_cdb == NULL) {
1723 /* FIXME use of_statdir ? */
1724 if (lstat(name, &st)) {
1728 if (dirreenumerate(dir, &st)) {
1729 /* we already did it once and the dir haven't been modified */
1730 return dir->d_offcnt;
1734 data.did = dir->d_did;
1735 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1736 setdiroffcnt(curdir, &st, ret);
1737 dir->d_flags |= DIRF_CNID;
1743 /* ------------------------------
1744 resolve a file id */
1745 int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1754 uint16_t vid, bitmap;
1756 static char buffer[12 + MAXPATHLEN + 1];
1757 int len = 12 + MAXPATHLEN + 1;
1762 memcpy(&vid, ibuf, sizeof(vid));
1763 ibuf += sizeof(vid);
1765 if (NULL == ( vol = getvolbyvid( vid )) ) {
1766 return( AFPERR_PARAM);
1769 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1773 memcpy(&id, ibuf, sizeof( id ));
1778 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1782 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1783 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1786 if (NULL == ( dir = dirlookup( vol, id )) ) {
1787 return AFPERR_NOID; /* idem AFPERR_PARAM */
1789 if (movecwd(vol, dir) < 0) {
1793 return AFPERR_ACCESS;
1797 return AFPERR_PARAM;
1801 memset(&path, 0, sizeof(path));
1802 path.u_name = upath;
1803 if ( of_stat(&path) < 0 ) {
1805 /* with nfs and our working directory is deleted */
1806 if (errno == ESTALE) {
1810 if ( errno == ENOENT && !retry) {
1811 /* cnid db is out of sync, reenumerate the directory and update ids */
1812 reenumerate_id(vol, ".", dir);
1820 return AFPERR_ACCESS;
1824 return AFPERR_PARAM;
1828 /* directories are bad */
1829 if (S_ISDIR(path.st.st_mode)) {
1830 /* OS9 and OSX don't return the same error code */
1831 return (obj->afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1834 memcpy(&bitmap, ibuf, sizeof(bitmap));
1835 bitmap = ntohs( bitmap );
1836 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding(obj)))) {
1840 if (AFP_OK != (err = getfilparams(obj, vol, bitmap, &path , curdir,
1841 rbuf + sizeof(bitmap), &buflen, 0))) {
1844 *rbuflen = buflen + sizeof(bitmap);
1845 memcpy(rbuf, ibuf, sizeof(bitmap));
1850 /* ------------------------------ */
1851 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1861 static char buffer[12 + MAXPATHLEN + 1];
1862 int len = 12 + MAXPATHLEN + 1;
1867 memcpy(&vid, ibuf, sizeof(vid));
1868 ibuf += sizeof(vid);
1870 if (NULL == ( vol = getvolbyvid( vid )) ) {
1871 return( AFPERR_PARAM);
1874 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1878 if (vol->v_flags & AFPVOL_RO)
1879 return AFPERR_VLOCK;
1881 memcpy(&id, ibuf, sizeof( id ));
1885 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1889 if (NULL == ( dir = dirlookup( vol, id )) ) {
1890 if (afp_errno == AFPERR_NOOBJ) {
1894 return( AFPERR_PARAM );
1898 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1902 return AFPERR_ACCESS;
1907 /* still try to delete the id */
1911 return AFPERR_PARAM;
1914 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1915 return AFPERR_BADTYPE;
1918 if (cnid_delete(vol->v_cdb, fileid)) {
1921 return AFPERR_VLOCK;
1924 return AFPERR_ACCESS;
1926 return AFPERR_PARAM;
1933 /* ------------------------------ */
1934 static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
1938 if (path->st_errno) {
1939 switch (path->st_errno) {
1941 afp_errno = AFPERR_NOID;
1945 afp_errno = AFPERR_ACCESS;
1948 afp_errno = AFPERR_PARAM;
1953 /* we use file_access both for legacy Mac perm and
1954 * for unix privilege, rename will take care of folder perms
1956 if (file_access(obj, vol, path, OPENACC_WR ) < 0) {
1957 afp_errno = AFPERR_ACCESS;
1961 if ((*of = of_findname(path))) {
1962 /* reuse struct adouble so it won't break locks */
1966 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
1968 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1970 * The user must have the Read & Write privilege for both files in order to use this command.
1972 ad_close(adp, ADFLAGS_HF);
1973 afp_errno = AFPERR_ACCESS;
1980 #define APPLETEMP ".AppleTempXXXXXX"
1982 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1984 struct stat srcst, destst;
1986 struct dir *dir, *sdir;
1987 char *spath, temp[17], *p;
1988 char *supath, *upath;
1993 struct adouble *adsp = NULL;
1994 struct adouble *addp = NULL;
1995 struct ofork *s_of = NULL;
1996 struct ofork *d_of = NULL;
2006 memcpy(&vid, ibuf, sizeof(vid));
2007 ibuf += sizeof(vid);
2009 if (NULL == ( vol = getvolbyvid( vid )) ) {
2010 return( AFPERR_PARAM);
2013 if ((vol->v_flags & AFPVOL_RO))
2014 return AFPERR_VLOCK;
2016 /* source and destination dids */
2017 memcpy(&sid, ibuf, sizeof(sid));
2018 ibuf += sizeof(sid);
2019 memcpy(&did, ibuf, sizeof(did));
2020 ibuf += sizeof(did);
2023 if (NULL == (dir = dirlookup( vol, sid )) ) {
2024 return afp_errno; /* was AFPERR_PARAM */
2027 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2028 return get_afp_errno(AFPERR_NOOBJ);
2031 if ( path_isadir(path) ) {
2032 return AFPERR_BADTYPE; /* it's a dir */
2035 /* save some stuff */
2038 spath = obj->oldtmp;
2039 supath = obj->newtmp;
2040 strcpy(spath, path->m_name);
2041 strcpy(supath, path->u_name); /* this is for the cnid changing */
2042 p = absupath( vol, sdir, supath);
2044 /* pathname too long */
2045 return AFPERR_PARAM ;
2049 if (!(adsp = find_adouble(obj, vol, path, &s_of, &ads))) {
2053 /* ***** from here we may have resource fork open **** */
2055 /* look for the source cnid. if it doesn't exist, don't worry about
2057 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2059 if (NULL == ( dir = dirlookup( vol, did )) ) {
2060 err = afp_errno; /* was AFPERR_PARAM */
2061 goto err_exchangefile;
2064 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2065 err = get_afp_errno(AFPERR_NOOBJ);
2066 goto err_exchangefile;
2069 if ( path_isadir(path) ) {
2070 err = AFPERR_BADTYPE;
2071 goto err_exchangefile;
2074 /* FPExchangeFiles is the only call that can return the SameObj
2076 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2077 err = AFPERR_SAMEOBJ;
2078 goto err_exchangefile;
2082 if (!(addp = find_adouble(obj, vol, path, &d_of, &add))) {
2084 goto err_exchangefile;
2088 /* they are not on the same device and at least one is open
2089 * FIXME broken for for crossdev and adouble v2
2092 crossdev = (srcst.st_dev != destst.st_dev);
2093 if (/* (d_of || s_of) && */ crossdev) {
2095 goto err_exchangefile;
2098 /* look for destination id. */
2099 upath = path->u_name;
2100 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2102 /* construct a temp name.
2103 * NOTE: the temp file will be in the dest file's directory. it
2104 * will also be inaccessible from AFP. */
2105 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2106 if (!mktemp(temp)) {
2108 goto err_exchangefile;
2112 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2113 ad_close(adsp, ADFLAGS_HF);
2114 ad_close(addp, ADFLAGS_HF);
2117 /* now, quickly rename the file. we error if we can't. */
2118 if ((err = renamefile(vol, curdir, -1, p, temp, temp, adsp)) != AFP_OK)
2119 goto err_exchangefile;
2120 of_rename(vol, s_of, sdir, spath, curdir, temp);
2122 /* rename destination to source */
2123 if ((err = renamefile(vol, curdir, -1, upath, p, spath, addp)) != AFP_OK)
2124 goto err_src_to_tmp;
2125 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2127 /* rename temp to destination */
2128 if ((err = renamefile(vol, curdir, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2129 goto err_dest_to_src;
2130 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2132 /* id's need switching. src -> dest and dest -> src.
2133 * we need to re-stat() if it was a cross device copy.
2136 cnid_delete(vol->v_cdb, sid);
2138 cnid_delete(vol->v_cdb, did);
2140 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2141 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2143 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2144 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2149 err = AFPERR_ACCESS;
2154 goto err_temp_to_dest;
2157 /* here we need to reopen if crossdev */
2158 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2163 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2168 /* change perms, src gets dest perm and vice versa */
2173 * we need to exchange ACL entries as well
2175 /* exchange_acls(vol, p, upath); */
2180 path->m_name = NULL;
2181 path->u_name = upath;
2183 setfilunixmode(vol, path, destst.st_mode);
2184 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2191 setfilunixmode(vol, path, srcst.st_mode);
2192 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2197 goto err_exchangefile;
2199 /* all this stuff is so that we can unwind a failed operation
2202 /* rename dest to temp */
2203 renamefile(vol, curdir, -1, upath, temp, temp, adsp);
2204 of_rename(vol, s_of, curdir, upath, curdir, temp);
2207 /* rename source back to dest */
2208 renamefile(vol, curdir, -1, p, upath, path->m_name, addp);
2209 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2212 /* rename temp back to source */
2213 renamefile(vol, curdir, -1, temp, p, spath, adsp);
2214 of_rename(vol, s_of, curdir, temp, sdir, spath);
2217 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2218 ad_close(adsp, ADFLAGS_HF);
2220 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2221 ad_close(addp, ADFLAGS_HF);
2225 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2226 (void)dir_remove(vol, cached);
2227 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2228 (void)dir_remove(vol, cached);