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>
28 #include "directory.h"
37 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
38 * field bytes subfield bytes
41 * ioFlFndrInfo 16 -> type 4 type field
42 * creator 4 creator field
43 * flags 2 finder flags:
45 * location 4 location in window
46 * folder 2 window that contains file
48 * ioFlXFndrInfo 16 -> iconID 2 icon id
50 * script 1 script system
52 * commentID 2 comment id
53 * putawayID 4 home directory id
56 const u_char ufinderi[ADEDLEN_FINDERI] = {
57 0, 0, 0, 0, 0, 0, 0, 0,
58 1, 0, 0, 0, 0, 0, 0, 0,
59 0, 0, 0, 0, 0, 0, 0, 0,
60 0, 0, 0, 0, 0, 0, 0, 0
63 static const u_char old_ufinderi[] = {
64 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
67 /* ----------------------
69 static int default_type(void *finder)
71 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
76 /* FIXME path : unix or mac name ? (for now it's unix name ) */
77 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
80 void *ad_finder = NULL;
84 ad_finder = ad_entry(adp, ADEID_FINDERI);
87 memcpy(data, ad_finder, ADEDLEN_FINDERI);
89 if (default_type(ad_finder))
93 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));
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);
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));
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()) {
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 /* deactivate cnid caching/storing in AppleDouble files and set ro mode*/
253 vol->v_flags &= ~AFPVOL_CACHE;
254 vol->v_flags |= AFPVOL_RO;
256 /* kill ourself with SIGUSR2 aka msg pending */
257 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
258 "Check server messages for details. Switching to read-only mode.");
259 kill(getpid(), SIGUSR2);
261 goto restart; /* not try again with the temp CNID db */
264 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
265 "Check server messages for details, can't recover from this state!");
269 afp_errno = AFPERR_MISC;
273 else if (adp && (adcnid != dbcnid)) { /* 4 */
274 /* Update the ressource fork. For a folder adp is always null */
275 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
276 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
277 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
288 /* -------------------------- */
289 int getmetadata(struct vol *vol,
291 struct path *path, struct dir *dir,
292 char *buf, size_t *buflen, struct adouble *adp)
294 char *data, *l_nameoff = NULL, *upath;
295 char *utf_nameoff = NULL;
300 u_char achar, fdType[4];
305 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
307 upath = path->u_name;
311 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
312 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
313 || (bitmap & (1 << FILPBIT_FNUM))) {
316 struct dir *cachedfile;
317 int len = strlen(upath);
318 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
319 id = cachedfile->d_did;
321 id = get_id(vol, adp, st, dir->d_did, upath, len);
323 /* Add it to the cache */
324 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
325 ntohl(dir->d_did), upath, ntohl(id));
327 /* Get macname from unixname first */
328 if (path->m_name == NULL) {
329 if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
330 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
336 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
337 || (bconchar(fullpath, '/') != BSTR_OK)
338 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
339 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
343 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
344 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
348 if ((dircache_add(vol, cachedfile)) != 0) {
349 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
357 if (id == CNID_INVALID)
361 path->m_name = utompath(vol, upath, id, utf8_encoding());
364 while ( bitmap != 0 ) {
365 while (( bitmap & 1 ) == 0 ) {
373 ad_getattr(adp, &ashort);
374 } else if (vol_inv_dots(vol) && *upath == '.') {
375 ashort = htons(ATTRBIT_INVISIBLE);
379 /* FIXME do we want a visual clue if the file is read only
382 accessmode(vol, ".", &ma, dir , NULL);
383 if ((ma.ma_user & AR_UWRITE)) {
384 accessmode(vol, upath, &ma, dir , st);
385 if (!(ma.ma_user & AR_UWRITE)) {
386 ashort |= htons(ATTRBIT_NOWRITE);
390 memcpy(data, &ashort, sizeof( ashort ));
391 data += sizeof( ashort );
392 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
393 path->u_name, ntohs(ashort));
397 memcpy(data, &dir->d_did, sizeof( uint32_t ));
398 data += sizeof( uint32_t );
399 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
400 path->u_name, ntohl(dir->d_did));
404 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
405 aint = AD_DATE_FROM_UNIX(st->st_mtime);
406 memcpy(data, &aint, sizeof( aint ));
407 data += sizeof( aint );
411 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
412 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
413 aint = AD_DATE_FROM_UNIX(st->st_mtime);
416 aint = AD_DATE_FROM_UNIX(st->st_mtime);
418 memcpy(data, &aint, sizeof( int ));
419 data += sizeof( int );
423 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
424 aint = AD_DATE_START;
425 memcpy(data, &aint, sizeof( int ));
426 data += sizeof( int );
430 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
431 data += ADEDLEN_FINDERI;
436 data += sizeof( uint16_t );
440 memset(data, 0, sizeof(uint16_t));
441 data += sizeof( uint16_t );
445 memcpy(data, &id, sizeof( id ));
446 data += sizeof( id );
447 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
448 path->u_name, ntohl(id));
452 if (st->st_size > 0xffffffff)
455 aint = htonl( st->st_size );
456 memcpy(data, &aint, sizeof( aint ));
457 data += sizeof( aint );
462 if (adp->ad_rlen > 0xffffffff)
465 aint = htonl( adp->ad_rlen);
469 memcpy(data, &aint, sizeof( aint ));
470 data += sizeof( aint );
473 /* Current client needs ProDOS info block for this file.
474 Use simple heuristic and let the Mac "type" string tell
475 us what the PD file code should be. Everything gets a
476 subtype of 0x0000 unless the original value was hashed
477 to "pXYZ" when we created it. See IA, Ver 2.
478 <shirsch@adelphia.net> */
479 case FILPBIT_PDINFO :
480 if (afp_version >= 30) { /* UTF8 name */
481 utf8 = kTextEncodingUTF8;
483 data += sizeof( uint16_t );
485 memcpy(data, &aint, sizeof( aint ));
486 data += sizeof( aint );
490 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
492 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
496 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
500 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
504 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
508 else if ( fdType[0] == 'p' ) {
510 ashort = (fdType[2] * 256) + fdType[3];
524 memcpy(data, &ashort, sizeof( ashort ));
525 data += sizeof( ashort );
526 memset(data, 0, sizeof( ashort ));
527 data += sizeof( ashort );
530 case FILPBIT_EXTDFLEN:
531 aint = htonl(st->st_size >> 32);
532 memcpy(data, &aint, sizeof( aint ));
533 data += sizeof( aint );
534 aint = htonl(st->st_size);
535 memcpy(data, &aint, sizeof( aint ));
536 data += sizeof( aint );
538 case FILPBIT_EXTRFLEN:
541 aint = htonl(adp->ad_rlen >> 32);
542 memcpy(data, &aint, sizeof( aint ));
543 data += sizeof( aint );
545 aint = htonl(adp->ad_rlen);
546 memcpy(data, &aint, sizeof( aint ));
547 data += sizeof( aint );
549 case FILPBIT_UNIXPR :
550 /* accessmode may change st_mode with ACLs */
551 accessmode(vol, upath, &ma, dir , st);
553 aint = htonl(st->st_uid);
554 memcpy( data, &aint, sizeof( aint ));
555 data += sizeof( aint );
556 aint = htonl(st->st_gid);
557 memcpy( data, &aint, sizeof( aint ));
558 data += sizeof( aint );
561 type == slnk indicates an OSX style symlink,
562 we have to add S_IFLNK to the mode, otherwise
563 10.3 clients freak out. */
567 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
568 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
574 memcpy( data, &aint, sizeof( aint ));
575 data += sizeof( aint );
577 *data++ = ma.ma_user;
578 *data++ = ma.ma_world;
579 *data++ = ma.ma_group;
580 *data++ = ma.ma_owner;
584 return( AFPERR_BITMAP );
590 ashort = htons( data - buf );
591 memcpy(l_nameoff, &ashort, sizeof( ashort ));
592 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
595 ashort = htons( data - buf );
596 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
597 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
599 *buflen = data - buf;
603 /* ----------------------- */
604 int getfilparams(struct vol *vol,
606 struct path *path, struct dir *dir,
607 char *buf, size_t *buflen )
609 struct adouble ad, *adp;
613 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
615 opened = PARAM_NEED_ADP(bitmap);
620 int flags = (bitmap & (1 << FILPBIT_ATTR)) ? ADFLAGS_CHECK_OF : 0;
622 adp = of_ad(vol, path, &ad);
623 upath = path->u_name;
625 if ( ad_metadata( upath, flags, adp) < 0 ) {
628 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
629 upath, strerror(errno));
630 return AFPERR_ACCESS;
632 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
641 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
642 ad_close(adp, ADFLAGS_HF);
647 /* ----------------------------- */
648 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
653 struct ofork *of = NULL;
655 int creatf, did, openf, retvalue = AFP_OK;
661 creatf = (unsigned char) *ibuf++;
663 memcpy(&vid, ibuf, sizeof( vid ));
664 ibuf += sizeof( vid );
666 if (NULL == ( vol = getvolbyvid( vid )) )
667 return( AFPERR_PARAM );
669 if (vol->v_flags & AFPVOL_RO)
672 memcpy(&did, ibuf, sizeof( did));
673 ibuf += sizeof( did );
675 if (NULL == ( dir = dirlookup( vol, did )) )
678 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
679 return get_afp_errno(AFPERR_PARAM);
681 if ( *s_path->m_name == '\0' )
682 return( AFPERR_BADTYPE );
684 upath = s_path->u_name;
687 /* if upath is deleted we already in trouble anyway */
688 if ((of = of_findname(s_path))) {
696 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
698 /* on a soft create, if the file is open then ad_open won't fail
699 because open syscall is not called */
700 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
702 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
706 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
707 return ( AFPERR_NOOBJ );
709 return( AFPERR_EXIST );
711 return( AFPERR_ACCESS );
714 return( AFPERR_DFULL );
716 return( AFPERR_PARAM );
719 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
720 /* on noadouble volumes, just creating the data fork is ok */
721 if (vol_noadouble(vol)) {
722 ad_close( &ad, ADFLAGS_DF );
723 goto createfile_done;
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);
744 (void)get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath));
747 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
748 fce_register_new_file(s_path);
753 setvoltime(obj, vol );
758 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
764 uint16_t vid, bitmap;
769 memcpy(&vid, ibuf, sizeof( vid ));
770 ibuf += sizeof( vid );
771 if (NULL == ( vol = getvolbyvid( vid )) ) {
772 return( AFPERR_PARAM );
775 if (vol->v_flags & AFPVOL_RO)
778 memcpy(&did, ibuf, sizeof( did ));
779 ibuf += sizeof( did );
780 if (NULL == ( dir = dirlookup( vol, did )) ) {
781 return afp_errno; /* was AFPERR_NOOBJ */
784 memcpy(&bitmap, ibuf, sizeof( bitmap ));
785 bitmap = ntohs( bitmap );
786 ibuf += sizeof( bitmap );
788 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
789 return get_afp_errno(AFPERR_PARAM);
792 if (path_isadir(s_path)) {
793 return( AFPERR_BADTYPE ); /* it's a directory */
796 if ( s_path->st_errno != 0 ) {
797 return( AFPERR_NOOBJ );
800 if ((u_long)ibuf & 1 ) {
804 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
805 setvoltime(obj, vol );
812 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
815 extern struct path Cur_Path;
817 int setfilparams(struct vol *vol,
818 struct path *path, uint16_t f_bitmap, char *buf )
820 struct adouble ad, *adp;
822 int bit, isad = 1, err = AFP_OK;
824 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
825 uint16_t ashort, bshort, oshort;
828 uint16_t upriv_bit = 0;
832 int change_mdate = 0;
833 int change_parent_mdate = 0;
838 uint16_t bitmap = f_bitmap;
839 uint32_t cdate,bdate;
840 u_char finder_buf[32];
843 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
846 adp = of_ad(vol, path, &ad);
847 upath = path->u_name;
849 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
850 return AFPERR_ACCESS;
853 /* with unix priv maybe we have to change adouble file priv first */
855 while ( bitmap != 0 ) {
856 while (( bitmap & 1 ) == 0 ) {
863 memcpy(&ashort, buf, sizeof( ashort ));
864 buf += sizeof( ashort );
868 memcpy(&cdate, buf, sizeof(cdate));
869 buf += sizeof( cdate );
872 memcpy(&newdate, buf, sizeof( newdate ));
873 buf += sizeof( newdate );
877 memcpy(&bdate, buf, sizeof( bdate));
878 buf += sizeof( bdate );
882 memcpy(finder_buf, buf, 32 );
883 if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
888 char buf[PATH_MAX+1];
889 if ((fp=open(path->u_name,O_RDONLY))>=0){
890 if ((len=read(fp,buf,PATH_MAX+1))){
891 if (unlink(path->u_name)==0){
893 erc = symlink(buf, path->u_name);
902 goto setfilparam_done;
907 case FILPBIT_UNIXPR :
908 if (!vol_unix_priv(vol)) {
909 /* this volume doesn't use unix priv */
915 change_parent_mdate = 1;
917 memcpy( &aint, buf, sizeof( aint ));
918 f_uid = ntohl (aint);
919 buf += sizeof( aint );
920 memcpy( &aint, buf, sizeof( aint ));
921 f_gid = ntohl (aint);
922 buf += sizeof( aint );
923 setfilowner(vol, f_uid, f_gid, path);
925 memcpy( &upriv, buf, sizeof( upriv ));
926 buf += sizeof( upriv );
927 upriv = ntohl (upriv);
928 if ((upriv & S_IWUSR)) {
929 setfilunixmode(vol, path, upriv);
936 case FILPBIT_PDINFO :
937 if (afp_version < 30) { /* else it's UTF8 name */
940 /* Keep special case to support crlf translations */
941 if ((unsigned int) achar == 0x04) {
942 fdType = (u_char *)"TEXT";
945 xyy[0] = ( u_char ) 'p';
956 /* break while loop */
965 /* second try with adouble open
967 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
968 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
970 * For some things, we don't need an adouble header:
971 * - change of modification date
972 * - UNIX privs (Bug-ID #2863424)
974 if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
975 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
976 return AFPERR_ACCESS;
978 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
980 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
981 ad_setname(adp, path->m_name);
986 while ( bitmap != 0 ) {
987 while (( bitmap & 1 ) == 0 ) {
994 ad_getattr(adp, &bshort);
996 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
997 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1001 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1002 change_parent_mdate = 1;
1003 ad_setattr(adp, bshort);
1005 case FILPBIT_CDATE :
1006 ad_setdate(adp, AD_DATE_CREATE, cdate);
1008 case FILPBIT_MDATE :
1010 case FILPBIT_BDATE :
1011 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1013 case FILPBIT_FINFO :
1014 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1016 ((em = getextmap( path->m_name )) &&
1017 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1018 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1019 || ((em = getdefextmap()) &&
1020 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1021 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1023 memcpy(finder_buf, ufinderi, 8 );
1025 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1027 case FILPBIT_UNIXPR :
1029 setfilunixmode(vol, path, upriv);
1032 case FILPBIT_PDINFO :
1033 if (afp_version < 30) { /* else it's UTF8 name */
1034 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1035 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1040 err = AFPERR_BITMAP;
1041 goto setfilparam_done;
1048 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1049 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1053 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1054 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1060 ad_close(adp, ADFLAGS_HF);
1063 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1064 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1065 bitmap = 1<<FILPBIT_MDATE;
1066 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1070 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1076 * renamefile and copyfile take the old and new unix pathnames
1077 * and the new mac name.
1079 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1080 * passing -1 means this is not used, src path is a full path
1081 * src the source path
1082 * dst the dest filename in current dir
1083 * newname the dest mac name
1084 * adp adouble struct of src file, if open, or & zeroed one
1087 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1091 LOG(log_debug, logtype_afpd,
1092 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1094 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1097 return( AFPERR_NOOBJ );
1100 return( AFPERR_ACCESS );
1102 return AFPERR_VLOCK;
1103 case EXDEV : /* Cross device move -- try copy */
1104 /* NOTE: with open file it's an error because after the copy we will
1105 * get two files, it's fixable for our process (eg reopen the new file, get the
1106 * locks, and so on. But it doesn't solve the case with a second process
1108 if (adp->ad_open_forks) {
1109 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1110 return AFPERR_OLOCK; /* little lie */
1112 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1113 /* on error copyfile delete dest */
1116 return deletefile(vol, sdir_fd, src, 0);
1118 return( AFPERR_PARAM );
1122 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1126 /* try to undo the data fork rename,
1127 * we know we are on the same device
1130 unix_rename(-1, dst, sdir_fd, src );
1131 /* return the first error */
1134 return AFPERR_NOOBJ;
1137 return AFPERR_ACCESS ;
1139 return AFPERR_VLOCK;
1141 return AFPERR_PARAM ;
1146 /* don't care if we can't open the newly renamed ressource fork */
1147 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1148 ad_setname(adp, newname);
1150 ad_close( adp, ADFLAGS_HF );
1157 convert a Mac long name to an utf8 name,
1159 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1163 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1169 /* ---------------- */
1170 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1177 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1183 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1184 if (afp_version >= 30) {
1185 /* convert it to UTF8
1187 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1191 strncpy( newname, ibuf, plen );
1192 newname[ plen ] = '\0';
1194 if (strlen(newname) != plen) {
1195 /* there's \0 in newname, e.g. it's a pathname not
1203 memcpy(&hint, ibuf, sizeof(hint));
1204 ibuf += sizeof(hint);
1206 memcpy(&len16, ibuf, sizeof(len16));
1207 ibuf += sizeof(len16);
1208 plen = ntohs(len16);
1211 if (plen > AFPOBJ_TMPSIZ) {
1214 strncpy( newname, ibuf, plen );
1215 newname[ plen ] = '\0';
1216 if (strlen(newname) != plen) {
1225 /* -----------------------------------
1227 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1229 struct vol *s_vol, *d_vol;
1231 char *newname, *p, *upath;
1232 struct path *s_path;
1233 uint32_t sdid, ddid;
1234 int err, retvalue = AFP_OK;
1235 uint16_t svid, dvid;
1237 struct adouble ad, *adp;
1243 memcpy(&svid, ibuf, sizeof( svid ));
1244 ibuf += sizeof( svid );
1245 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1246 return( AFPERR_PARAM );
1249 memcpy(&sdid, ibuf, sizeof( sdid ));
1250 ibuf += sizeof( sdid );
1251 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1255 memcpy(&dvid, ibuf, sizeof( dvid ));
1256 ibuf += sizeof( dvid );
1257 memcpy(&ddid, ibuf, sizeof( ddid ));
1258 ibuf += sizeof( ddid );
1260 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1261 return get_afp_errno(AFPERR_PARAM);
1263 if ( path_isadir(s_path) ) {
1264 return( AFPERR_BADTYPE );
1267 /* don't allow copies when the file is open.
1268 * XXX: the spec only calls for read/deny write access.
1269 * however, copyfile doesn't have any of that info,
1270 * and locks need to stay coherent. as a result,
1271 * we just balk if the file is opened already. */
1273 adp = of_ad(s_vol, s_path, &ad);
1275 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY) < 0) {
1276 return AFPERR_DENYCONF;
1278 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1279 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1282 retvalue = AFPERR_DENYCONF;
1286 newname = obj->newtmp;
1287 strcpy( newname, s_path->m_name );
1289 p = ctoupath( s_vol, curdir, newname );
1291 retvalue = AFPERR_PARAM;
1295 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1296 retvalue = AFPERR_PARAM;
1300 if (d_vol->v_flags & AFPVOL_RO) {
1301 retvalue = AFPERR_VLOCK;
1305 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1306 retvalue = afp_errno;
1310 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1311 retvalue = get_afp_errno(AFPERR_NOOBJ);
1315 if ( *s_path->m_name != '\0' ) {
1316 retvalue =path_error(s_path, AFPERR_NOOBJ);
1320 /* one of the handful of places that knows about the path type */
1321 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1322 retvalue = AFPERR_PARAM;
1325 /* newname is always only a filename so curdir *is* its
1328 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1329 retvalue =AFPERR_PARAM;
1333 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1339 setvoltime(obj, d_vol );
1342 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1346 /* ----------------------- */
1347 static int copy_all(const int dfd, const void *buf,
1353 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1356 while (buflen > 0) {
1357 if ((cc = write(dfd, buf, buflen)) < 0) {
1369 LOG(log_debug9, logtype_afpd, "end copy_all:");
1375 /* --------------------------
1376 * copy only the fork data stream
1378 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1386 if (eid == ADEID_DFORK) {
1387 sfd = ad_data_fileno(ads);
1388 dfd = ad_data_fileno(add);
1391 sfd = ad_reso_fileno(ads);
1392 dfd = ad_reso_fileno(add);
1395 if (add->ad_version == AD_VERSION2)
1396 soff = doff = ad_getentryoff(ads, eid);
1398 if (eid == ADEID_DFORK)
1399 soff = doff = ad_getentryoff(ads, eid);
1404 soff = doff = ADEDOFF_RFORK_OSX;
1409 if ((off_t)-1 == lseek(sfd, soff, SEEK_SET))
1412 if ((off_t)-1 == lseek(dfd, doff, SEEK_SET))
1415 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1416 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1420 #define BUF 128*1024*1024
1422 if (fstat(sfd, &st) == 0) {
1425 if ( offset >= st.st_size) {
1428 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1429 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1432 case EINVAL: /* there's no guarantee that all fs support sendfile */
1441 lseek(sfd, offset, SEEK_SET);
1445 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1452 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1459 /* ----------------------------------
1460 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1461 * because we are doing it elsewhere.
1462 * currently if newname is NULL then adp is NULL.
1464 int copyfile(const struct vol *s_vol,
1465 const struct vol *d_vol,
1470 struct adouble *adp)
1472 struct adouble ads, add;
1479 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1480 sfd, src, dst, newname);
1483 ad_init(&ads, s_vol);
1487 adflags = ADFLAGS_DF | ADFLAGS_RF | ADFLAGS_NORF;
1489 adflags |= ADFLAGS_HF;
1492 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_NOHF | ADFLAGS_RDONLY) < 0) {
1497 if (!AD_RSRC_OPEN(adp))
1498 /* no resource fork, don't create one for dst file */
1499 adflags &= ~ADFLAGS_RF;
1501 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1503 if (stat_result < 0) {
1504 /* unlikely but if fstat fails, the default file mode will be 0666. */
1505 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1508 ad_init(&add, d_vol);
1509 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode) < 0) {
1511 ad_close( adp, adflags );
1512 if (EEXIST != ret_err) {
1513 deletefile(d_vol, -1, dst, 0);
1516 return AFPERR_EXIST;
1519 if (AD_RSRC_OPEN(adp))
1520 err = copy_fork(ADEID_RFORK, &add, adp);
1523 err = copy_fork(ADEID_DFORK, &add, adp);
1528 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1529 /* set the new name in the resource fork */
1530 ad_copy_header(&add, adp);
1531 ad_setname(&add, newname);
1534 ad_close( adp, adflags );
1536 if (ad_close( &add, adflags ) <0) {
1541 deletefile(d_vol, -1, dst, 0);
1543 else if (stat_result == 0) {
1544 /* set dest modification date to src date */
1547 ut.actime = ut.modtime = st.st_mtime;
1549 /* FIXME netatalk doesn't use resource fork file date
1550 * but maybe we should set its modtime too.
1555 switch ( ret_err ) {
1561 return AFPERR_DFULL;
1563 return AFPERR_NOOBJ;
1565 return AFPERR_ACCESS;
1567 return AFPERR_VLOCK;
1569 return AFPERR_PARAM;
1573 /* -----------------------------------
1574 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1575 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1577 when deletefile is called we don't have lock on it, file is closed (for us)
1578 untrue if called by renamefile
1580 ad_open always try to open file RDWR first and ad_lock takes care of
1581 WRITE lock on read only file.
1584 static int check_attrib(struct adouble *adp)
1586 uint16_t bshort = 0;
1588 ad_getattr(adp, &bshort);
1590 * Does kFPDeleteInhibitBit (bit 8) set?
1592 if ((bshort & htons(ATTRBIT_NODELETE))) {
1593 return AFPERR_OLOCK;
1595 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1601 * dirfd can be used for unlinkat semantics
1603 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1606 struct adouble *adp = NULL;
1607 int adflags, err = AFP_OK;
1610 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1614 /* was EACCESS error try to get only metadata */
1615 /* we never want to create a resource fork here, we are going to delete it
1616 * moreover sometimes deletefile is called with a no existent file and
1617 * ad_open would create a 0 byte resource fork
1619 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1620 if ((err = check_attrib(&ad))) {
1621 ad_close(&ad, ADFLAGS_HF);
1628 /* try to open both forks at once */
1629 adflags = ADFLAGS_DF;
1630 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1635 case EACCES: /* maybe it's a file with no write mode for us */
1636 break; /* was return AFPERR_ACCESS;*/
1649 if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1650 adflags |= ADFLAGS_RF;
1651 /* FIXME we have a pb here because we want to know if a file is open
1652 * there's a 'priority inversion' if you can't open the ressource fork RW
1653 * you can delete it if it's open because you can't get a write lock.
1655 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1658 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1660 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1666 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1667 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1669 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1671 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1672 cnid_delete(vol->v_cdb, id);
1678 ad_close(&ad, ADFLAGS_HF);
1681 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1686 /* ------------------------------------ */
1687 /* return a file id */
1688 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1697 struct path *s_path;
1703 memcpy(&vid, ibuf, sizeof(vid));
1704 ibuf += sizeof(vid);
1706 if (NULL == ( vol = getvolbyvid( vid )) ) {
1707 return( AFPERR_PARAM);
1710 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1714 if (vol->v_flags & AFPVOL_RO)
1715 return AFPERR_VLOCK;
1717 memcpy(&did, ibuf, sizeof( did ));
1718 ibuf += sizeof(did);
1720 if (NULL == ( dir = dirlookup( vol, did )) ) {
1721 return afp_errno; /* was AFPERR_PARAM */
1724 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1725 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1728 if ( path_isadir(s_path) ) {
1729 return( AFPERR_BADTYPE );
1732 upath = s_path->u_name;
1733 switch (s_path->st_errno) {
1735 break; /* success */
1738 return AFPERR_ACCESS;
1740 return AFPERR_NOOBJ;
1742 return AFPERR_PARAM;
1745 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1746 memcpy(rbuf, &id, sizeof(id));
1747 *rbuflen = sizeof(id);
1748 return AFPERR_EXISTID;
1751 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1752 memcpy(rbuf, &id, sizeof(id));
1753 *rbuflen = sizeof(id);
1760 /* ------------------------------- */
1766 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1769 struct reenum *param = data;
1770 struct vol *vol = param->vol;
1771 cnid_t did = param->did;
1774 if ( lstat(de->d_name, &path.st) < 0 )
1777 /* update or add to cnid */
1778 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1783 /* --------------------
1784 * Ok the db is out of synch with the dir.
1785 * but if it's a deleted file we don't want to do it again and again.
1788 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1794 if (vol->v_cdb == NULL) {
1798 /* FIXME use of_statdir ? */
1799 if (lstat(name, &st)) {
1803 if (dirreenumerate(dir, &st)) {
1804 /* we already did it once and the dir haven't been modified */
1805 return dir->d_offcnt;
1809 data.did = dir->d_did;
1810 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1811 setdiroffcnt(curdir, &st, ret);
1812 dir->d_flags |= DIRF_CNID;
1818 /* ------------------------------
1819 resolve a file id */
1820 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1829 uint16_t vid, bitmap;
1831 static char buffer[12 + MAXPATHLEN + 1];
1832 int len = 12 + MAXPATHLEN + 1;
1837 memcpy(&vid, ibuf, sizeof(vid));
1838 ibuf += sizeof(vid);
1840 if (NULL == ( vol = getvolbyvid( vid )) ) {
1841 return( AFPERR_PARAM);
1844 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1848 memcpy(&id, ibuf, sizeof( id ));
1853 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1857 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1858 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1861 if (NULL == ( dir = dirlookup( vol, id )) ) {
1862 return AFPERR_NOID; /* idem AFPERR_PARAM */
1864 if (movecwd(vol, dir) < 0) {
1868 return AFPERR_ACCESS;
1872 return AFPERR_PARAM;
1876 memset(&path, 0, sizeof(path));
1877 path.u_name = upath;
1878 if ( of_stat(&path) < 0 ) {
1880 /* with nfs and our working directory is deleted */
1881 if (errno == ESTALE) {
1885 if ( errno == ENOENT && !retry) {
1886 /* cnid db is out of sync, reenumerate the directory and update ids */
1887 reenumerate_id(vol, ".", dir);
1895 return AFPERR_ACCESS;
1899 return AFPERR_PARAM;
1903 /* directories are bad */
1904 if (S_ISDIR(path.st.st_mode)) {
1905 /* OS9 and OSX don't return the same error code */
1906 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1909 memcpy(&bitmap, ibuf, sizeof(bitmap));
1910 bitmap = ntohs( bitmap );
1911 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1915 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1916 rbuf + sizeof(bitmap), &buflen))) {
1919 *rbuflen = buflen + sizeof(bitmap);
1920 memcpy(rbuf, ibuf, sizeof(bitmap));
1925 /* ------------------------------ */
1926 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1936 static char buffer[12 + MAXPATHLEN + 1];
1937 int len = 12 + MAXPATHLEN + 1;
1942 memcpy(&vid, ibuf, sizeof(vid));
1943 ibuf += sizeof(vid);
1945 if (NULL == ( vol = getvolbyvid( vid )) ) {
1946 return( AFPERR_PARAM);
1949 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1953 if (vol->v_flags & AFPVOL_RO)
1954 return AFPERR_VLOCK;
1956 memcpy(&id, ibuf, sizeof( id ));
1960 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1964 if (NULL == ( dir = dirlookup( vol, id )) ) {
1965 if (afp_errno == AFPERR_NOOBJ) {
1969 return( AFPERR_PARAM );
1973 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1977 return AFPERR_ACCESS;
1982 /* still try to delete the id */
1986 return AFPERR_PARAM;
1989 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1990 return AFPERR_BADTYPE;
1993 if (cnid_delete(vol->v_cdb, fileid)) {
1996 return AFPERR_VLOCK;
1999 return AFPERR_ACCESS;
2001 return AFPERR_PARAM;
2008 /* ------------------------------ */
2009 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
2013 if (path->st_errno) {
2014 switch (path->st_errno) {
2016 afp_errno = AFPERR_NOID;
2020 afp_errno = AFPERR_ACCESS;
2023 afp_errno = AFPERR_PARAM;
2028 /* we use file_access both for legacy Mac perm and
2029 * for unix privilege, rename will take care of folder perms
2031 if (file_access(path, OPENACC_WR ) < 0) {
2032 afp_errno = AFPERR_ACCESS;
2036 if ((*of = of_findname(path))) {
2037 /* reuse struct adouble so it won't break locks */
2041 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
2043 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2045 * The user must have the Read & Write privilege for both files in order to use this command.
2047 ad_close(adp, ADFLAGS_HF);
2048 afp_errno = AFPERR_ACCESS;
2055 #define APPLETEMP ".AppleTempXXXXXX"
2057 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2059 struct stat srcst, destst;
2061 struct dir *dir, *sdir;
2062 char *spath, temp[17], *p;
2063 char *supath, *upath;
2068 struct adouble *adsp = NULL;
2069 struct adouble *addp = NULL;
2070 struct ofork *s_of = NULL;
2071 struct ofork *d_of = NULL;
2084 memcpy(&vid, ibuf, sizeof(vid));
2085 ibuf += sizeof(vid);
2087 if (NULL == ( vol = getvolbyvid( vid )) ) {
2088 return( AFPERR_PARAM);
2091 if ((vol->v_flags & AFPVOL_RO))
2092 return AFPERR_VLOCK;
2094 /* source and destination dids */
2095 memcpy(&sid, ibuf, sizeof(sid));
2096 ibuf += sizeof(sid);
2097 memcpy(&did, ibuf, sizeof(did));
2098 ibuf += sizeof(did);
2101 if (NULL == (dir = dirlookup( vol, sid )) ) {
2102 return afp_errno; /* was AFPERR_PARAM */
2105 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2106 return get_afp_errno(AFPERR_NOOBJ);
2109 if ( path_isadir(path) ) {
2110 return AFPERR_BADTYPE; /* it's a dir */
2113 /* save some stuff */
2116 spath = obj->oldtmp;
2117 supath = obj->newtmp;
2118 strcpy(spath, path->m_name);
2119 strcpy(supath, path->u_name); /* this is for the cnid changing */
2120 p = absupath( vol, sdir, supath);
2122 /* pathname too long */
2123 return AFPERR_PARAM ;
2127 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2131 /* ***** from here we may have resource fork open **** */
2133 /* look for the source cnid. if it doesn't exist, don't worry about
2135 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2137 if (NULL == ( dir = dirlookup( vol, did )) ) {
2138 err = afp_errno; /* was AFPERR_PARAM */
2139 goto err_exchangefile;
2142 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2143 err = get_afp_errno(AFPERR_NOOBJ);
2144 goto err_exchangefile;
2147 if ( path_isadir(path) ) {
2148 err = AFPERR_BADTYPE;
2149 goto err_exchangefile;
2152 /* FPExchangeFiles is the only call that can return the SameObj
2154 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2155 err = AFPERR_SAMEOBJ;
2156 goto err_exchangefile;
2160 if (!(addp = find_adouble( path, &d_of, &add))) {
2162 goto err_exchangefile;
2166 /* they are not on the same device and at least one is open
2167 * FIXME broken for for crossdev and adouble v2
2170 crossdev = (srcst.st_dev != destst.st_dev);
2171 if (/* (d_of || s_of) && */ crossdev) {
2173 goto err_exchangefile;
2176 /* look for destination id. */
2177 upath = path->u_name;
2178 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2180 /* construct a temp name.
2181 * NOTE: the temp file will be in the dest file's directory. it
2182 * will also be inaccessible from AFP. */
2183 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2184 if (!mktemp(temp)) {
2186 goto err_exchangefile;
2190 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2191 ad_close(adsp, ADFLAGS_HF);
2192 ad_close(addp, ADFLAGS_HF);
2195 /* now, quickly rename the file. we error if we can't. */
2196 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2197 goto err_exchangefile;
2198 of_rename(vol, s_of, sdir, spath, curdir, temp);
2200 /* rename destination to source */
2201 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2202 goto err_src_to_tmp;
2203 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2205 /* rename temp to destination */
2206 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2207 goto err_dest_to_src;
2208 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2210 /* id's need switching. src -> dest and dest -> src.
2211 * we need to re-stat() if it was a cross device copy.
2214 cnid_delete(vol->v_cdb, sid);
2216 cnid_delete(vol->v_cdb, did);
2218 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2219 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2221 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2222 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2227 err = AFPERR_ACCESS;
2232 goto err_temp_to_dest;
2235 /* here we need to reopen if crossdev */
2236 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2241 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2246 /* change perms, src gets dest perm and vice versa */
2251 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2252 err = AFP_OK; /* ignore error */
2253 goto err_temp_to_dest;
2257 * we need to exchange ACL entries as well
2259 /* exchange_acls(vol, p, upath); */
2264 path->m_name = NULL;
2265 path->u_name = upath;
2267 setfilunixmode(vol, path, destst.st_mode);
2268 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2275 setfilunixmode(vol, path, srcst.st_mode);
2276 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2278 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2279 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2284 goto err_exchangefile;
2286 /* all this stuff is so that we can unwind a failed operation
2289 /* rename dest to temp */
2290 renamefile(vol, -1, upath, temp, temp, adsp);
2291 of_rename(vol, s_of, curdir, upath, curdir, temp);
2294 /* rename source back to dest */
2295 renamefile(vol, -1, p, upath, path->m_name, addp);
2296 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2299 /* rename temp back to source */
2300 renamefile(vol, -1, temp, p, spath, adsp);
2301 of_rename(vol, s_of, curdir, temp, sdir, spath);
2304 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2305 ad_close(adsp, ADFLAGS_HF);
2307 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2308 ad_close(addp, ADFLAGS_HF);
2312 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2313 (void)dir_remove(vol, cached);
2314 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2315 (void)dir_remove(vol, cached);