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);
95 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
98 ashort = htons(FINDERINFO_INVISIBLE);
99 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
103 if (islink && !vol_syml_opt(vol)) {
105 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
106 linkflag |= htons(FINDERINFO_ISALIAS);
107 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
108 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
109 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
112 /** Only enter if no appledouble information and no finder information found. */
113 if (chk_ext && (em = getextmap( upath ))) {
114 memcpy(data, em->em_type, sizeof( em->em_type ));
115 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
121 /* ---------------------
123 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, uint32_t utf8)
128 aint = strlen( name );
132 if (utf8_encoding(vol->v_obj)) {
133 /* but name is an utf8 mac name */
136 /* global static variable... */
138 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
147 if (aint > MACFILELEN)
154 if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
155 aint = UTF8FILELEN_EARLY;
157 utf8 = vol->v_kTextEncoding;
158 memcpy(data, &utf8, sizeof(utf8));
159 data += sizeof(utf8);
162 memcpy(data, &temp, sizeof(temp));
163 data += sizeof(temp);
166 memcpy( data, src, aint );
176 * FIXME: PDINFO is UTF8 and doesn't need adp
178 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
179 (1 << FILPBIT_CDATE) |\
180 (1 << FILPBIT_MDATE) |\
181 (1 << FILPBIT_BDATE) |\
182 (1 << FILPBIT_FINFO) |\
183 (1 << FILPBIT_RFLEN) |\
184 (1 << FILPBIT_EXTRFLEN) |\
185 (1 << FILPBIT_PDINFO) |\
186 (1 << FILPBIT_FNUM) |\
187 (1 << FILPBIT_UNIXPR)))
190 * @brief Get CNID for did/upath args both from database and adouble file
192 * 1. Get the objects CNID as stored in its adouble file
193 * 2. Get the objects CNID from the database
194 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
195 * 4. In case 2 and 3 differ, store 3 in the adouble file
197 * @param vol (rw) volume
198 * @param adp (rw) adouble struct of object upath, might be NULL
199 * @param st (r) stat of upath, must NOT be NULL
200 * @param did (r) parent CNID of upath
201 * @param upath (r) name of object
202 * @param len (r) strlen of upath
204 uint32_t get_id(struct vol *vol,
206 const struct stat *st,
211 static int first = 1; /* mark if this func is called the first time */
213 uint32_t dbcnid = CNID_INVALID;
216 if (vol->v_cdb != NULL) {
217 /* prime aint with what we think is the cnid, set did to zero for
218 catching moved files */
219 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
221 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
222 /* Throw errors if cnid_add fails. */
223 if (dbcnid == CNID_INVALID) {
225 case CNID_ERR_CLOSE: /* the db is closed */
228 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
229 afp_errno = AFPERR_PARAM;
232 afp_errno = AFPERR_PARAM;
235 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
236 /* we have to do it here for "dbd" because it uses "lazy opening" */
237 /* In order to not end in a loop somehow with goto restart below */
239 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
240 cnid_close(vol->v_cdb);
241 free(vol->v_cnidscheme);
242 vol->v_cnidscheme = strdup("tdb");
244 int flags = CNID_FLAG_MEMORY;
245 if ((vol->v_flags & AFPVOL_NODEV)) {
246 flags |= CNID_FLAG_NODEV;
248 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
250 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
252 if (!(vol->v_flags & AFPVOL_TM)) {
253 vol->v_flags |= AFPVOL_RO;
254 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
255 "Check server messages for details. Switching to read-only mode.");
256 kill(getpid(), SIGUSR2);
258 goto restart; /* now try again with the temp CNID db */
260 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
261 "Check server messages for details, can't recover from this state!");
264 afp_errno = AFPERR_MISC;
268 else if (adp && adcnid && (adcnid != dbcnid)) { /* 4 */
269 /* Update the ressource fork. For a folder adp is always null */
270 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
271 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
272 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
273 if (ad_flush(adp) != 0)
274 LOG(log_error, logtype_afpd, "get_id(\"%s\"): can't flush", fullpathname(upath));
284 /* -------------------------- */
285 int getmetadata(const AFPObj *obj,
288 struct path *path, struct dir *dir,
289 char *buf, size_t *buflen, struct adouble *adp)
291 char *data, *l_nameoff = NULL, *upath;
292 char *utf_nameoff = NULL;
297 u_char achar, fdType[4];
302 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
304 upath = path->u_name;
308 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
309 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding(obj)) /* FIXME should be m_name utf8 filename */
310 || (bitmap & (1 << FILPBIT_FNUM))) {
313 struct dir *cachedfile;
314 int len = strlen(upath);
315 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
316 id = cachedfile->d_did;
318 id = get_id(vol, adp, st, dir->d_did, upath, len);
320 /* Add it to the cache */
321 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
322 ntohl(dir->d_did), upath, ntohl(id));
324 /* Get macname from unixname first */
325 if (path->m_name == NULL) {
326 if ((path->m_name = utompath(vol, upath, id, utf8_encoding(obj))) == NULL) {
327 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
333 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
334 || (bconchar(fullpath, '/') != BSTR_OK)
335 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
336 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
340 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
341 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
345 if ((dircache_add(vol, cachedfile)) != 0) {
346 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
354 if (id == CNID_INVALID)
358 path->m_name = utompath(vol, upath, id, utf8_encoding(vol->v_obj));
361 while ( bitmap != 0 ) {
362 while (( bitmap & 1 ) == 0 ) {
370 ad_getattr(adp, &ashort);
371 } else if (vol_inv_dots(vol) && *upath == '.') {
372 ashort = htons(ATTRBIT_INVISIBLE);
376 /* FIXME do we want a visual clue if the file is read only
379 accessmode(vol, ".", &ma, dir , NULL);
380 if ((ma.ma_user & AR_UWRITE)) {
381 accessmode(vol, upath, &ma, dir , st);
382 if (!(ma.ma_user & AR_UWRITE)) {
383 ashort |= htons(ATTRBIT_NOWRITE);
387 memcpy(data, &ashort, sizeof( ashort ));
388 data += sizeof( ashort );
389 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
390 path->u_name, ntohs(ashort));
394 memcpy(data, &dir->d_did, sizeof( uint32_t ));
395 data += sizeof( uint32_t );
396 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
397 path->u_name, ntohl(dir->d_did));
401 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
402 aint = AD_DATE_FROM_UNIX(st->st_mtime);
403 memcpy(data, &aint, sizeof( aint ));
404 data += sizeof( aint );
408 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
409 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
410 aint = AD_DATE_FROM_UNIX(st->st_mtime);
413 aint = AD_DATE_FROM_UNIX(st->st_mtime);
415 memcpy(data, &aint, sizeof( int ));
416 data += sizeof( int );
420 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
421 aint = AD_DATE_START;
422 memcpy(data, &aint, sizeof( int ));
423 data += sizeof( int );
427 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
428 data += ADEDLEN_FINDERI;
433 data += sizeof( uint16_t );
437 memset(data, 0, sizeof(uint16_t));
438 data += sizeof( uint16_t );
442 memcpy(data, &id, sizeof( id ));
443 data += sizeof( id );
444 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
445 path->u_name, ntohl(id));
449 if (st->st_size > 0xffffffff)
452 aint = htonl( st->st_size );
453 memcpy(data, &aint, sizeof( aint ));
454 data += sizeof( aint );
459 if (adp->ad_rlen > 0xffffffff)
462 aint = htonl( adp->ad_rlen);
466 memcpy(data, &aint, sizeof( aint ));
467 data += sizeof( aint );
470 /* Current client needs ProDOS info block for this file.
471 Use simple heuristic and let the Mac "type" string tell
472 us what the PD file code should be. Everything gets a
473 subtype of 0x0000 unless the original value was hashed
474 to "pXYZ" when we created it. See IA, Ver 2.
475 <shirsch@adelphia.net> */
476 case FILPBIT_PDINFO :
477 if (obj->afp_version >= 30) { /* UTF8 name */
478 utf8 = kTextEncodingUTF8;
480 data += sizeof( uint16_t );
482 memcpy(data, &aint, sizeof( aint ));
483 data += sizeof( aint );
487 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
489 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
493 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
497 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
501 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
505 else if ( fdType[0] == 'p' ) {
507 ashort = (fdType[2] * 256) + fdType[3];
521 memcpy(data, &ashort, sizeof( ashort ));
522 data += sizeof( ashort );
523 memset(data, 0, sizeof( ashort ));
524 data += sizeof( ashort );
527 case FILPBIT_EXTDFLEN:
528 aint = htonl(st->st_size >> 32);
529 memcpy(data, &aint, sizeof( aint ));
530 data += sizeof( aint );
531 aint = htonl(st->st_size);
532 memcpy(data, &aint, sizeof( aint ));
533 data += sizeof( aint );
535 case FILPBIT_EXTRFLEN:
538 aint = htonl(adp->ad_rlen >> 32);
539 memcpy(data, &aint, sizeof( aint ));
540 data += sizeof( aint );
542 aint = htonl(adp->ad_rlen);
543 memcpy(data, &aint, sizeof( aint ));
544 data += sizeof( aint );
546 case FILPBIT_UNIXPR :
547 /* accessmode may change st_mode with ACLs */
548 accessmode(obj, vol, upath, &ma, dir , st);
550 aint = htonl(st->st_uid);
551 memcpy( data, &aint, sizeof( aint ));
552 data += sizeof( aint );
553 aint = htonl(st->st_gid);
554 memcpy( data, &aint, sizeof( aint ));
555 data += sizeof( aint );
558 type == slnk indicates an OSX style symlink,
559 we have to add S_IFLNK to the mode, otherwise
560 10.3 clients freak out. */
564 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
565 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
571 memcpy( data, &aint, sizeof( aint ));
572 data += sizeof( aint );
574 *data++ = ma.ma_user;
575 *data++ = ma.ma_world;
576 *data++ = ma.ma_group;
577 *data++ = ma.ma_owner;
581 return( AFPERR_BITMAP );
587 ashort = htons( data - buf );
588 memcpy(l_nameoff, &ashort, sizeof( ashort ));
589 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
592 ashort = htons( data - buf );
593 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
594 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
596 *buflen = data - buf;
600 /* ----------------------- */
601 int getfilparams(const AFPObj *obj, struct vol *vol, uint16_t bitmap, struct path *path,
602 struct dir *dir, char *buf, size_t *buflen, int in_enumerate)
604 struct adouble ad, *adp;
607 int flags; /* uninitialized ok */
609 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
611 opened = PARAM_NEED_ADP(bitmap);
617 * Dont check for and resturn open fork attributes when enumerating
618 * This saves a lot of syscalls, the client will hopefully only use the result
619 * in FPGetFileParms where we return the correct value
621 flags = (!in_enumerate &&(bitmap & (1 << FILPBIT_ATTR))) ? ADFLAGS_CHECK_OF : 0;
623 adp = of_ad(vol, path, &ad);
624 upath = path->u_name;
626 if ( ad_metadata( upath, flags, adp) < 0 ) {
629 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
630 upath, strerror(errno));
631 return AFPERR_ACCESS;
633 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
642 rc = getmetadata(obj, vol, bitmap, path, dir, buf, buflen, adp);
645 ad_close(adp, ADFLAGS_HF | flags);
650 /* ----------------------------- */
651 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
656 struct ofork *of = NULL;
658 int creatf, did, openf, retvalue = AFP_OK;
664 creatf = (unsigned char) *ibuf++;
666 memcpy(&vid, ibuf, sizeof( vid ));
667 ibuf += sizeof( vid );
669 if (NULL == ( vol = getvolbyvid( vid )) )
670 return( AFPERR_PARAM );
672 if (vol->v_flags & AFPVOL_RO)
675 memcpy(&did, ibuf, sizeof( did));
676 ibuf += sizeof( did );
678 if (NULL == ( dir = dirlookup( vol, did )) )
681 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
682 return get_afp_errno(AFPERR_PARAM);
683 if ( *s_path->m_name == '\0' )
684 return( AFPERR_BADTYPE );
686 upath = s_path->u_name;
689 /* if upath is deleted we already in trouble anyway */
690 if ((of = of_findname(vol, s_path))) {
698 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
700 /* on a soft create, if the file is open then ad_open won't fail
701 because open syscall is not called */
702 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
704 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
708 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
709 return ( AFPERR_NOOBJ );
711 return( AFPERR_EXIST );
713 return( AFPERR_ACCESS );
716 LOG(log_info, logtype_afpd, "afp_createfile: DISK FULL");
717 return( AFPERR_DFULL );
719 return( AFPERR_PARAM );
722 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
723 /* FIXME with hard create on an existing file, we already
724 * corrupted the data file.
726 netatalk_unlink( upath );
727 ad_close( &ad, ADFLAGS_DF );
728 return AFPERR_ACCESS;
731 path = s_path->m_name;
732 ad_setname(&ad, path);
735 if (lstat(upath, &st) != 0) {
736 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
737 upath, strerror(errno));
738 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
743 if ((id = get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath))) == CNID_INVALID) {
744 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
745 goto createfile_iderr;
747 (void)ad_setid(&ad, st.st_dev, st.st_ino, id, dir->d_did, vol->v_stamp);
751 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
752 fce_register(FCE_FILE_CREATE, fullpathname(upath), NULL, fce_file);
756 setvoltime(obj, vol );
761 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
767 uint16_t vid, bitmap;
772 memcpy(&vid, ibuf, sizeof( vid ));
773 ibuf += sizeof( vid );
774 if (NULL == ( vol = getvolbyvid( vid )) ) {
775 return( AFPERR_PARAM );
778 if (vol->v_flags & AFPVOL_RO)
781 memcpy(&did, ibuf, sizeof( did ));
782 ibuf += sizeof( did );
783 if (NULL == ( dir = dirlookup( vol, did )) ) {
784 return afp_errno; /* was AFPERR_NOOBJ */
787 memcpy(&bitmap, ibuf, sizeof( bitmap ));
788 bitmap = ntohs( bitmap );
789 ibuf += sizeof( bitmap );
791 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
792 return get_afp_errno(AFPERR_PARAM);
795 if (path_isadir(s_path)) {
796 return( AFPERR_BADTYPE ); /* it's a directory */
799 if ( s_path->st_errno != 0 ) {
800 return( AFPERR_NOOBJ );
803 if ((u_long)ibuf & 1 ) {
807 if (AFP_OK == ( rc = setfilparams(obj, vol, s_path, bitmap, ibuf )) ) {
808 setvoltime(obj, vol );
815 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
818 extern struct path Cur_Path;
820 int setfilparams(const AFPObj *obj, struct vol *vol,
821 struct path *path, uint16_t f_bitmap, char *buf )
823 struct adouble ad, *adp;
825 int bit, isad = 1, err = AFP_OK;
827 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
828 uint16_t ashort, bshort, oshort;
831 uint16_t upriv_bit = 0;
833 int change_mdate = 0;
834 int change_parent_mdate = 0;
839 uint16_t bitmap = f_bitmap;
840 uint32_t cdate,bdate;
841 u_char finder_buf[32];
842 int symlinked = S_ISLNK(path->st.st_mode);
845 char symbuf[MAXPATHLEN+1];
848 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
851 adp = of_ad(vol, path, &ad);
852 upath = path->u_name;
854 if (!vol_unix_priv(vol) && check_access(obj, vol, upath, OPENACC_WR ) < 0) {
855 return AFPERR_ACCESS;
858 /* with unix priv maybe we have to change adouble file priv first */
860 while ( bitmap != 0 ) {
861 while (( bitmap & 1 ) == 0 ) {
868 memcpy(&ashort, buf, sizeof( ashort ));
869 buf += sizeof( ashort );
873 memcpy(&cdate, buf, sizeof(cdate));
874 buf += sizeof( cdate );
877 memcpy(&newdate, buf, sizeof( newdate ));
878 buf += sizeof( newdate );
882 memcpy(&bdate, buf, sizeof( bdate));
883 buf += sizeof( bdate );
887 if (memcmp(buf,"slnkrhap",8) == 0
888 && !(S_ISLNK(path->st.st_mode))
889 && !(vol->v_flags & AFPVOL_FOLLOWSYM)) {
890 /* request to turn this into a symlink */
891 if ((fp = open(path->u_name, O_RDONLY)) == -1) {
893 goto setfilparam_done;
895 len = read(fp, symbuf, MAXPATHLEN);
899 goto setfilparam_done;
901 if (unlink(path->u_name) != 0) {
903 goto setfilparam_done;
906 if (symlink(symbuf, path->u_name) != 0) {
908 goto setfilparam_done;
913 memcpy(finder_buf, buf, 32 );
916 case FILPBIT_UNIXPR :
917 if (!vol_unix_priv(vol)) {
918 /* this volume doesn't use unix priv */
924 change_parent_mdate = 1;
926 memcpy( &aint, buf, sizeof( aint ));
927 f_uid = ntohl (aint);
928 buf += sizeof( aint );
929 memcpy( &aint, buf, sizeof( aint ));
930 f_gid = ntohl (aint);
931 buf += sizeof( aint );
932 setfilowner(vol, f_uid, f_gid, path);
934 memcpy( &upriv, buf, sizeof( upriv ));
935 buf += sizeof( upriv );
936 upriv = ntohl (upriv);
937 if ((upriv & S_IWUSR)) {
938 setfilunixmode(vol, path, upriv);
945 case FILPBIT_PDINFO :
946 if (obj->afp_version < 30) { /* else it's UTF8 name */
949 /* Keep special case to support crlf translations */
950 if ((unsigned int) achar == 0x04) {
951 fdType = (u_char *)"TEXT";
954 xyy[0] = ( u_char ) 'p';
965 /* break while loop */
974 /* second try with adouble open
976 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
977 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
979 * For some things, we don't need an adouble header:
980 * - change of modification date
981 * - UNIX privs (Bug-ID #2863424)
983 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
984 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
985 return AFPERR_ACCESS;
987 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
989 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
990 ad_setname(adp, path->m_name);
992 if ((id = get_id(vol, adp, &path->st, curdir->d_did, upath, strlen(upath))) == CNID_INVALID) {
993 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
996 (void)ad_setid(adp, path->st.st_dev, path->st.st_ino, id, curdir->d_did, vol->v_stamp);
1001 while ( bitmap != 0 ) {
1002 while (( bitmap & 1 ) == 0 ) {
1009 ad_getattr(adp, &bshort);
1011 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1012 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1016 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1017 change_parent_mdate = 1;
1018 ad_setattr(adp, bshort);
1020 case FILPBIT_CDATE :
1021 ad_setdate(adp, AD_DATE_CREATE, cdate);
1023 case FILPBIT_MDATE :
1025 case FILPBIT_BDATE :
1026 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1028 case FILPBIT_FINFO :
1029 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1031 ((em = getextmap( path->m_name )) &&
1032 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1033 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1034 || ((em = getdefextmap()) &&
1035 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1036 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1038 memcpy(finder_buf, ufinderi, 8 );
1040 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1042 case FILPBIT_UNIXPR :
1044 setfilunixmode(vol, path, upriv);
1047 case FILPBIT_PDINFO :
1048 if (obj->afp_version < 30) { /* else it's UTF8 name */
1049 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1050 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1055 err = AFPERR_BITMAP;
1056 goto setfilparam_done;
1063 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1064 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1068 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1069 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1075 ad_close(adp, ADFLAGS_HF);
1078 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1079 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1080 bitmap = 1<<FILPBIT_MDATE;
1081 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1085 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1091 * renamefile and copyfile take the old and new unix pathnames
1092 * and the new mac name.
1094 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1095 * passing -1 means this is not used, src path is a full path
1096 * src the source path
1097 * dst the dest filename in current dir
1098 * newname the dest mac name
1099 * adp adouble struct of src file, if open, or & zeroed one
1102 int renamefile(struct vol *vol, struct dir *ddir, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1106 LOG(log_debug, logtype_afpd,
1107 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1109 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1112 return( AFPERR_NOOBJ );
1115 return( AFPERR_ACCESS );
1117 return AFPERR_VLOCK;
1118 case EXDEV : /* Cross device move -- try copy */
1119 /* NOTE: with open file it's an error because after the copy we will
1120 * get two files, it's fixable for our process (eg reopen the new file, get the
1121 * locks, and so on. But it doesn't solve the case with a second process
1123 if (adp->ad_open_forks) {
1124 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1125 return AFPERR_OLOCK; /* little lie */
1127 if (AFP_OK != ( rc = copyfile(vol, vol, ddir, sdir_fd, src, dst, newname, NULL )) ) {
1128 /* on error copyfile delete dest */
1131 return deletefile(vol, sdir_fd, src, 0);
1133 return( AFPERR_PARAM );
1137 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1141 /* try to undo the data fork rename,
1142 * we know we are on the same device
1145 unix_rename(-1, dst, sdir_fd, src );
1146 /* return the first error */
1149 return AFPERR_NOOBJ;
1152 return AFPERR_ACCESS ;
1154 return AFPERR_VLOCK;
1156 return AFPERR_PARAM ;
1161 /* don't care if we can't open the newly renamed ressource fork */
1162 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1163 ad_setname(adp, newname);
1165 ad_close( adp, ADFLAGS_HF );
1172 convert a Mac long name to an utf8 name,
1174 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1178 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1184 /* ---------------- */
1185 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1192 if ( type != 2 && !(vol->v_obj->afp_version >= 30 && type == 3) ) {
1198 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1199 if (vol->v_obj->afp_version >= 30) {
1200 /* convert it to UTF8
1202 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1206 strncpy( newname, ibuf, plen );
1207 newname[ plen ] = '\0';
1209 if (strlen(newname) != plen) {
1210 /* there's \0 in newname, e.g. it's a pathname not
1218 memcpy(&hint, ibuf, sizeof(hint));
1219 ibuf += sizeof(hint);
1221 memcpy(&len16, ibuf, sizeof(len16));
1222 ibuf += sizeof(len16);
1223 plen = ntohs(len16);
1226 if (plen > AFPOBJ_TMPSIZ) {
1229 strncpy( newname, ibuf, plen );
1230 newname[ plen ] = '\0';
1231 if (strlen(newname) != plen) {
1240 /* -----------------------------------
1242 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1244 struct vol *s_vol, *d_vol;
1246 char *newname, *p, *upath;
1247 struct path *s_path;
1248 uint32_t sdid, ddid;
1249 int err, retvalue = AFP_OK;
1250 uint16_t svid, dvid;
1252 struct adouble ad, *adp;
1258 memcpy(&svid, ibuf, sizeof( svid ));
1259 ibuf += sizeof( svid );
1260 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1261 return( AFPERR_PARAM );
1264 memcpy(&sdid, ibuf, sizeof( sdid ));
1265 ibuf += sizeof( sdid );
1266 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1270 memcpy(&dvid, ibuf, sizeof( dvid ));
1271 ibuf += sizeof( dvid );
1272 memcpy(&ddid, ibuf, sizeof( ddid ));
1273 ibuf += sizeof( ddid );
1275 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1276 return get_afp_errno(AFPERR_PARAM);
1278 if ( path_isadir(s_path) ) {
1279 return( AFPERR_BADTYPE );
1282 /* don't allow copies when the file is open.
1283 * XXX: the spec only calls for read/deny write access.
1284 * however, copyfile doesn't have any of that info,
1285 * and locks need to stay coherent. as a result,
1286 * we just balk if the file is opened already. */
1288 adp = of_ad(s_vol, s_path, &ad);
1290 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1291 return AFPERR_DENYCONF;
1293 #ifdef HAVE_FSHARE_T
1295 shmd.f_access = F_RDACC;
1296 shmd.f_deny = F_NODNY;
1297 if (fcntl(ad_data_fileno(adp), F_SHARE, &shmd) != 0) {
1298 retvalue = AFPERR_DENYCONF;
1301 if (AD_RSRC_OPEN(adp) && fcntl(ad_reso_fileno(adp), F_SHARE, &shmd) != 0) {
1302 retvalue = AFPERR_DENYCONF;
1306 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1307 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1310 retvalue = AFPERR_DENYCONF;
1314 newname = obj->newtmp;
1315 strcpy( newname, s_path->m_name );
1317 p = ctoupath( s_vol, curdir, newname );
1319 retvalue = AFPERR_PARAM;
1323 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1324 retvalue = AFPERR_PARAM;
1328 if (d_vol->v_flags & AFPVOL_RO) {
1329 retvalue = AFPERR_VLOCK;
1333 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1334 retvalue = afp_errno;
1338 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1339 retvalue = get_afp_errno(AFPERR_NOOBJ);
1343 if ( *s_path->m_name != '\0' ) {
1344 retvalue =path_error(s_path, AFPERR_NOOBJ);
1348 /* one of the handful of places that knows about the path type */
1349 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1350 retvalue = AFPERR_PARAM;
1353 /* newname is always only a filename so curdir *is* its
1356 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding(d_vol->v_obj)))) {
1357 retvalue =AFPERR_PARAM;
1361 if ( (err = copyfile(s_vol, d_vol, curdir, -1, p, upath , newname, adp)) < 0 ) {
1367 setvoltime(obj, d_vol );
1370 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1374 /* ----------------------------------
1375 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1376 * because we are doing it elsewhere.
1377 * currently if newname is NULL then adp is NULL.
1379 int copyfile(struct vol *s_vol,
1386 struct adouble *adp)
1388 struct adouble ads, add;
1395 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1396 sfd, src, dst, newname);
1399 ad_init(&ads, s_vol);
1403 adflags = ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RF | ADFLAGS_NORF;
1405 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1410 if (!AD_META_OPEN(adp))
1411 /* no resource fork, don't create one for dst file */
1412 adflags &= ~ADFLAGS_HF;
1414 if (!AD_RSRC_OPEN(adp))
1415 /* no resource fork, don't create one for dst file */
1416 adflags &= ~ADFLAGS_RF;
1418 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1420 if (stat_result < 0) {
1421 /* unlikely but if fstat fails, the default file mode will be 0666. */
1422 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1425 ad_init(&add, d_vol);
1426 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1428 ad_close( adp, adflags );
1429 if (EEXIST != ret_err) {
1430 deletefile(d_vol, -1, dst, 0);
1433 return AFPERR_EXIST;
1436 if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1437 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1440 if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1441 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1446 if (AD_META_OPEN(&add)) {
1447 if (AD_META_OPEN(adp))
1448 ad_copy_header(&add, adp);
1449 ad_setname(&add, dst);
1452 if (fstat(ad_meta_fileno(&add), &stdest) != 0) {
1456 if ((id = get_id(d_vol, &add, &stdest, d_dir->d_did, dst, strlen(dst))) == CNID_INVALID) {
1460 (void)ad_setid(&add, stdest.st_dev, stdest.st_ino, id, d_dir->d_did, d_vol->v_stamp);
1465 ad_close( adp, adflags );
1467 if (ad_close( &add, adflags ) <0) {
1472 deletefile(d_vol, -1, dst, 0);
1474 else if (stat_result == 0) {
1475 /* set dest modification date to src date */
1478 ut.actime = ut.modtime = st.st_mtime;
1480 /* FIXME netatalk doesn't use resource fork file date
1481 * but maybe we should set its modtime too.
1486 switch ( ret_err ) {
1492 LOG(log_info, logtype_afpd, "copyfile: DISK FULL");
1493 return AFPERR_DFULL;
1495 return AFPERR_NOOBJ;
1497 return AFPERR_ACCESS;
1499 return AFPERR_VLOCK;
1501 return AFPERR_PARAM;
1505 /* -----------------------------------
1506 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1507 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1509 when deletefile is called we don't have lock on it, file is closed (for us)
1510 untrue if called by renamefile
1512 ad_open always try to open file RDWR first and ad_lock takes care of
1513 WRITE lock on read only file.
1516 static int check_attrib(struct adouble *adp)
1518 uint16_t bshort = 0;
1520 ad_getattr(adp, &bshort);
1522 * Does kFPDeleteInhibitBit (bit 8) set?
1524 if ((bshort & htons(ATTRBIT_NODELETE))) {
1525 return AFPERR_OLOCK;
1527 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1533 * dirfd can be used for unlinkat semantics
1535 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1538 struct adouble *adp = NULL;
1539 int adflags, err = AFP_OK;
1542 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1546 /* was EACCESS error try to get only metadata */
1547 /* we never want to create a resource fork here, we are going to delete it
1548 * moreover sometimes deletefile is called with a no existent file and
1549 * ad_open would create a 0 byte resource fork
1551 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1552 if ((err = check_attrib(&ad))) {
1553 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1560 /* try to open both forks at once */
1561 adflags = ADFLAGS_DF;
1562 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1567 case EACCES: /* maybe it's a file with no write mode for us */
1568 break; /* was return AFPERR_ACCESS;*/
1581 if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1582 adflags |= ADFLAGS_RF;
1583 /* FIXME we have a pb here because we want to know if a file is open
1584 * there's a 'priority inversion' if you can't open the ressource fork RW
1585 * you can delete it if it's open because you can't get a write lock.
1587 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1590 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1592 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1598 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1599 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1601 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1603 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1604 cnid_delete(vol->v_cdb, id);
1610 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1613 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1618 /* ------------------------------------ */
1619 /* return a file id */
1620 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1629 struct path *s_path;
1635 memcpy(&vid, ibuf, sizeof(vid));
1636 ibuf += sizeof(vid);
1638 if (NULL == ( vol = getvolbyvid( vid )) ) {
1639 return( AFPERR_PARAM);
1642 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1646 if (vol->v_flags & AFPVOL_RO)
1647 return AFPERR_VLOCK;
1649 memcpy(&did, ibuf, sizeof( did ));
1650 ibuf += sizeof(did);
1652 if (NULL == ( dir = dirlookup( vol, did )) ) {
1653 return afp_errno; /* was AFPERR_PARAM */
1656 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1657 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1660 if ( path_isadir(s_path) ) {
1661 return( AFPERR_BADTYPE );
1664 upath = s_path->u_name;
1665 switch (s_path->st_errno) {
1667 break; /* success */
1670 return AFPERR_ACCESS;
1672 return AFPERR_NOOBJ;
1674 return AFPERR_PARAM;
1677 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1678 memcpy(rbuf, &id, sizeof(id));
1679 *rbuflen = sizeof(id);
1680 return AFPERR_EXISTID;
1683 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1684 memcpy(rbuf, &id, sizeof(id));
1685 *rbuflen = sizeof(id);
1692 /* ------------------------------- */
1698 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1701 struct reenum *param = data;
1702 struct vol *vol = param->vol;
1703 cnid_t did = param->did;
1706 if (ostat(de->d_name, &path.st, vol_syml_opt(vol)) < 0)
1709 /* update or add to cnid */
1710 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1715 /* --------------------
1716 * Ok the db is out of synch with the dir.
1717 * but if it's a deleted file we don't want to do it again and again.
1720 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1726 if (vol->v_cdb == NULL) {
1730 /* FIXME use of_statdir ? */
1731 if (ostat(name, &st, vol_syml_opt(vol))) {
1735 if (dirreenumerate(dir, &st)) {
1736 /* we already did it once and the dir haven't been modified */
1737 return dir->d_offcnt;
1741 data.did = dir->d_did;
1742 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1743 setdiroffcnt(curdir, &st, ret);
1744 dir->d_flags |= DIRF_CNID;
1750 /* ------------------------------
1751 resolve a file id */
1752 int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1761 uint16_t vid, bitmap;
1763 static char buffer[12 + MAXPATHLEN + 1];
1764 int len = 12 + MAXPATHLEN + 1;
1769 memcpy(&vid, ibuf, sizeof(vid));
1770 ibuf += sizeof(vid);
1772 if (NULL == ( vol = getvolbyvid( vid )) ) {
1773 return( AFPERR_PARAM);
1776 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1780 memcpy(&id, ibuf, sizeof( id ));
1785 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1789 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1790 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1793 if (NULL == ( dir = dirlookup( vol, id )) ) {
1794 return AFPERR_NOID; /* idem AFPERR_PARAM */
1796 if (movecwd(vol, dir) < 0) {
1800 return AFPERR_ACCESS;
1804 return AFPERR_PARAM;
1808 memset(&path, 0, sizeof(path));
1809 path.u_name = upath;
1810 if (of_stat(vol, &path) < 0 ) {
1812 /* with nfs and our working directory is deleted */
1813 if (errno == ESTALE) {
1817 if ( errno == ENOENT && !retry) {
1818 /* cnid db is out of sync, reenumerate the directory and update ids */
1819 reenumerate_id(vol, ".", dir);
1827 return AFPERR_ACCESS;
1831 return AFPERR_PARAM;
1835 /* directories are bad */
1836 if (S_ISDIR(path.st.st_mode)) {
1837 /* OS9 and OSX don't return the same error code */
1838 return (obj->afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1841 memcpy(&bitmap, ibuf, sizeof(bitmap));
1842 bitmap = ntohs( bitmap );
1843 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding(obj)))) {
1847 if (AFP_OK != (err = getfilparams(obj, vol, bitmap, &path , curdir,
1848 rbuf + sizeof(bitmap), &buflen, 0))) {
1851 *rbuflen = buflen + sizeof(bitmap);
1852 memcpy(rbuf, ibuf, sizeof(bitmap));
1857 /* ------------------------------ */
1858 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1868 static char buffer[12 + MAXPATHLEN + 1];
1869 int len = 12 + MAXPATHLEN + 1;
1874 memcpy(&vid, ibuf, sizeof(vid));
1875 ibuf += sizeof(vid);
1877 if (NULL == ( vol = getvolbyvid( vid )) ) {
1878 return( AFPERR_PARAM);
1881 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1885 if (vol->v_flags & AFPVOL_RO)
1886 return AFPERR_VLOCK;
1888 memcpy(&id, ibuf, sizeof( id ));
1892 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1896 if (NULL == ( dir = dirlookup( vol, id )) ) {
1897 if (afp_errno == AFPERR_NOOBJ) {
1901 return( AFPERR_PARAM );
1905 if ((movecwd(vol, dir) < 0) || (ostat(upath, &st, vol_syml_opt(vol)) < 0)) {
1909 return AFPERR_ACCESS;
1914 /* still try to delete the id */
1918 return AFPERR_PARAM;
1921 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1922 return AFPERR_BADTYPE;
1925 if (cnid_delete(vol->v_cdb, fileid)) {
1928 return AFPERR_VLOCK;
1931 return AFPERR_ACCESS;
1933 return AFPERR_PARAM;
1940 /* ------------------------------ */
1941 static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
1945 if (path->st_errno) {
1946 switch (path->st_errno) {
1948 afp_errno = AFPERR_NOID;
1952 afp_errno = AFPERR_ACCESS;
1955 afp_errno = AFPERR_PARAM;
1960 /* we use file_access both for legacy Mac perm and
1961 * for unix privilege, rename will take care of folder perms
1963 if (file_access(obj, vol, path, OPENACC_WR ) < 0) {
1964 afp_errno = AFPERR_ACCESS;
1968 if ((*of = of_findname(vol, path))) {
1969 /* reuse struct adouble so it won't break locks */
1973 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
1975 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1977 * The user must have the Read & Write privilege for both files in order to use this command.
1979 ad_close(adp, ADFLAGS_HF);
1980 afp_errno = AFPERR_ACCESS;
1987 #define APPLETEMP ".AppleTempXXXXXX"
1989 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1991 struct stat srcst, destst;
1993 struct dir *dir, *sdir;
1994 char *spath, temp[17], *p;
1995 char *supath, *upath;
2000 struct adouble *adsp = NULL;
2001 struct adouble *addp = NULL;
2002 struct ofork *s_of = NULL;
2003 struct ofork *d_of = NULL;
2013 memcpy(&vid, ibuf, sizeof(vid));
2014 ibuf += sizeof(vid);
2016 if (NULL == ( vol = getvolbyvid( vid )) ) {
2017 return( AFPERR_PARAM);
2020 if ((vol->v_flags & AFPVOL_RO))
2021 return AFPERR_VLOCK;
2023 /* source and destination dids */
2024 memcpy(&sid, ibuf, sizeof(sid));
2025 ibuf += sizeof(sid);
2026 memcpy(&did, ibuf, sizeof(did));
2027 ibuf += sizeof(did);
2030 if (NULL == (dir = dirlookup( vol, sid )) ) {
2031 return afp_errno; /* was AFPERR_PARAM */
2034 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2035 return get_afp_errno(AFPERR_NOOBJ);
2038 if ( path_isadir(path) ) {
2039 return AFPERR_BADTYPE; /* it's a dir */
2042 /* save some stuff */
2045 spath = obj->oldtmp;
2046 supath = obj->newtmp;
2047 strcpy(spath, path->m_name);
2048 strcpy(supath, path->u_name); /* this is for the cnid changing */
2049 p = absupath( vol, sdir, supath);
2051 /* pathname too long */
2052 return AFPERR_PARAM ;
2056 if (!(adsp = find_adouble(obj, vol, path, &s_of, &ads))) {
2060 /* ***** from here we may have resource fork open **** */
2062 /* look for the source cnid. if it doesn't exist, don't worry about
2064 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2066 if (NULL == ( dir = dirlookup( vol, did )) ) {
2067 err = afp_errno; /* was AFPERR_PARAM */
2068 goto err_exchangefile;
2071 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2072 err = get_afp_errno(AFPERR_NOOBJ);
2073 goto err_exchangefile;
2076 if ( path_isadir(path) ) {
2077 err = AFPERR_BADTYPE;
2078 goto err_exchangefile;
2081 /* FPExchangeFiles is the only call that can return the SameObj
2083 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2084 err = AFPERR_SAMEOBJ;
2085 goto err_exchangefile;
2089 if (!(addp = find_adouble(obj, vol, path, &d_of, &add))) {
2091 goto err_exchangefile;
2095 /* they are not on the same device and at least one is open
2096 * FIXME broken for for crossdev and adouble v2
2099 crossdev = (srcst.st_dev != destst.st_dev);
2100 if (/* (d_of || s_of) && */ crossdev) {
2102 goto err_exchangefile;
2105 /* look for destination id. */
2106 upath = path->u_name;
2107 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2109 /* construct a temp name.
2110 * NOTE: the temp file will be in the dest file's directory. it
2111 * will also be inaccessible from AFP. */
2112 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2114 if ((fd = mkstemp(temp)) == -1) {
2116 goto err_exchangefile;
2121 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2122 ad_close(adsp, ADFLAGS_HF);
2123 ad_close(addp, ADFLAGS_HF);
2126 /* now, quickly rename the file. we error if we can't. */
2127 if ((err = renamefile(vol, curdir, -1, p, temp, temp, adsp)) != AFP_OK)
2128 goto err_exchangefile;
2129 of_rename(vol, s_of, sdir, spath, curdir, temp);
2131 /* rename destination to source */
2132 if ((err = renamefile(vol, curdir, -1, upath, p, spath, addp)) != AFP_OK)
2133 goto err_src_to_tmp;
2134 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2136 /* rename temp to destination */
2137 if ((err = renamefile(vol, curdir, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2138 goto err_dest_to_src;
2139 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2141 /* id's need switching. src -> dest and dest -> src.
2142 * we need to re-stat() if it was a cross device copy.
2145 cnid_delete(vol->v_cdb, sid);
2147 cnid_delete(vol->v_cdb, did);
2149 if ((did && ( (crossdev && ostat(upath, &srcst, vol_syml_opt(vol)) < 0) ||
2150 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2152 (sid && ( (crossdev && ostat(p, &destst, vol_syml_opt(vol)) < 0) ||
2153 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2158 err = AFPERR_ACCESS;
2163 goto err_temp_to_dest;
2166 /* here we need to reopen if crossdev */
2167 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2172 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2177 /* change perms, src gets dest perm and vice versa */
2182 * we need to exchange ACL entries as well
2184 /* exchange_acls(vol, p, upath); */
2189 path->m_name = NULL;
2190 path->u_name = upath;
2192 setfilunixmode(vol, path, destst.st_mode);
2193 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2200 setfilunixmode(vol, path, srcst.st_mode);
2201 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2206 goto err_exchangefile;
2208 /* all this stuff is so that we can unwind a failed operation
2211 /* rename dest to temp */
2212 renamefile(vol, curdir, -1, upath, temp, temp, adsp);
2213 of_rename(vol, s_of, curdir, upath, curdir, temp);
2216 /* rename source back to dest */
2217 renamefile(vol, curdir, -1, p, upath, path->m_name, addp);
2218 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2221 /* rename temp back to source */
2222 renamefile(vol, curdir, -1, temp, p, spath, adsp);
2223 of_rename(vol, s_of, curdir, temp, sdir, spath);
2226 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2227 ad_close(adsp, ADFLAGS_HF);
2229 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2230 ad_close(addp, ADFLAGS_HF);
2234 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2235 (void)dir_remove(vol, cached);
2236 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2237 (void)dir_remove(vol, cached);