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)
81 void *ad_finder = NULL;
85 ad_finder = ad_entry(adp, ADEID_FINDERI);
88 memcpy(data, ad_finder, ADEDLEN_FINDERI);
90 if (default_type(ad_finder))
94 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));
104 if (islink && !vol_syml_opt(vol)) {
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 AFP_CNID_START("cnid_add");
223 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
226 /* Throw errors if cnid_add fails. */
227 if (dbcnid == CNID_INVALID) {
229 case CNID_ERR_CLOSE: /* the db is closed */
232 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
233 afp_errno = AFPERR_PARAM;
236 afp_errno = AFPERR_PARAM;
239 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
240 /* we have to do it here for "dbd" because it uses "lazy opening" */
241 /* In order to not end in a loop somehow with goto restart below */
243 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
244 cnid_close(vol->v_cdb);
245 free(vol->v_cnidscheme);
246 vol->v_cnidscheme = strdup("tdb");
248 int flags = CNID_FLAG_MEMORY;
249 if ((vol->v_flags & AFPVOL_NODEV)) {
250 flags |= CNID_FLAG_NODEV;
252 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
254 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
256 if (!(vol->v_flags & AFPVOL_TM)) {
257 vol->v_flags |= AFPVOL_RO;
258 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
259 "Check server messages for details. Switching to read-only mode.");
260 kill(getpid(), SIGUSR2);
262 goto restart; /* now try again with the temp CNID db */
264 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
265 "Check server messages for details, can't recover from this state!");
268 afp_errno = AFPERR_MISC;
272 else if (adp && adcnid && (adcnid != dbcnid)) { /* 4 */
273 /* Update the ressource fork. For a folder adp is always null */
274 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
275 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
276 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
277 if (ad_flush(adp) != 0)
278 LOG(log_error, logtype_afpd, "get_id(\"%s\"): can't flush", fullpathname(upath));
288 /* -------------------------- */
289 int getmetadata(const AFPObj *obj,
292 struct path *path, struct dir *dir,
293 char *buf, size_t *buflen, struct adouble *adp)
295 char *data, *l_nameoff = NULL, *upath;
296 char *utf_nameoff = NULL;
301 u_char achar, fdType[4];
306 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
308 upath = path->u_name;
312 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
313 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding(obj)) /* FIXME should be m_name utf8 filename */
314 || (bitmap & (1 << FILPBIT_FNUM))) {
317 struct dir *cachedfile;
318 int len = strlen(upath);
319 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
320 id = cachedfile->d_did;
322 id = get_id(vol, adp, st, dir->d_did, upath, len);
324 /* Add it to the cache */
325 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
326 ntohl(dir->d_did), upath, ntohl(id));
328 /* Get macname from unixname first */
329 if (path->m_name == NULL) {
330 if ((path->m_name = utompath(vol, upath, id, utf8_encoding(obj))) == NULL) {
331 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
337 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
338 || (bconchar(fullpath, '/') != BSTR_OK)
339 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
340 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
345 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
346 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
350 if ((dircache_add(vol, cachedfile)) != 0) {
351 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
359 if (id == CNID_INVALID)
363 path->m_name = utompath(vol, upath, id, utf8_encoding(vol->v_obj));
366 while ( bitmap != 0 ) {
367 while (( bitmap & 1 ) == 0 ) {
375 ad_getattr(adp, &ashort);
376 } else if (vol_inv_dots(vol) && *upath == '.') {
377 ashort = htons(ATTRBIT_INVISIBLE);
380 ashort &= ~htons(vol->v_ignattr);
382 /* FIXME do we want a visual clue if the file is read only
385 accessmode(vol, ".", &ma, dir , NULL);
386 if ((ma.ma_user & AR_UWRITE)) {
387 accessmode(vol, upath, &ma, dir , st);
388 if (!(ma.ma_user & AR_UWRITE)) {
389 ashort |= htons(ATTRBIT_NOWRITE);
393 memcpy(data, &ashort, sizeof( ashort ));
394 data += sizeof( ashort );
395 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
396 path->u_name, ntohs(ashort));
400 memcpy(data, &dir->d_did, sizeof( uint32_t ));
401 data += sizeof( uint32_t );
402 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
403 path->u_name, ntohl(dir->d_did));
407 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
408 aint = AD_DATE_FROM_UNIX(st->st_mtime);
409 memcpy(data, &aint, sizeof( aint ));
410 data += sizeof( aint );
414 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
415 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
416 aint = AD_DATE_FROM_UNIX(st->st_mtime);
419 aint = AD_DATE_FROM_UNIX(st->st_mtime);
421 memcpy(data, &aint, sizeof( int ));
422 data += sizeof( int );
426 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
427 aint = AD_DATE_START;
428 memcpy(data, &aint, sizeof( int ));
429 data += sizeof( int );
433 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
434 data += ADEDLEN_FINDERI;
439 data += sizeof( uint16_t );
443 memset(data, 0, sizeof(uint16_t));
444 data += sizeof( uint16_t );
448 memcpy(data, &id, sizeof( id ));
449 data += sizeof( id );
450 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
451 path->u_name, ntohl(id));
455 if (st->st_size > 0xffffffff)
458 aint = htonl( st->st_size );
459 memcpy(data, &aint, sizeof( aint ));
460 data += sizeof( aint );
463 case FILPBIT_RFLEN: {
466 if (adp->ad_rlen > 0xffffffff)
469 aint = htonl( adp->ad_rlen);
471 rlen = ad_reso_size(path->u_name, 0, NULL);
472 if (rlen > 0xffffffff)
476 memcpy(data, &aint, sizeof( aint ));
477 data += sizeof( aint );
481 /* Current client needs ProDOS info block for this file.
482 Use simple heuristic and let the Mac "type" string tell
483 us what the PD file code should be. Everything gets a
484 subtype of 0x0000 unless the original value was hashed
485 to "pXYZ" when we created it. See IA, Ver 2.
486 <shirsch@adelphia.net> */
487 case FILPBIT_PDINFO :
488 if (obj->afp_version >= 30) { /* UTF8 name */
489 utf8 = kTextEncodingUTF8;
491 data += sizeof( uint16_t );
493 memcpy(data, &aint, sizeof( aint ));
494 data += sizeof( aint );
498 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
500 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
504 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
508 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
512 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
516 else if ( fdType[0] == 'p' ) {
518 ashort = (fdType[2] * 256) + fdType[3];
532 memcpy(data, &ashort, sizeof( ashort ));
533 data += sizeof( ashort );
534 memset(data, 0, sizeof( ashort ));
535 data += sizeof( ashort );
538 case FILPBIT_EXTDFLEN:
539 aint = htonl(st->st_size >> 32);
540 memcpy(data, &aint, sizeof( aint ));
541 data += sizeof( aint );
542 aint = htonl(st->st_size);
543 memcpy(data, &aint, sizeof( aint ));
544 data += sizeof( aint );
546 case FILPBIT_EXTRFLEN:
548 aint = htonl(adp->ad_rlen >> 32);
549 memcpy(data, &aint, sizeof( aint ));
550 data += sizeof( aint );
551 aint = htonl(adp->ad_rlen);
552 memcpy(data, &aint, sizeof( aint ));
553 data += sizeof( aint );
555 int64_t rlen = hton64(ad_reso_size(path->u_name, 0, NULL));
556 memcpy(data, &rlen, sizeof(rlen));
557 data += sizeof(rlen);
560 case FILPBIT_UNIXPR :
561 /* accessmode may change st_mode with ACLs */
562 accessmode(obj, vol, upath, &ma, dir , st);
564 aint = htonl(st->st_uid);
565 memcpy( data, &aint, sizeof( aint ));
566 data += sizeof( aint );
567 aint = htonl(st->st_gid);
568 memcpy( data, &aint, sizeof( aint ));
569 data += sizeof( aint );
572 type == slnk indicates an OSX style symlink,
573 we have to add S_IFLNK to the mode, otherwise
574 10.3 clients freak out. */
578 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
579 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
585 memcpy( data, &aint, sizeof( aint ));
586 data += sizeof( aint );
588 *data++ = ma.ma_user;
589 *data++ = ma.ma_world;
590 *data++ = ma.ma_group;
591 *data++ = ma.ma_owner;
595 return( AFPERR_BITMAP );
601 ashort = htons( data - buf );
602 memcpy(l_nameoff, &ashort, sizeof( ashort ));
603 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
606 ashort = htons( data - buf );
607 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
608 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
610 *buflen = data - buf;
614 /* ----------------------- */
615 int getfilparams(const AFPObj *obj, struct vol *vol, uint16_t bitmap, struct path *path,
616 struct dir *dir, char *buf, size_t *buflen, int in_enumerate)
618 struct adouble ad, *adp;
621 int flags; /* uninitialized ok */
623 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
625 opened = PARAM_NEED_ADP(bitmap);
631 * Dont check for and resturn open fork attributes when enumerating
632 * This saves a lot of syscalls, the client will hopefully only use the result
633 * in FPGetFileParms where we return the correct value
635 flags = (!in_enumerate &&(bitmap & (1 << FILPBIT_ATTR))) ? ADFLAGS_CHECK_OF : 0;
637 adp = of_ad(vol, path, &ad);
638 upath = path->u_name;
640 if ( ad_metadata( upath, flags, adp) < 0 ) {
643 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
644 upath, strerror(errno));
645 return AFPERR_ACCESS;
647 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
656 rc = getmetadata(obj, vol, bitmap, path, dir, buf, buflen, adp);
659 ad_close(adp, ADFLAGS_HF | flags);
664 /* ----------------------------- */
665 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
670 struct ofork *of = NULL;
672 int creatf, did, openf, retvalue = AFP_OK;
678 creatf = (unsigned char) *ibuf++;
680 memcpy(&vid, ibuf, sizeof( vid ));
681 ibuf += sizeof( vid );
683 if (NULL == ( vol = getvolbyvid( vid )) )
684 return( AFPERR_PARAM );
686 if (vol->v_flags & AFPVOL_RO)
689 memcpy(&did, ibuf, sizeof( did));
690 ibuf += sizeof( did );
692 if (NULL == ( dir = dirlookup( vol, did )) )
695 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
696 return get_afp_errno(AFPERR_PARAM);
697 if ( *s_path->m_name == '\0' )
698 return( AFPERR_BADTYPE );
700 upath = s_path->u_name;
703 /* if upath is deleted we already in trouble anyway */
704 if ((of = of_findname(vol, s_path))) {
712 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
714 /* on a soft create, if the file is open then ad_open won't fail
715 because open syscall is not called */
716 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
718 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
722 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
723 return ( AFPERR_NOOBJ );
725 return( AFPERR_EXIST );
727 return( AFPERR_ACCESS );
730 LOG(log_info, logtype_afpd, "afp_createfile: DISK FULL");
731 return( AFPERR_DFULL );
733 return( AFPERR_PARAM );
736 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
737 /* FIXME with hard create on an existing file, we already
738 * corrupted the data file.
740 netatalk_unlink( upath );
741 ad_close( &ad, ADFLAGS_DF );
742 return AFPERR_ACCESS;
745 path = s_path->m_name;
746 ad_setname(&ad, path);
749 if (lstat(upath, &st) != 0) {
750 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
751 upath, strerror(errno));
752 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
757 if ((id = get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath))) == CNID_INVALID) {
758 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
759 goto createfile_iderr;
761 (void)ad_setid(&ad, st.st_dev, st.st_ino, id, dir->d_did, vol->v_stamp);
765 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
766 fce_register(FCE_FILE_CREATE, fullpathname(upath), NULL, fce_file);
770 setvoltime(obj, vol );
775 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
781 uint16_t vid, bitmap;
786 memcpy(&vid, ibuf, sizeof( vid ));
787 ibuf += sizeof( vid );
788 if (NULL == ( vol = getvolbyvid( vid )) ) {
789 return( AFPERR_PARAM );
792 if (vol->v_flags & AFPVOL_RO)
795 memcpy(&did, ibuf, sizeof( did ));
796 ibuf += sizeof( did );
797 if (NULL == ( dir = dirlookup( vol, did )) ) {
798 return afp_errno; /* was AFPERR_NOOBJ */
801 memcpy(&bitmap, ibuf, sizeof( bitmap ));
802 bitmap = ntohs( bitmap );
803 ibuf += sizeof( bitmap );
805 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
806 return get_afp_errno(AFPERR_PARAM);
809 if (path_isadir(s_path)) {
810 return( AFPERR_BADTYPE ); /* it's a directory */
813 if ( s_path->st_errno != 0 ) {
814 return( AFPERR_NOOBJ );
817 if ((u_long)ibuf & 1 ) {
821 if (AFP_OK == ( rc = setfilparams(obj, vol, s_path, bitmap, ibuf )) ) {
822 setvoltime(obj, vol );
829 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
832 extern struct path Cur_Path;
834 int setfilparams(const AFPObj *obj, struct vol *vol,
835 struct path *path, uint16_t f_bitmap, char *buf )
837 struct adouble ad, *adp;
839 int bit, isad = 1, err = AFP_OK;
841 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
842 uint16_t ashort, bshort, oshort;
845 uint16_t upriv_bit = 0;
847 int change_mdate = 0;
848 int change_parent_mdate = 0;
853 uint16_t bitmap = f_bitmap;
854 uint32_t cdate,bdate;
855 u_char finder_buf[32];
856 int symlinked = S_ISLNK(path->st.st_mode);
859 char symbuf[MAXPATHLEN+1];
862 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
865 adp = of_ad(vol, path, &ad);
866 upath = path->u_name;
868 if (!vol_unix_priv(vol) && check_access(obj, vol, upath, OPENACC_WR ) < 0) {
869 return AFPERR_ACCESS;
872 /* with unix priv maybe we have to change adouble file priv first */
874 while ( bitmap != 0 ) {
875 while (( bitmap & 1 ) == 0 ) {
882 memcpy(&ashort, buf, sizeof( ashort ));
883 buf += sizeof( ashort );
887 memcpy(&cdate, buf, sizeof(cdate));
888 buf += sizeof( cdate );
891 memcpy(&newdate, buf, sizeof( newdate ));
892 buf += sizeof( newdate );
896 memcpy(&bdate, buf, sizeof( bdate));
897 buf += sizeof( bdate );
901 if (memcmp(buf,"slnkrhap",8) == 0
902 && !(S_ISLNK(path->st.st_mode))
903 && !(vol->v_flags & AFPVOL_FOLLOWSYM)) {
904 /* request to turn this into a symlink */
905 if ((fp = open(path->u_name, O_RDONLY)) == -1) {
907 goto setfilparam_done;
909 len = read(fp, symbuf, MAXPATHLEN);
913 goto setfilparam_done;
915 if (unlink(path->u_name) != 0) {
917 goto setfilparam_done;
920 if (symlink(symbuf, path->u_name) != 0) {
922 goto setfilparam_done;
927 memcpy(finder_buf, buf, 32 );
930 case FILPBIT_UNIXPR :
931 if (!vol_unix_priv(vol)) {
932 /* this volume doesn't use unix priv */
938 change_parent_mdate = 1;
940 memcpy( &aint, buf, sizeof( aint ));
941 f_uid = ntohl (aint);
942 buf += sizeof( aint );
943 memcpy( &aint, buf, sizeof( aint ));
944 f_gid = ntohl (aint);
945 buf += sizeof( aint );
946 setfilowner(vol, f_uid, f_gid, path);
948 memcpy( &upriv, buf, sizeof( upriv ));
949 buf += sizeof( upriv );
950 upriv = ntohl (upriv);
951 if ((upriv & S_IWUSR)) {
952 setfilunixmode(vol, path, upriv);
959 case FILPBIT_PDINFO :
960 if (obj->afp_version < 30) { /* else it's UTF8 name */
963 /* Keep special case to support crlf translations */
964 if ((unsigned int) achar == 0x04) {
965 fdType = (u_char *)"TEXT";
968 xyy[0] = ( u_char ) 'p';
979 /* break while loop */
988 /* second try with adouble open
990 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
991 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
993 * For some things, we don't need an adouble header:
994 * - change of modification date
995 * - UNIX privs (Bug-ID #2863424)
997 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
998 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
999 return AFPERR_ACCESS;
1001 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
1003 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
1004 ad_setname(adp, path->m_name);
1006 if ((id = get_id(vol, adp, &path->st, curdir->d_did, upath, strlen(upath))) == CNID_INVALID) {
1007 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
1010 (void)ad_setid(adp, path->st.st_dev, path->st.st_ino, id, curdir->d_did, vol->v_stamp);
1015 while ( bitmap != 0 ) {
1016 while (( bitmap & 1 ) == 0 ) {
1023 ad_getattr(adp, &bshort);
1025 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1026 ashort &= ~htons(vol->v_ignattr);
1027 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1031 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1032 change_parent_mdate = 1;
1033 ad_setattr(adp, bshort);
1035 case FILPBIT_CDATE :
1036 ad_setdate(adp, AD_DATE_CREATE, cdate);
1038 case FILPBIT_MDATE :
1040 case FILPBIT_BDATE :
1041 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1043 case FILPBIT_FINFO :
1044 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1046 ((em = getextmap( path->m_name )) &&
1047 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1048 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1049 || ((em = getdefextmap()) &&
1050 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1051 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1053 memcpy(finder_buf, ufinderi, 8 );
1055 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1057 case FILPBIT_UNIXPR :
1059 setfilunixmode(vol, path, upriv);
1062 case FILPBIT_PDINFO :
1063 if (obj->afp_version < 30) { /* else it's UTF8 name */
1064 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1065 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1070 err = AFPERR_BITMAP;
1071 goto setfilparam_done;
1078 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1079 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1083 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1084 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1090 ad_close(adp, ADFLAGS_HF);
1093 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1094 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1095 bitmap = 1<<FILPBIT_MDATE;
1096 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1100 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1106 * renamefile and copyfile take the old and new unix pathnames
1107 * and the new mac name.
1109 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1110 * passing -1 means this is not used, src path is a full path
1111 * src the source path
1112 * dst the dest filename in current dir
1113 * newname the dest mac name
1114 * adp adouble struct of src file, if open, or & zeroed one
1117 int renamefile(struct vol *vol, struct dir *ddir, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1121 LOG(log_debug, logtype_afpd,
1122 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1124 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1127 return( AFPERR_NOOBJ );
1130 return( AFPERR_ACCESS );
1132 return AFPERR_VLOCK;
1133 case EXDEV : /* Cross device move -- try copy */
1134 /* NOTE: with open file it's an error because after the copy we will
1135 * get two files, it's fixable for our process (eg reopen the new file, get the
1136 * locks, and so on. But it doesn't solve the case with a second process
1138 if (adp->ad_open_forks) {
1139 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1140 return AFPERR_OLOCK; /* little lie */
1142 if (AFP_OK != ( rc = copyfile(vol, vol, ddir, sdir_fd, src, dst, newname, NULL )) ) {
1143 /* on error copyfile delete dest */
1146 return deletefile(vol, sdir_fd, src, 0);
1148 return( AFPERR_PARAM );
1152 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1156 /* try to undo the data fork rename,
1157 * we know we are on the same device
1160 unix_rename(-1, dst, sdir_fd, src );
1161 /* return the first error */
1164 return AFPERR_NOOBJ;
1167 return AFPERR_ACCESS ;
1169 return AFPERR_VLOCK;
1171 return AFPERR_PARAM ;
1176 /* don't care if we can't open the newly renamed ressource fork */
1177 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1178 ad_setname(adp, newname);
1180 ad_close( adp, ADFLAGS_HF );
1187 convert a Mac long name to an utf8 name,
1189 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1193 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1199 /* ---------------- */
1200 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1207 if ( type != 2 && !(vol->v_obj->afp_version >= 30 && type == 3) ) {
1213 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1214 if (vol->v_obj->afp_version >= 30) {
1215 /* convert it to UTF8
1217 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1221 strncpy( newname, ibuf, plen );
1222 newname[ plen ] = '\0';
1224 if (strlen(newname) != plen) {
1225 /* there's \0 in newname, e.g. it's a pathname not
1233 memcpy(&hint, ibuf, sizeof(hint));
1234 ibuf += sizeof(hint);
1236 memcpy(&len16, ibuf, sizeof(len16));
1237 ibuf += sizeof(len16);
1238 plen = ntohs(len16);
1241 if (plen > AFPOBJ_TMPSIZ) {
1244 strncpy( newname, ibuf, plen );
1245 newname[ plen ] = '\0';
1246 if (strlen(newname) != plen) {
1255 /* -----------------------------------
1257 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1259 struct vol *s_vol, *d_vol;
1261 char *newname, *p, *upath;
1262 struct path *s_path;
1263 uint32_t sdid, ddid;
1264 int err, retvalue = AFP_OK;
1265 uint16_t svid, dvid;
1267 struct adouble ad, *adp;
1273 memcpy(&svid, ibuf, sizeof( svid ));
1274 ibuf += sizeof( svid );
1275 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1276 return( AFPERR_PARAM );
1279 memcpy(&sdid, ibuf, sizeof( sdid ));
1280 ibuf += sizeof( sdid );
1281 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1285 memcpy(&dvid, ibuf, sizeof( dvid ));
1286 ibuf += sizeof( dvid );
1287 memcpy(&ddid, ibuf, sizeof( ddid ));
1288 ibuf += sizeof( ddid );
1290 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1291 return get_afp_errno(AFPERR_PARAM);
1293 if ( path_isadir(s_path) ) {
1294 return( AFPERR_BADTYPE );
1297 /* don't allow copies when the file is open.
1298 * XXX: the spec only calls for read/deny write access.
1299 * however, copyfile doesn't have any of that info,
1300 * and locks need to stay coherent. as a result,
1301 * we just balk if the file is opened already. */
1303 adp = of_ad(s_vol, s_path, &ad);
1305 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1306 return AFPERR_DENYCONF;
1308 #ifdef HAVE_FSHARE_T
1310 shmd.f_access = F_RDACC;
1311 shmd.f_deny = F_NODNY;
1312 if (fcntl(ad_data_fileno(adp), F_SHARE, &shmd) != 0) {
1313 retvalue = AFPERR_DENYCONF;
1316 if (AD_RSRC_OPEN(adp) && fcntl(ad_reso_fileno(adp), F_SHARE, &shmd) != 0) {
1317 retvalue = AFPERR_DENYCONF;
1321 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1322 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1325 retvalue = AFPERR_DENYCONF;
1329 newname = obj->newtmp;
1330 strcpy( newname, s_path->m_name );
1332 p = ctoupath( s_vol, curdir, newname );
1334 retvalue = AFPERR_PARAM;
1338 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1339 retvalue = AFPERR_PARAM;
1343 if (d_vol->v_flags & AFPVOL_RO) {
1344 retvalue = AFPERR_VLOCK;
1348 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1349 retvalue = afp_errno;
1353 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1354 retvalue = get_afp_errno(AFPERR_NOOBJ);
1358 if ( *s_path->m_name != '\0' ) {
1359 retvalue =path_error(s_path, AFPERR_NOOBJ);
1363 /* one of the handful of places that knows about the path type */
1364 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1365 retvalue = AFPERR_PARAM;
1368 /* newname is always only a filename so curdir *is* its
1371 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding(d_vol->v_obj)))) {
1372 retvalue =AFPERR_PARAM;
1376 if ( (err = copyfile(s_vol, d_vol, curdir, -1, p, upath , newname, adp)) < 0 ) {
1382 setvoltime(obj, d_vol );
1385 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1389 /* ----------------------------------
1390 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1391 * because we are doing it elsewhere.
1392 * currently if newname is NULL then adp is NULL.
1394 int copyfile(struct vol *s_vol,
1401 struct adouble *adp)
1403 struct adouble ads, add;
1410 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1411 sfd, src, dst, newname);
1414 ad_init(&ads, s_vol);
1418 adflags = ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RF | ADFLAGS_NORF;
1420 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1425 if (!AD_META_OPEN(adp))
1426 /* no resource fork, don't create one for dst file */
1427 adflags &= ~ADFLAGS_HF;
1429 if (!AD_RSRC_OPEN(adp))
1430 /* no resource fork, don't create one for dst file */
1431 adflags &= ~ADFLAGS_RF;
1433 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1435 if (stat_result < 0) {
1436 /* unlikely but if fstat fails, the default file mode will be 0666. */
1437 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1440 ad_init(&add, d_vol);
1441 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1443 ad_close( adp, adflags );
1444 if (EEXIST != ret_err) {
1445 deletefile(d_vol, -1, dst, 0);
1448 return AFPERR_EXIST;
1451 if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1452 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1455 if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1456 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1461 if (AD_META_OPEN(&add)) {
1462 if (AD_META_OPEN(adp))
1463 ad_copy_header(&add, adp);
1464 ad_setname(&add, dst);
1467 if (fstat(ad_meta_fileno(&add), &stdest) != 0) {
1471 if ((id = get_id(d_vol, &add, &stdest, d_dir->d_did, dst, strlen(dst))) == CNID_INVALID) {
1475 (void)ad_setid(&add, stdest.st_dev, stdest.st_ino, id, d_dir->d_did, d_vol->v_stamp);
1480 ad_close( adp, adflags );
1482 if (ad_close( &add, adflags ) <0) {
1487 deletefile(d_vol, -1, dst, 0);
1489 else if (stat_result == 0) {
1490 /* set dest modification date to src date */
1493 ut.actime = ut.modtime = st.st_mtime;
1495 /* FIXME netatalk doesn't use resource fork file date
1496 * but maybe we should set its modtime too.
1501 switch ( ret_err ) {
1507 LOG(log_info, logtype_afpd, "copyfile: DISK FULL");
1508 return AFPERR_DFULL;
1510 return AFPERR_NOOBJ;
1512 return AFPERR_ACCESS;
1514 return AFPERR_VLOCK;
1516 return AFPERR_PARAM;
1520 /* -----------------------------------
1521 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1522 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1524 when deletefile is called we don't have lock on it, file is closed (for us)
1525 untrue if called by renamefile
1527 ad_open always try to open file RDWR first and ad_lock takes care of
1528 WRITE lock on read only file.
1531 static int check_attrib(const struct vol *vol, struct adouble *adp)
1533 uint16_t bshort = 0;
1535 ad_getattr(adp, &bshort);
1537 * Does kFPDeleteInhibitBit (bit 8) set?
1539 if (!(vol->v_ignattr & ATTRBIT_NODELETE) && (bshort & htons(ATTRBIT_NODELETE))) {
1540 return AFPERR_OLOCK;
1542 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1548 * dirfd can be used for unlinkat semantics
1550 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1553 struct adouble *adp = NULL;
1554 int adflags, err = AFP_OK;
1557 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1561 /* was EACCESS error try to get only metadata */
1562 /* we never want to create a resource fork here, we are going to delete it
1563 * moreover sometimes deletefile is called with a no existent file and
1564 * ad_open would create a 0 byte resource fork
1566 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1567 if ((err = check_attrib(vol, &ad))) {
1568 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1575 /* try to open both forks at once */
1576 adflags = ADFLAGS_DF;
1577 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1582 case EACCES: /* maybe it's a file with no write mode for us */
1583 break; /* was return AFPERR_ACCESS;*/
1596 if ( adp && AD_RSRC_OPEN(adp) ) { /* there's a resource fork */
1597 adflags |= ADFLAGS_RF;
1598 /* FIXME we have a pb here because we want to know if a file is open
1599 * there's a 'priority inversion' if you can't open the ressource fork RW
1600 * you can delete it if it's open because you can't get a write lock.
1602 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1605 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1607 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1613 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1614 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1616 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1619 AFP_CNID_START("cnid_get");
1620 id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file));
1623 AFP_CNID_START("cnid_delete");
1624 cnid_delete(vol->v_cdb, id);
1632 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1635 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1640 /* ------------------------------------ */
1641 /* return a file id */
1642 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1651 struct path *s_path;
1657 memcpy(&vid, ibuf, sizeof(vid));
1658 ibuf += sizeof(vid);
1660 if (NULL == ( vol = getvolbyvid( vid )) ) {
1661 return( AFPERR_PARAM);
1664 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1668 if (vol->v_flags & AFPVOL_RO)
1669 return AFPERR_VLOCK;
1671 memcpy(&did, ibuf, sizeof( did ));
1672 ibuf += sizeof(did);
1674 if (NULL == ( dir = dirlookup( vol, did )) ) {
1675 return afp_errno; /* was AFPERR_PARAM */
1678 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1679 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1682 if ( path_isadir(s_path) ) {
1683 return( AFPERR_BADTYPE );
1686 upath = s_path->u_name;
1687 switch (s_path->st_errno) {
1689 break; /* success */
1692 return AFPERR_ACCESS;
1694 return AFPERR_NOOBJ;
1696 return AFPERR_PARAM;
1699 AFP_CNID_START("cnid_lookup");
1700 id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath));
1703 memcpy(rbuf, &id, sizeof(id));
1704 *rbuflen = sizeof(id);
1705 return AFPERR_EXISTID;
1708 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1709 memcpy(rbuf, &id, sizeof(id));
1710 *rbuflen = sizeof(id);
1717 /* ------------------------------- */
1723 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1726 struct reenum *param = data;
1727 struct vol *vol = param->vol;
1728 cnid_t did = param->did;
1731 if (ostat(de->d_name, &path.st, vol_syml_opt(vol)) < 0)
1734 /* update or add to cnid */
1735 AFP_CNID_START("cnid_add");
1736 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1742 /* --------------------
1743 * Ok the db is out of synch with the dir.
1744 * but if it's a deleted file we don't want to do it again and again.
1747 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1753 if (vol->v_cdb == NULL) {
1757 /* FIXME use of_statdir ? */
1758 if (ostat(name, &st, vol_syml_opt(vol))) {
1762 if (dirreenumerate(dir, &st)) {
1763 /* we already did it once and the dir haven't been modified */
1764 return dir->d_offcnt;
1768 data.did = dir->d_did;
1769 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1770 setdiroffcnt(curdir, &st, ret);
1771 dir->d_flags |= DIRF_CNID;
1777 /* ------------------------------
1778 resolve a file id */
1779 int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1788 uint16_t vid, bitmap;
1790 static char buffer[12 + MAXPATHLEN + 1];
1791 int len = 12 + MAXPATHLEN + 1;
1796 memcpy(&vid, ibuf, sizeof(vid));
1797 ibuf += sizeof(vid);
1799 if (NULL == ( vol = getvolbyvid( vid )) ) {
1800 return( AFPERR_PARAM);
1803 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1807 memcpy(&id, ibuf, sizeof( id ));
1812 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1816 AFP_CNID_START("cnid_resolve");
1817 upath = cnid_resolve(vol->v_cdb, &id, buffer, len);
1819 if (upath == NULL) {
1820 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1823 if (NULL == ( dir = dirlookup( vol, id )) ) {
1824 return AFPERR_NOID; /* idem AFPERR_PARAM */
1826 if (movecwd(vol, dir) < 0) {
1830 return AFPERR_ACCESS;
1834 return AFPERR_PARAM;
1838 memset(&path, 0, sizeof(path));
1839 path.u_name = upath;
1840 if (of_stat(vol, &path) < 0 ) {
1842 /* with nfs and our working directory is deleted */
1843 if (errno == ESTALE) {
1847 if ( errno == ENOENT && !retry) {
1848 /* cnid db is out of sync, reenumerate the directory and update ids */
1849 reenumerate_id(vol, ".", dir);
1857 return AFPERR_ACCESS;
1861 return AFPERR_PARAM;
1865 /* directories are bad */
1866 if (S_ISDIR(path.st.st_mode)) {
1867 /* OS9 and OSX don't return the same error code */
1868 return (obj->afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1871 memcpy(&bitmap, ibuf, sizeof(bitmap));
1872 bitmap = ntohs( bitmap );
1873 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding(obj)))) {
1877 if (AFP_OK != (err = getfilparams(obj, vol, bitmap, &path , curdir,
1878 rbuf + sizeof(bitmap), &buflen, 0))) {
1881 *rbuflen = buflen + sizeof(bitmap);
1882 memcpy(rbuf, ibuf, sizeof(bitmap));
1887 /* ------------------------------ */
1888 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1898 static char buffer[12 + MAXPATHLEN + 1];
1899 int len = 12 + MAXPATHLEN + 1;
1904 memcpy(&vid, ibuf, sizeof(vid));
1905 ibuf += sizeof(vid);
1907 if (NULL == ( vol = getvolbyvid( vid )) ) {
1908 return( AFPERR_PARAM);
1911 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1915 if (vol->v_flags & AFPVOL_RO)
1916 return AFPERR_VLOCK;
1918 memcpy(&id, ibuf, sizeof( id ));
1922 AFP_CNID_START("cnid_resolve");
1923 upath = cnid_resolve(vol->v_cdb, &id, buffer, len);
1925 if (upath == NULL) {
1929 if (NULL == ( dir = dirlookup( vol, id )) ) {
1930 if (afp_errno == AFPERR_NOOBJ) {
1934 return( AFPERR_PARAM );
1938 if ((movecwd(vol, dir) < 0) || (ostat(upath, &st, vol_syml_opt(vol)) < 0)) {
1942 return AFPERR_ACCESS;
1947 /* still try to delete the id */
1951 return AFPERR_PARAM;
1954 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1955 return AFPERR_BADTYPE;
1958 AFP_CNID_START("cnid_delete");
1959 if (cnid_delete(vol->v_cdb, fileid)) {
1963 return AFPERR_VLOCK;
1966 return AFPERR_ACCESS;
1968 return AFPERR_PARAM;
1975 /* ------------------------------ */
1976 static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
1980 if (path->st_errno) {
1981 switch (path->st_errno) {
1983 afp_errno = AFPERR_NOID;
1987 afp_errno = AFPERR_ACCESS;
1990 afp_errno = AFPERR_PARAM;
1995 /* we use file_access both for legacy Mac perm and
1996 * for unix privilege, rename will take care of folder perms
1998 if (file_access(obj, vol, path, OPENACC_WR ) < 0) {
1999 afp_errno = AFPERR_ACCESS;
2003 if ((*of = of_findname(vol, path))) {
2004 /* reuse struct adouble so it won't break locks */
2008 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
2010 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2012 * The user must have the Read & Write privilege for both files in order to use this command.
2014 ad_close(adp, ADFLAGS_HF);
2015 afp_errno = AFPERR_ACCESS;
2022 #define APPLETEMP ".AppleTempXXXXXX"
2024 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2026 struct stat srcst, destst;
2028 struct dir *dir, *sdir;
2029 char *spath, temp[17], *p;
2030 char *supath, *upath;
2035 struct adouble *adsp = NULL;
2036 struct adouble *addp = NULL;
2037 struct ofork *s_of = NULL;
2038 struct ofork *d_of = NULL;
2048 memcpy(&vid, ibuf, sizeof(vid));
2049 ibuf += sizeof(vid);
2051 if (NULL == ( vol = getvolbyvid( vid )) ) {
2052 return( AFPERR_PARAM);
2055 if ((vol->v_flags & AFPVOL_RO))
2056 return AFPERR_VLOCK;
2058 /* source and destination dids */
2059 memcpy(&sid, ibuf, sizeof(sid));
2060 ibuf += sizeof(sid);
2061 memcpy(&did, ibuf, sizeof(did));
2062 ibuf += sizeof(did);
2065 if (NULL == (dir = dirlookup( vol, sid )) ) {
2066 return afp_errno; /* was AFPERR_PARAM */
2069 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2070 return get_afp_errno(AFPERR_NOOBJ);
2073 if ( path_isadir(path) ) {
2074 return AFPERR_BADTYPE; /* it's a dir */
2077 /* save some stuff */
2080 spath = obj->oldtmp;
2081 supath = obj->newtmp;
2082 strcpy(spath, path->m_name);
2083 strcpy(supath, path->u_name); /* this is for the cnid changing */
2084 p = absupath( vol, sdir, supath);
2086 /* pathname too long */
2087 return AFPERR_PARAM ;
2091 if (!(adsp = find_adouble(obj, vol, path, &s_of, &ads))) {
2095 /* ***** from here we may have resource fork open **** */
2097 /* look for the source cnid. if it doesn't exist, don't worry about
2099 AFP_CNID_START("cnid_lookup");
2100 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2103 if (NULL == ( dir = dirlookup( vol, did )) ) {
2104 err = afp_errno; /* was AFPERR_PARAM */
2105 goto err_exchangefile;
2108 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2109 err = get_afp_errno(AFPERR_NOOBJ);
2110 goto err_exchangefile;
2113 if ( path_isadir(path) ) {
2114 err = AFPERR_BADTYPE;
2115 goto err_exchangefile;
2118 /* FPExchangeFiles is the only call that can return the SameObj
2120 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2121 err = AFPERR_SAMEOBJ;
2122 goto err_exchangefile;
2126 if (!(addp = find_adouble(obj, vol, path, &d_of, &add))) {
2128 goto err_exchangefile;
2132 /* they are not on the same device and at least one is open
2133 * FIXME broken for for crossdev and adouble v2
2136 crossdev = (srcst.st_dev != destst.st_dev);
2137 if (/* (d_of || s_of) && */ crossdev) {
2139 goto err_exchangefile;
2142 /* look for destination id. */
2143 upath = path->u_name;
2144 AFP_CNID_START("cnid_lookup");
2145 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2148 /* construct a temp name.
2149 * NOTE: the temp file will be in the dest file's directory. it
2150 * will also be inaccessible from AFP. */
2151 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2153 if ((fd = mkstemp(temp)) == -1) {
2155 goto err_exchangefile;
2160 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2161 ad_close(adsp, ADFLAGS_HF);
2162 ad_close(addp, ADFLAGS_HF);
2165 /* now, quickly rename the file. we error if we can't. */
2166 if ((err = renamefile(vol, curdir, -1, p, temp, temp, adsp)) != AFP_OK)
2167 goto err_exchangefile;
2168 of_rename(vol, s_of, sdir, spath, curdir, temp);
2170 /* rename destination to source */
2171 if ((err = renamefile(vol, curdir, -1, upath, p, spath, addp)) != AFP_OK)
2172 goto err_src_to_tmp;
2173 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2175 /* rename temp to destination */
2176 if ((err = renamefile(vol, curdir, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2177 goto err_dest_to_src;
2178 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2181 * id's need switching. src -> dest and dest -> src.
2182 * we need to re-stat() if it was a cross device copy.
2185 AFP_CNID_START("cnid_delete");
2186 cnid_delete(vol->v_cdb, sid);
2190 AFP_CNID_START("cnid_delete");
2191 cnid_delete(vol->v_cdb, did);
2195 if ((did && ( (crossdev && ostat(upath, &srcst, vol_syml_opt(vol)) < 0) ||
2196 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2198 (sid && ( (crossdev && ostat(p, &destst, vol_syml_opt(vol)) < 0) ||
2199 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2204 err = AFPERR_ACCESS;
2209 goto err_temp_to_dest;
2212 /* here we need to reopen if crossdev */
2213 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2218 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2223 /* change perms, src gets dest perm and vice versa */
2228 * we need to exchange ACL entries as well
2230 /* exchange_acls(vol, p, upath); */
2235 path->m_name = NULL;
2236 path->u_name = upath;
2238 setfilunixmode(vol, path, destst.st_mode);
2239 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2246 setfilunixmode(vol, path, srcst.st_mode);
2247 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2252 goto err_exchangefile;
2254 /* all this stuff is so that we can unwind a failed operation
2257 /* rename dest to temp */
2258 renamefile(vol, curdir, -1, upath, temp, temp, adsp);
2259 of_rename(vol, s_of, curdir, upath, curdir, temp);
2262 /* rename source back to dest */
2263 renamefile(vol, curdir, -1, p, upath, path->m_name, addp);
2264 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2267 /* rename temp back to source */
2268 renamefile(vol, curdir, -1, temp, p, spath, adsp);
2269 of_rename(vol, s_of, curdir, temp, sdir, spath);
2272 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2273 ad_close(adsp, ADFLAGS_HF);
2275 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2276 ad_close(addp, ADFLAGS_HF);
2280 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2281 (void)dir_remove(vol, cached);
2282 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2283 (void)dir_remove(vol, cached);