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 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));
342 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
343 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
347 if ((dircache_add(vol, cachedfile)) != 0) {
348 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
356 if (id == CNID_INVALID)
360 path->m_name = utompath(vol, upath, id, utf8_encoding(vol->v_obj));
363 while ( bitmap != 0 ) {
364 while (( bitmap & 1 ) == 0 ) {
372 ad_getattr(adp, &ashort);
373 } else if (vol_inv_dots(vol) && *upath == '.') {
374 ashort = htons(ATTRBIT_INVISIBLE);
378 /* FIXME do we want a visual clue if the file is read only
381 accessmode(vol, ".", &ma, dir , NULL);
382 if ((ma.ma_user & AR_UWRITE)) {
383 accessmode(vol, upath, &ma, dir , st);
384 if (!(ma.ma_user & AR_UWRITE)) {
385 ashort |= htons(ATTRBIT_NOWRITE);
389 memcpy(data, &ashort, sizeof( ashort ));
390 data += sizeof( ashort );
391 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
392 path->u_name, ntohs(ashort));
396 memcpy(data, &dir->d_did, sizeof( uint32_t ));
397 data += sizeof( uint32_t );
398 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
399 path->u_name, ntohl(dir->d_did));
403 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
404 aint = AD_DATE_FROM_UNIX(st->st_mtime);
405 memcpy(data, &aint, sizeof( aint ));
406 data += sizeof( aint );
410 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
411 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
412 aint = AD_DATE_FROM_UNIX(st->st_mtime);
415 aint = AD_DATE_FROM_UNIX(st->st_mtime);
417 memcpy(data, &aint, sizeof( int ));
418 data += sizeof( int );
422 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
423 aint = AD_DATE_START;
424 memcpy(data, &aint, sizeof( int ));
425 data += sizeof( int );
429 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
430 data += ADEDLEN_FINDERI;
435 data += sizeof( uint16_t );
439 memset(data, 0, sizeof(uint16_t));
440 data += sizeof( uint16_t );
444 memcpy(data, &id, sizeof( id ));
445 data += sizeof( id );
446 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
447 path->u_name, ntohl(id));
451 if (st->st_size > 0xffffffff)
454 aint = htonl( st->st_size );
455 memcpy(data, &aint, sizeof( aint ));
456 data += sizeof( aint );
461 if (adp->ad_rlen > 0xffffffff)
464 aint = htonl( adp->ad_rlen);
468 memcpy(data, &aint, sizeof( aint ));
469 data += sizeof( aint );
472 /* Current client needs ProDOS info block for this file.
473 Use simple heuristic and let the Mac "type" string tell
474 us what the PD file code should be. Everything gets a
475 subtype of 0x0000 unless the original value was hashed
476 to "pXYZ" when we created it. See IA, Ver 2.
477 <shirsch@adelphia.net> */
478 case FILPBIT_PDINFO :
479 if (obj->afp_version >= 30) { /* UTF8 name */
480 utf8 = kTextEncodingUTF8;
482 data += sizeof( uint16_t );
484 memcpy(data, &aint, sizeof( aint ));
485 data += sizeof( aint );
489 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
491 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
495 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
499 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
503 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
507 else if ( fdType[0] == 'p' ) {
509 ashort = (fdType[2] * 256) + fdType[3];
523 memcpy(data, &ashort, sizeof( ashort ));
524 data += sizeof( ashort );
525 memset(data, 0, sizeof( ashort ));
526 data += sizeof( ashort );
529 case FILPBIT_EXTDFLEN:
530 aint = htonl(st->st_size >> 32);
531 memcpy(data, &aint, sizeof( aint ));
532 data += sizeof( aint );
533 aint = htonl(st->st_size);
534 memcpy(data, &aint, sizeof( aint ));
535 data += sizeof( aint );
537 case FILPBIT_EXTRFLEN:
540 aint = htonl(adp->ad_rlen >> 32);
541 memcpy(data, &aint, sizeof( aint ));
542 data += sizeof( aint );
544 aint = htonl(adp->ad_rlen);
545 memcpy(data, &aint, sizeof( aint ));
546 data += sizeof( aint );
548 case FILPBIT_UNIXPR :
549 /* accessmode may change st_mode with ACLs */
550 accessmode(obj, vol, upath, &ma, dir , st);
552 aint = htonl(st->st_uid);
553 memcpy( data, &aint, sizeof( aint ));
554 data += sizeof( aint );
555 aint = htonl(st->st_gid);
556 memcpy( data, &aint, sizeof( aint ));
557 data += sizeof( aint );
560 type == slnk indicates an OSX style symlink,
561 we have to add S_IFLNK to the mode, otherwise
562 10.3 clients freak out. */
566 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
567 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
573 memcpy( data, &aint, sizeof( aint ));
574 data += sizeof( aint );
576 *data++ = ma.ma_user;
577 *data++ = ma.ma_world;
578 *data++ = ma.ma_group;
579 *data++ = ma.ma_owner;
583 return( AFPERR_BITMAP );
589 ashort = htons( data - buf );
590 memcpy(l_nameoff, &ashort, sizeof( ashort ));
591 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
594 ashort = htons( data - buf );
595 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
596 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
598 *buflen = data - buf;
602 /* ----------------------- */
603 int getfilparams(const AFPObj *obj, struct vol *vol, uint16_t bitmap, struct path *path,
604 struct dir *dir, char *buf, size_t *buflen, int in_enumerate)
606 struct adouble ad, *adp;
609 int flags; /* uninitialized ok */
611 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
613 opened = PARAM_NEED_ADP(bitmap);
619 * Dont check for and resturn open fork attributes when enumerating
620 * This saves a lot of syscalls, the client will hopefully only use the result
621 * in FPGetFileParms where we return the correct value
623 flags = (!in_enumerate &&(bitmap & (1 << FILPBIT_ATTR))) ? ADFLAGS_CHECK_OF : 0;
625 adp = of_ad(vol, path, &ad);
626 upath = path->u_name;
628 if ( ad_metadata( upath, flags, adp) < 0 ) {
631 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
632 upath, strerror(errno));
633 return AFPERR_ACCESS;
635 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
644 rc = getmetadata(obj, vol, bitmap, path, dir, buf, buflen, adp);
647 ad_close(adp, ADFLAGS_HF | flags);
652 /* ----------------------------- */
653 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
658 struct ofork *of = NULL;
660 int creatf, did, openf, retvalue = AFP_OK;
666 creatf = (unsigned char) *ibuf++;
668 memcpy(&vid, ibuf, sizeof( vid ));
669 ibuf += sizeof( vid );
671 if (NULL == ( vol = getvolbyvid( vid )) )
672 return( AFPERR_PARAM );
674 if (vol->v_flags & AFPVOL_RO)
677 memcpy(&did, ibuf, sizeof( did));
678 ibuf += sizeof( did );
680 if (NULL == ( dir = dirlookup( vol, did )) )
683 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
684 return get_afp_errno(AFPERR_PARAM);
685 if ( *s_path->m_name == '\0' )
686 return( AFPERR_BADTYPE );
688 upath = s_path->u_name;
691 /* if upath is deleted we already in trouble anyway */
692 if ((of = of_findname(vol, s_path))) {
700 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
702 /* on a soft create, if the file is open then ad_open won't fail
703 because open syscall is not called */
704 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
706 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
710 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
711 return ( AFPERR_NOOBJ );
713 return( AFPERR_EXIST );
715 return( AFPERR_ACCESS );
718 LOG(log_info, logtype_afpd, "afp_createfile: DISK FULL");
719 return( AFPERR_DFULL );
721 return( AFPERR_PARAM );
724 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
725 /* FIXME with hard create on an existing file, we already
726 * corrupted the data file.
728 netatalk_unlink( upath );
729 ad_close( &ad, ADFLAGS_DF );
730 return AFPERR_ACCESS;
733 path = s_path->m_name;
734 ad_setname(&ad, path);
737 if (lstat(upath, &st) != 0) {
738 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
739 upath, strerror(errno));
740 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
745 if ((id = get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath))) == CNID_INVALID) {
746 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
747 goto createfile_iderr;
749 (void)ad_setid(&ad, st.st_dev, st.st_ino, id, dir->d_did, vol->v_stamp);
753 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
754 fce_register(FCE_FILE_CREATE, fullpathname(upath), NULL, fce_file);
758 setvoltime(obj, vol );
763 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
769 uint16_t vid, bitmap;
774 memcpy(&vid, ibuf, sizeof( vid ));
775 ibuf += sizeof( vid );
776 if (NULL == ( vol = getvolbyvid( vid )) ) {
777 return( AFPERR_PARAM );
780 if (vol->v_flags & AFPVOL_RO)
783 memcpy(&did, ibuf, sizeof( did ));
784 ibuf += sizeof( did );
785 if (NULL == ( dir = dirlookup( vol, did )) ) {
786 return afp_errno; /* was AFPERR_NOOBJ */
789 memcpy(&bitmap, ibuf, sizeof( bitmap ));
790 bitmap = ntohs( bitmap );
791 ibuf += sizeof( bitmap );
793 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
794 return get_afp_errno(AFPERR_PARAM);
797 if (path_isadir(s_path)) {
798 return( AFPERR_BADTYPE ); /* it's a directory */
801 if ( s_path->st_errno != 0 ) {
802 return( AFPERR_NOOBJ );
805 if ((u_long)ibuf & 1 ) {
809 if (AFP_OK == ( rc = setfilparams(obj, vol, s_path, bitmap, ibuf )) ) {
810 setvoltime(obj, vol );
817 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
820 extern struct path Cur_Path;
822 int setfilparams(const AFPObj *obj, struct vol *vol,
823 struct path *path, uint16_t f_bitmap, char *buf )
825 struct adouble ad, *adp;
827 int bit, isad = 1, err = AFP_OK;
829 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
830 uint16_t ashort, bshort, oshort;
833 uint16_t upriv_bit = 0;
835 int change_mdate = 0;
836 int change_parent_mdate = 0;
841 uint16_t bitmap = f_bitmap;
842 uint32_t cdate,bdate;
843 u_char finder_buf[32];
844 int symlinked = S_ISLNK(path->st.st_mode);
847 char symbuf[MAXPATHLEN+1];
850 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
853 adp = of_ad(vol, path, &ad);
854 upath = path->u_name;
856 if (!vol_unix_priv(vol) && check_access(obj, vol, upath, OPENACC_WR ) < 0) {
857 return AFPERR_ACCESS;
860 /* with unix priv maybe we have to change adouble file priv first */
862 while ( bitmap != 0 ) {
863 while (( bitmap & 1 ) == 0 ) {
870 memcpy(&ashort, buf, sizeof( ashort ));
871 buf += sizeof( ashort );
875 memcpy(&cdate, buf, sizeof(cdate));
876 buf += sizeof( cdate );
879 memcpy(&newdate, buf, sizeof( newdate ));
880 buf += sizeof( newdate );
884 memcpy(&bdate, buf, sizeof( bdate));
885 buf += sizeof( bdate );
889 if (memcmp(buf,"slnkrhap",8) == 0
890 && !(S_ISLNK(path->st.st_mode))
891 && !(vol->v_flags & AFPVOL_FOLLOWSYM)) {
892 /* request to turn this into a symlink */
893 if ((fp = open(path->u_name, O_RDONLY)) == -1) {
895 goto setfilparam_done;
897 len = read(fp, symbuf, MAXPATHLEN);
901 goto setfilparam_done;
903 if (unlink(path->u_name) != 0) {
905 goto setfilparam_done;
908 if (symlink(symbuf, path->u_name) != 0) {
910 goto setfilparam_done;
915 memcpy(finder_buf, buf, 32 );
918 case FILPBIT_UNIXPR :
919 if (!vol_unix_priv(vol)) {
920 /* this volume doesn't use unix priv */
926 change_parent_mdate = 1;
928 memcpy( &aint, buf, sizeof( aint ));
929 f_uid = ntohl (aint);
930 buf += sizeof( aint );
931 memcpy( &aint, buf, sizeof( aint ));
932 f_gid = ntohl (aint);
933 buf += sizeof( aint );
934 setfilowner(vol, f_uid, f_gid, path);
936 memcpy( &upriv, buf, sizeof( upriv ));
937 buf += sizeof( upriv );
938 upriv = ntohl (upriv);
939 if ((upriv & S_IWUSR)) {
940 setfilunixmode(vol, path, upriv);
947 case FILPBIT_PDINFO :
948 if (obj->afp_version < 30) { /* else it's UTF8 name */
951 /* Keep special case to support crlf translations */
952 if ((unsigned int) achar == 0x04) {
953 fdType = (u_char *)"TEXT";
956 xyy[0] = ( u_char ) 'p';
967 /* break while loop */
976 /* second try with adouble open
978 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
979 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
981 * For some things, we don't need an adouble header:
982 * - change of modification date
983 * - UNIX privs (Bug-ID #2863424)
985 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
986 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
987 return AFPERR_ACCESS;
989 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
991 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
992 ad_setname(adp, path->m_name);
994 if ((id = get_id(vol, adp, &path->st, curdir->d_did, upath, strlen(upath))) == CNID_INVALID) {
995 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
998 (void)ad_setid(adp, path->st.st_dev, path->st.st_ino, id, curdir->d_did, vol->v_stamp);
1003 while ( bitmap != 0 ) {
1004 while (( bitmap & 1 ) == 0 ) {
1011 ad_getattr(adp, &bshort);
1013 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1014 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1018 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1019 change_parent_mdate = 1;
1020 ad_setattr(adp, bshort);
1022 case FILPBIT_CDATE :
1023 ad_setdate(adp, AD_DATE_CREATE, cdate);
1025 case FILPBIT_MDATE :
1027 case FILPBIT_BDATE :
1028 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1030 case FILPBIT_FINFO :
1031 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1033 ((em = getextmap( path->m_name )) &&
1034 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1035 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1036 || ((em = getdefextmap()) &&
1037 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1038 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1040 memcpy(finder_buf, ufinderi, 8 );
1042 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1044 case FILPBIT_UNIXPR :
1046 setfilunixmode(vol, path, upriv);
1049 case FILPBIT_PDINFO :
1050 if (obj->afp_version < 30) { /* else it's UTF8 name */
1051 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1052 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1057 err = AFPERR_BITMAP;
1058 goto setfilparam_done;
1065 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1066 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1070 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1071 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1077 ad_close(adp, ADFLAGS_HF);
1080 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1081 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1082 bitmap = 1<<FILPBIT_MDATE;
1083 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1087 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1093 * renamefile and copyfile take the old and new unix pathnames
1094 * and the new mac name.
1096 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1097 * passing -1 means this is not used, src path is a full path
1098 * src the source path
1099 * dst the dest filename in current dir
1100 * newname the dest mac name
1101 * adp adouble struct of src file, if open, or & zeroed one
1104 int renamefile(struct vol *vol, struct dir *ddir, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1108 LOG(log_debug, logtype_afpd,
1109 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1111 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1114 return( AFPERR_NOOBJ );
1117 return( AFPERR_ACCESS );
1119 return AFPERR_VLOCK;
1120 case EXDEV : /* Cross device move -- try copy */
1121 /* NOTE: with open file it's an error because after the copy we will
1122 * get two files, it's fixable for our process (eg reopen the new file, get the
1123 * locks, and so on. But it doesn't solve the case with a second process
1125 if (adp->ad_open_forks) {
1126 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1127 return AFPERR_OLOCK; /* little lie */
1129 if (AFP_OK != ( rc = copyfile(vol, vol, ddir, sdir_fd, src, dst, newname, NULL )) ) {
1130 /* on error copyfile delete dest */
1133 return deletefile(vol, sdir_fd, src, 0);
1135 return( AFPERR_PARAM );
1139 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1143 /* try to undo the data fork rename,
1144 * we know we are on the same device
1147 unix_rename(-1, dst, sdir_fd, src );
1148 /* return the first error */
1151 return AFPERR_NOOBJ;
1154 return AFPERR_ACCESS ;
1156 return AFPERR_VLOCK;
1158 return AFPERR_PARAM ;
1163 /* don't care if we can't open the newly renamed ressource fork */
1164 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1165 ad_setname(adp, newname);
1167 ad_close( adp, ADFLAGS_HF );
1174 convert a Mac long name to an utf8 name,
1176 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1180 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1186 /* ---------------- */
1187 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1194 if ( type != 2 && !(vol->v_obj->afp_version >= 30 && type == 3) ) {
1200 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1201 if (vol->v_obj->afp_version >= 30) {
1202 /* convert it to UTF8
1204 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1208 strncpy( newname, ibuf, plen );
1209 newname[ plen ] = '\0';
1211 if (strlen(newname) != plen) {
1212 /* there's \0 in newname, e.g. it's a pathname not
1220 memcpy(&hint, ibuf, sizeof(hint));
1221 ibuf += sizeof(hint);
1223 memcpy(&len16, ibuf, sizeof(len16));
1224 ibuf += sizeof(len16);
1225 plen = ntohs(len16);
1228 if (plen > AFPOBJ_TMPSIZ) {
1231 strncpy( newname, ibuf, plen );
1232 newname[ plen ] = '\0';
1233 if (strlen(newname) != plen) {
1242 /* -----------------------------------
1244 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1246 struct vol *s_vol, *d_vol;
1248 char *newname, *p, *upath;
1249 struct path *s_path;
1250 uint32_t sdid, ddid;
1251 int err, retvalue = AFP_OK;
1252 uint16_t svid, dvid;
1254 struct adouble ad, *adp;
1260 memcpy(&svid, ibuf, sizeof( svid ));
1261 ibuf += sizeof( svid );
1262 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1263 return( AFPERR_PARAM );
1266 memcpy(&sdid, ibuf, sizeof( sdid ));
1267 ibuf += sizeof( sdid );
1268 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1272 memcpy(&dvid, ibuf, sizeof( dvid ));
1273 ibuf += sizeof( dvid );
1274 memcpy(&ddid, ibuf, sizeof( ddid ));
1275 ibuf += sizeof( ddid );
1277 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1278 return get_afp_errno(AFPERR_PARAM);
1280 if ( path_isadir(s_path) ) {
1281 return( AFPERR_BADTYPE );
1284 /* don't allow copies when the file is open.
1285 * XXX: the spec only calls for read/deny write access.
1286 * however, copyfile doesn't have any of that info,
1287 * and locks need to stay coherent. as a result,
1288 * we just balk if the file is opened already. */
1290 adp = of_ad(s_vol, s_path, &ad);
1292 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1293 return AFPERR_DENYCONF;
1295 #ifdef HAVE_FSHARE_T
1297 shmd.f_access = F_RDACC;
1298 shmd.f_deny = F_NODNY;
1299 if (fcntl(ad_data_fileno(adp), F_SHARE, &shmd) != 0) {
1300 retvalue = AFPERR_DENYCONF;
1303 if (AD_RSRC_OPEN(adp) && fcntl(ad_reso_fileno(adp), F_SHARE, &shmd) != 0) {
1304 retvalue = AFPERR_DENYCONF;
1308 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1309 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1312 retvalue = AFPERR_DENYCONF;
1316 newname = obj->newtmp;
1317 strcpy( newname, s_path->m_name );
1319 p = ctoupath( s_vol, curdir, newname );
1321 retvalue = AFPERR_PARAM;
1325 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1326 retvalue = AFPERR_PARAM;
1330 if (d_vol->v_flags & AFPVOL_RO) {
1331 retvalue = AFPERR_VLOCK;
1335 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1336 retvalue = afp_errno;
1340 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1341 retvalue = get_afp_errno(AFPERR_NOOBJ);
1345 if ( *s_path->m_name != '\0' ) {
1346 retvalue =path_error(s_path, AFPERR_NOOBJ);
1350 /* one of the handful of places that knows about the path type */
1351 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1352 retvalue = AFPERR_PARAM;
1355 /* newname is always only a filename so curdir *is* its
1358 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding(d_vol->v_obj)))) {
1359 retvalue =AFPERR_PARAM;
1363 if ( (err = copyfile(s_vol, d_vol, curdir, -1, p, upath , newname, adp)) < 0 ) {
1369 setvoltime(obj, d_vol );
1372 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1376 /* ----------------------------------
1377 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1378 * because we are doing it elsewhere.
1379 * currently if newname is NULL then adp is NULL.
1381 int copyfile(struct vol *s_vol,
1388 struct adouble *adp)
1390 struct adouble ads, add;
1397 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1398 sfd, src, dst, newname);
1401 ad_init(&ads, s_vol);
1405 adflags = ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RF | ADFLAGS_NORF;
1407 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1412 if (!AD_META_OPEN(adp))
1413 /* no resource fork, don't create one for dst file */
1414 adflags &= ~ADFLAGS_HF;
1416 if (!AD_RSRC_OPEN(adp))
1417 /* no resource fork, don't create one for dst file */
1418 adflags &= ~ADFLAGS_RF;
1420 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1422 if (stat_result < 0) {
1423 /* unlikely but if fstat fails, the default file mode will be 0666. */
1424 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1427 ad_init(&add, d_vol);
1428 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1430 ad_close( adp, adflags );
1431 if (EEXIST != ret_err) {
1432 deletefile(d_vol, -1, dst, 0);
1435 return AFPERR_EXIST;
1438 if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1439 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1442 if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1443 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1448 if (AD_META_OPEN(&add)) {
1449 if (AD_META_OPEN(adp))
1450 ad_copy_header(&add, adp);
1451 ad_setname(&add, dst);
1454 if (fstat(ad_meta_fileno(&add), &stdest) != 0) {
1458 if ((id = get_id(d_vol, &add, &stdest, d_dir->d_did, dst, strlen(dst))) == CNID_INVALID) {
1462 (void)ad_setid(&add, stdest.st_dev, stdest.st_ino, id, d_dir->d_did, d_vol->v_stamp);
1467 ad_close( adp, adflags );
1469 if (ad_close( &add, adflags ) <0) {
1474 deletefile(d_vol, -1, dst, 0);
1476 else if (stat_result == 0) {
1477 /* set dest modification date to src date */
1480 ut.actime = ut.modtime = st.st_mtime;
1482 /* FIXME netatalk doesn't use resource fork file date
1483 * but maybe we should set its modtime too.
1488 switch ( ret_err ) {
1494 LOG(log_info, logtype_afpd, "copyfile: DISK FULL");
1495 return AFPERR_DFULL;
1497 return AFPERR_NOOBJ;
1499 return AFPERR_ACCESS;
1501 return AFPERR_VLOCK;
1503 return AFPERR_PARAM;
1507 /* -----------------------------------
1508 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1509 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1511 when deletefile is called we don't have lock on it, file is closed (for us)
1512 untrue if called by renamefile
1514 ad_open always try to open file RDWR first and ad_lock takes care of
1515 WRITE lock on read only file.
1518 static int check_attrib(struct adouble *adp)
1520 uint16_t bshort = 0;
1522 ad_getattr(adp, &bshort);
1524 * Does kFPDeleteInhibitBit (bit 8) set?
1526 if ((bshort & htons(ATTRBIT_NODELETE))) {
1527 return AFPERR_OLOCK;
1529 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1535 * dirfd can be used for unlinkat semantics
1537 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1540 struct adouble *adp = NULL;
1541 int adflags, err = AFP_OK;
1544 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1548 /* was EACCESS error try to get only metadata */
1549 /* we never want to create a resource fork here, we are going to delete it
1550 * moreover sometimes deletefile is called with a no existent file and
1551 * ad_open would create a 0 byte resource fork
1553 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1554 if ((err = check_attrib(&ad))) {
1555 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1562 /* try to open both forks at once */
1563 adflags = ADFLAGS_DF;
1564 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1569 case EACCES: /* maybe it's a file with no write mode for us */
1570 break; /* was return AFPERR_ACCESS;*/
1583 if ( adp && AD_RSRC_OPEN(adp) ) { /* there's a resource fork */
1584 adflags |= ADFLAGS_RF;
1585 /* FIXME we have a pb here because we want to know if a file is open
1586 * there's a 'priority inversion' if you can't open the ressource fork RW
1587 * you can delete it if it's open because you can't get a write lock.
1589 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1592 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1594 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1600 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1601 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1603 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1605 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1606 cnid_delete(vol->v_cdb, id);
1612 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1615 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1620 /* ------------------------------------ */
1621 /* return a file id */
1622 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1631 struct path *s_path;
1637 memcpy(&vid, ibuf, sizeof(vid));
1638 ibuf += sizeof(vid);
1640 if (NULL == ( vol = getvolbyvid( vid )) ) {
1641 return( AFPERR_PARAM);
1644 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1648 if (vol->v_flags & AFPVOL_RO)
1649 return AFPERR_VLOCK;
1651 memcpy(&did, ibuf, sizeof( did ));
1652 ibuf += sizeof(did);
1654 if (NULL == ( dir = dirlookup( vol, did )) ) {
1655 return afp_errno; /* was AFPERR_PARAM */
1658 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1659 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1662 if ( path_isadir(s_path) ) {
1663 return( AFPERR_BADTYPE );
1666 upath = s_path->u_name;
1667 switch (s_path->st_errno) {
1669 break; /* success */
1672 return AFPERR_ACCESS;
1674 return AFPERR_NOOBJ;
1676 return AFPERR_PARAM;
1679 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1680 memcpy(rbuf, &id, sizeof(id));
1681 *rbuflen = sizeof(id);
1682 return AFPERR_EXISTID;
1685 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1686 memcpy(rbuf, &id, sizeof(id));
1687 *rbuflen = sizeof(id);
1694 /* ------------------------------- */
1700 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1703 struct reenum *param = data;
1704 struct vol *vol = param->vol;
1705 cnid_t did = param->did;
1708 if (ostat(de->d_name, &path.st, vol_syml_opt(vol)) < 0)
1711 /* update or add to cnid */
1712 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1717 /* --------------------
1718 * Ok the db is out of synch with the dir.
1719 * but if it's a deleted file we don't want to do it again and again.
1722 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1728 if (vol->v_cdb == NULL) {
1732 /* FIXME use of_statdir ? */
1733 if (ostat(name, &st, vol_syml_opt(vol))) {
1737 if (dirreenumerate(dir, &st)) {
1738 /* we already did it once and the dir haven't been modified */
1739 return dir->d_offcnt;
1743 data.did = dir->d_did;
1744 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1745 setdiroffcnt(curdir, &st, ret);
1746 dir->d_flags |= DIRF_CNID;
1752 /* ------------------------------
1753 resolve a file id */
1754 int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1763 uint16_t vid, bitmap;
1765 static char buffer[12 + MAXPATHLEN + 1];
1766 int len = 12 + MAXPATHLEN + 1;
1771 memcpy(&vid, ibuf, sizeof(vid));
1772 ibuf += sizeof(vid);
1774 if (NULL == ( vol = getvolbyvid( vid )) ) {
1775 return( AFPERR_PARAM);
1778 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1782 memcpy(&id, ibuf, sizeof( id ));
1787 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1791 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1792 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1795 if (NULL == ( dir = dirlookup( vol, id )) ) {
1796 return AFPERR_NOID; /* idem AFPERR_PARAM */
1798 if (movecwd(vol, dir) < 0) {
1802 return AFPERR_ACCESS;
1806 return AFPERR_PARAM;
1810 memset(&path, 0, sizeof(path));
1811 path.u_name = upath;
1812 if (of_stat(vol, &path) < 0 ) {
1814 /* with nfs and our working directory is deleted */
1815 if (errno == ESTALE) {
1819 if ( errno == ENOENT && !retry) {
1820 /* cnid db is out of sync, reenumerate the directory and update ids */
1821 reenumerate_id(vol, ".", dir);
1829 return AFPERR_ACCESS;
1833 return AFPERR_PARAM;
1837 /* directories are bad */
1838 if (S_ISDIR(path.st.st_mode)) {
1839 /* OS9 and OSX don't return the same error code */
1840 return (obj->afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1843 memcpy(&bitmap, ibuf, sizeof(bitmap));
1844 bitmap = ntohs( bitmap );
1845 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding(obj)))) {
1849 if (AFP_OK != (err = getfilparams(obj, vol, bitmap, &path , curdir,
1850 rbuf + sizeof(bitmap), &buflen, 0))) {
1853 *rbuflen = buflen + sizeof(bitmap);
1854 memcpy(rbuf, ibuf, sizeof(bitmap));
1859 /* ------------------------------ */
1860 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1870 static char buffer[12 + MAXPATHLEN + 1];
1871 int len = 12 + MAXPATHLEN + 1;
1876 memcpy(&vid, ibuf, sizeof(vid));
1877 ibuf += sizeof(vid);
1879 if (NULL == ( vol = getvolbyvid( vid )) ) {
1880 return( AFPERR_PARAM);
1883 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1887 if (vol->v_flags & AFPVOL_RO)
1888 return AFPERR_VLOCK;
1890 memcpy(&id, ibuf, sizeof( id ));
1894 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1898 if (NULL == ( dir = dirlookup( vol, id )) ) {
1899 if (afp_errno == AFPERR_NOOBJ) {
1903 return( AFPERR_PARAM );
1907 if ((movecwd(vol, dir) < 0) || (ostat(upath, &st, vol_syml_opt(vol)) < 0)) {
1911 return AFPERR_ACCESS;
1916 /* still try to delete the id */
1920 return AFPERR_PARAM;
1923 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1924 return AFPERR_BADTYPE;
1927 if (cnid_delete(vol->v_cdb, fileid)) {
1930 return AFPERR_VLOCK;
1933 return AFPERR_ACCESS;
1935 return AFPERR_PARAM;
1942 /* ------------------------------ */
1943 static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
1947 if (path->st_errno) {
1948 switch (path->st_errno) {
1950 afp_errno = AFPERR_NOID;
1954 afp_errno = AFPERR_ACCESS;
1957 afp_errno = AFPERR_PARAM;
1962 /* we use file_access both for legacy Mac perm and
1963 * for unix privilege, rename will take care of folder perms
1965 if (file_access(obj, vol, path, OPENACC_WR ) < 0) {
1966 afp_errno = AFPERR_ACCESS;
1970 if ((*of = of_findname(vol, path))) {
1971 /* reuse struct adouble so it won't break locks */
1975 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
1977 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1979 * The user must have the Read & Write privilege for both files in order to use this command.
1981 ad_close(adp, ADFLAGS_HF);
1982 afp_errno = AFPERR_ACCESS;
1989 #define APPLETEMP ".AppleTempXXXXXX"
1991 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1993 struct stat srcst, destst;
1995 struct dir *dir, *sdir;
1996 char *spath, temp[17], *p;
1997 char *supath, *upath;
2002 struct adouble *adsp = NULL;
2003 struct adouble *addp = NULL;
2004 struct ofork *s_of = NULL;
2005 struct ofork *d_of = NULL;
2015 memcpy(&vid, ibuf, sizeof(vid));
2016 ibuf += sizeof(vid);
2018 if (NULL == ( vol = getvolbyvid( vid )) ) {
2019 return( AFPERR_PARAM);
2022 if ((vol->v_flags & AFPVOL_RO))
2023 return AFPERR_VLOCK;
2025 /* source and destination dids */
2026 memcpy(&sid, ibuf, sizeof(sid));
2027 ibuf += sizeof(sid);
2028 memcpy(&did, ibuf, sizeof(did));
2029 ibuf += sizeof(did);
2032 if (NULL == (dir = dirlookup( vol, sid )) ) {
2033 return afp_errno; /* was AFPERR_PARAM */
2036 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2037 return get_afp_errno(AFPERR_NOOBJ);
2040 if ( path_isadir(path) ) {
2041 return AFPERR_BADTYPE; /* it's a dir */
2044 /* save some stuff */
2047 spath = obj->oldtmp;
2048 supath = obj->newtmp;
2049 strcpy(spath, path->m_name);
2050 strcpy(supath, path->u_name); /* this is for the cnid changing */
2051 p = absupath( vol, sdir, supath);
2053 /* pathname too long */
2054 return AFPERR_PARAM ;
2058 if (!(adsp = find_adouble(obj, vol, path, &s_of, &ads))) {
2062 /* ***** from here we may have resource fork open **** */
2064 /* look for the source cnid. if it doesn't exist, don't worry about
2066 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2068 if (NULL == ( dir = dirlookup( vol, did )) ) {
2069 err = afp_errno; /* was AFPERR_PARAM */
2070 goto err_exchangefile;
2073 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2074 err = get_afp_errno(AFPERR_NOOBJ);
2075 goto err_exchangefile;
2078 if ( path_isadir(path) ) {
2079 err = AFPERR_BADTYPE;
2080 goto err_exchangefile;
2083 /* FPExchangeFiles is the only call that can return the SameObj
2085 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2086 err = AFPERR_SAMEOBJ;
2087 goto err_exchangefile;
2091 if (!(addp = find_adouble(obj, vol, path, &d_of, &add))) {
2093 goto err_exchangefile;
2097 /* they are not on the same device and at least one is open
2098 * FIXME broken for for crossdev and adouble v2
2101 crossdev = (srcst.st_dev != destst.st_dev);
2102 if (/* (d_of || s_of) && */ crossdev) {
2104 goto err_exchangefile;
2107 /* look for destination id. */
2108 upath = path->u_name;
2109 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2111 /* construct a temp name.
2112 * NOTE: the temp file will be in the dest file's directory. it
2113 * will also be inaccessible from AFP. */
2114 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2116 if ((fd = mkstemp(temp)) == -1) {
2118 goto err_exchangefile;
2123 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2124 ad_close(adsp, ADFLAGS_HF);
2125 ad_close(addp, ADFLAGS_HF);
2128 /* now, quickly rename the file. we error if we can't. */
2129 if ((err = renamefile(vol, curdir, -1, p, temp, temp, adsp)) != AFP_OK)
2130 goto err_exchangefile;
2131 of_rename(vol, s_of, sdir, spath, curdir, temp);
2133 /* rename destination to source */
2134 if ((err = renamefile(vol, curdir, -1, upath, p, spath, addp)) != AFP_OK)
2135 goto err_src_to_tmp;
2136 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2138 /* rename temp to destination */
2139 if ((err = renamefile(vol, curdir, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2140 goto err_dest_to_src;
2141 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2143 /* id's need switching. src -> dest and dest -> src.
2144 * we need to re-stat() if it was a cross device copy.
2147 cnid_delete(vol->v_cdb, sid);
2149 cnid_delete(vol->v_cdb, did);
2151 if ((did && ( (crossdev && ostat(upath, &srcst, vol_syml_opt(vol)) < 0) ||
2152 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2154 (sid && ( (crossdev && ostat(p, &destst, vol_syml_opt(vol)) < 0) ||
2155 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2160 err = AFPERR_ACCESS;
2165 goto err_temp_to_dest;
2168 /* here we need to reopen if crossdev */
2169 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2174 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2179 /* change perms, src gets dest perm and vice versa */
2184 * we need to exchange ACL entries as well
2186 /* exchange_acls(vol, p, upath); */
2191 path->m_name = NULL;
2192 path->u_name = upath;
2194 setfilunixmode(vol, path, destst.st_mode);
2195 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2202 setfilunixmode(vol, path, srcst.st_mode);
2203 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2208 goto err_exchangefile;
2210 /* all this stuff is so that we can unwind a failed operation
2213 /* rename dest to temp */
2214 renamefile(vol, curdir, -1, upath, temp, temp, adsp);
2215 of_rename(vol, s_of, curdir, upath, curdir, temp);
2218 /* rename source back to dest */
2219 renamefile(vol, curdir, -1, p, upath, path->m_name, addp);
2220 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2223 /* rename temp back to source */
2224 renamefile(vol, curdir, -1, temp, p, spath, adsp);
2225 of_rename(vol, s_of, curdir, temp, sdir, spath);
2228 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2229 ad_close(adsp, ADFLAGS_HF);
2231 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2232 ad_close(addp, ADFLAGS_HF);
2236 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2237 (void)dir_remove(vol, cached);
2238 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2239 (void)dir_remove(vol, cached);