/* * This file has been modified for the cdrkit suite. * * The behaviour and appearence of the program code below can differ to a major * extent from the version distributed by the original author(s). * * For details, see Changelog file distributed with the cdrkit package. If you * received this file from another source then ask the distributing person for * a log of modifications. * */ /* @(#)apple.c 1.19 04/03/02 joerg, Copyright 1997, 1998, 1999, 2000 James Pearson */ /* * Copyright (c) 1997, 1998, 1999, 2000 James Pearson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Unix-HFS file interface including maping file extensions to TYPE/CREATOR * * Adapted from mkhfs routines for mkhybrid * * James Pearson 1/5/97 * Bug fix JCP 4/12/97 * Updated for 1.12 and added more Unix HFS filetypes. JCP 21/1/98 * Tidy up to use Finfo and Dinfo for all formats where * possible JCP 25/4/2000 * * Things still to de done: * * Check file size = finder + rsrc [+ data] is needed */ #ifdef APPLE_HYB #include #include "genisoimage.h" #include #include #include #include #include #include "apple.h" #include #ifdef USE_MAGIC #include magic_t magic_state = NULL; #ifndef MAGIC_ERROR /* workaround for older ´API */ #define MAGIC_ERROR 0 #endif #endif /* USE_MAGIC */ /* tidy up genisoimage definition ... */ typedef struct directory_entry dir_ent; /* routines for getting HFS names and info */ #ifndef HAVE_STRCASECMP static int strcasecmp(const char *s1, const char *s2); #endif static int get_none_dir(char *, char *, dir_ent *, int); static int get_none_info(char *, char *, dir_ent *, int); static int get_cap_dir(char *, char *, dir_ent *, int); static int get_cap_info(char *, char *, dir_ent *, int); static int get_es_dir(char *, char *, dir_ent *, int); static int get_es_info(char *, char *, dir_ent *, int); static int get_dbl_dir(char *, char *, dir_ent *, int); static int get_dbl_info(char *, char *, dir_ent *, int); static int get_mb_info(char *, char *, dir_ent *, int); static int get_sgl_info(char *, char *, dir_ent *, int); static int get_fe_dir(char *, char *, dir_ent *, int); static int get_fe_info(char *, char *, dir_ent *, int); static int get_sgi_dir(char *, char *, dir_ent *, int); static int get_sgi_info(char *, char *, dir_ent *, int); static int get_sfm_info(char *, char *, dir_ent *, int); #ifdef IS_MACOS_X static int get_xhfs_dir(char *, char *, dir_ent *, int); static int get_xhfs_info(char *, char *, dir_ent *, int); #else #define get_xhfs_dir get_none_dir #define get_xhfs_info get_none_info #endif /* IS_MACOS_X */ static void set_ct(hfsdirent *, char *, char *); static void set_Dinfo(byte *, hfsdirent *); static void set_Finfo(byte *, hfsdirent *); static void cstrncpy(char *, char *, int); static unsigned char dehex(char); static unsigned char hex2char(char *); static void hstrncpy(unsigned char *, char *, int); static int read_info_file(char *, void *, int); /*static unsigned short calc_mb_crc __PR((unsigned char *, long, unsigned short));*/ static struct hfs_info *get_hfs_fe_info(struct hfs_info *, char *); static struct hfs_info *get_hfs_sgi_info(struct hfs_info *, char *); static struct hfs_info *match_key(struct hfs_info *, char *); static int get_hfs_itype(char *, char *, char *); static void map_ext(char *, char **, char **, short *, char *); static afpmap **map; /* list of mappings */ static afpmap *defmap; /* the default mapping */ static int last_ent; /* previous mapped entry */ static int map_num; /* number of mappings */ static int mlen; /* min extension length */ static char tmp[PATH_MAX]; /* tmp working buffer */ static int hfs_num; /* number of file types */ static char p_buf[PATH_MAX]; /* info working buffer */ static FILE *p_fp = NULL; /* probe File pointer */ static int p_num = 0; /* probe bytes read */ static unsigned int hselect; /* type of HFS file selected */ struct hfs_type { /* Types of various HFS Unix files */ int type; /* type of file */ int flags; /* special flags */ char *info; /* finderinfo name */ char *rsrc; /* resource fork name */ int (*get_info)(char *, char *, dir_ent *, int); /* finderinfo */ /* function */ int (*get_dir)(char *, char *, dir_ent *, int); /* directory */ /* name */ /* function */ char *desc; /* description */ }; /* Above filled in */ static struct hfs_type hfs_types[] = { {TYPE_NONE, INSERT, "", "", get_none_info, get_none_dir, "None"}, {TYPE_CAP, INSERT, ".finderinfo/", ".resource/", get_cap_info, get_cap_dir, "CAP"}, {TYPE_NETA, INSERT, ".AppleDouble/", ".AppleDouble/", get_dbl_info, get_dbl_dir, "Netatalk"}, {TYPE_DBL, INSERT, "%", "%", get_dbl_info, get_dbl_dir, "AppleDouble"}, {TYPE_ESH, INSERT, ".rsrc/", ".rsrc/", get_es_info, get_es_dir, "EtherShare/UShare"}, {TYPE_FEU, NOPEND, "FINDER.DAT", "RESOURCE.FRK/", get_fe_info, get_fe_dir, "Exchange"}, {TYPE_FEL, NOPEND, "finder.dat", "resource.frk/", get_fe_info, get_fe_dir, "Exchange"}, {TYPE_SGI, NOPEND, ".HSancillary", ".HSResource/", get_sgi_info, get_sgi_dir, "XINET/SGI"}, {TYPE_MBIN, PROBE, "", "", get_mb_info, get_none_dir, "MacBinary"}, {TYPE_SGL, PROBE, "", "", get_sgl_info, get_none_dir, "AppleSingle"}, {TYPE_DAVE, INSERT, "resource.frk/", "resource.frk/", get_dbl_info, get_dbl_dir, "DAVE"}, {TYPE_SFM, APPEND | NORSRC, ":Afp_AfpInfo", ":Afp_Resource", get_sfm_info, get_none_dir, "SFM"}, {TYPE_XDBL, INSERT, "._", "._", get_dbl_info, get_dbl_dir, "MacOS X AppleDouble"}, {TYPE_XHFS, APPEND | NOINFO, "/rsrc", "/rsrc", get_xhfs_info, get_xhfs_dir, "MacOS X HFS"} }; /* used by get_magic_match() return */ static char tmp_type[CT_SIZE + 1], tmp_creator[CT_SIZE + 1]; #ifdef __used__ /* * An array useful for CRC calculations that use 0x1021 as the "seed" * taken from mcvert.c modified by Jim Van Verth. */ static unsigned short mb_magic[] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 }; #endif /* __used__ */ #ifndef HAVE_STRCASECMP static int strcasecmp(const char *s1, const char *s2) { while (tolower(*s1) == tolower(*s2)) { if (*s1 == 0) return (0); s1++; s2++; } return (tolower(*s1) - tolower(*s2)); } #endif /* * set_ct: set CREATOR and TYPE in hfs_ent * * CREATOR and TYPE are padded with spaces if not CT_SIZE long */ static void set_ct(hfsdirent *hfs_ent, char *c, char *t) { memset(hfs_ent->u.file.type, ' ', CT_SIZE); memset(hfs_ent->u.file.creator, ' ', CT_SIZE); strncpy(hfs_ent->u.file.type, t, MIN(CT_SIZE, strlen(t))); strncpy(hfs_ent->u.file.creator, c, MIN(CT_SIZE, strlen(c))); hfs_ent->u.file.type[CT_SIZE] = '\0'; hfs_ent->u.file.creator[CT_SIZE] = '\0'; } /* * cstrncopy: Cap Unix name to HFS name * * ':' is replaced by '%' and string is terminated with '\0' */ static void cstrncpy(char *t, char *f, int c) { while (c-- && *f) { switch (*f) { case ':': *t = '%'; break; default: *t = *f; break; } t++; f++; } *t = '\0'; } /* * dehex() * * Given a hexadecimal digit in ASCII, return the integer representation. * * Taken from linux/fs/hfs/trans.c by Paul H. Hargrove */ static unsigned char dehex(char c) { if ((c >= '0') && (c <= '9')) { return (c - '0'); } if ((c >= 'a') && (c <= 'f')) { return (c - 'a' + 10); } if ((c >= 'A') && (c <= 'F')) { return (c - 'A' + 10); } /* return (0xff); */ return (0); } static unsigned char hex2char(char *s) { unsigned char i1; unsigned char i2; unsigned char o; if (strlen(++s) < 2) return (0); i1 = (unsigned char) s[0]; i2 = (unsigned char) s[1]; if (!isxdigit(i1) || !isxdigit(i2)) return (0); o = (dehex(i1) << 4) & 0xf0; o |= (dehex(i2) & 0xf); return (o); } /* * hstrncpy: Unix name to HFS name with special character * translation. * * "%xx" or ":xx" is assumed to be a "special" character and * replaced by character code given by the hex characters "xx" * * if "xx" is not a hex number, then it is left alone - except * that ":" is replaced by "%" * */ static void hstrncpy(unsigned char *t, char *f, int c) { unsigned char o; while (c-- && *f) { switch (*f) { case ':': case '%': if ((o = hex2char(f)) == 0) { *t = conv_charset('%', in_nls, hfs_onls); } else { *t = o; f += 2; } break; default: *t = conv_charset(*f, in_nls, hfs_onls); break; } t++; f++; } *t = '\0'; } /* * basename: find just the filename with any directory component */ /* not used at the moment ... static char basename(a) char *a; { char *b; if ((b = strchr(a, '/'))) return (++b); else return (a); } */ /* * set_Dinfo: set directory info */ static void set_Dinfo(byte *ptr, hfsdirent *ent) { Dinfo *dinfo = (Dinfo *)ptr; /* finder flags */ ent->fdflags = d_getw((unsigned char *) dinfo->frFlags); if (icon_pos) { ent->u.dir.rect.top = d_getw((unsigned char *) dinfo->frRect[0]); ent->u.dir.rect.left = d_getw((unsigned char *) dinfo->frRect[1]); ent->u.dir.rect.bottom = d_getw((unsigned char *) dinfo->frRect[2]); ent->u.dir.rect.right = d_getw((unsigned char *) dinfo->frRect[3]); ent->fdlocation.v = d_getw((unsigned char *) dinfo->frLocation[0]); ent->fdlocation.h = d_getw((unsigned char *) dinfo->frLocation[1]); ent->u.dir.view = d_getw((unsigned char *) dinfo->frView); ent->u.dir.frscroll.v = d_getw((unsigned char *) dinfo->frScroll[0]); ent->u.dir.frscroll.h = d_getw((unsigned char *) dinfo->frScroll[1]); } else { /* * clear HFS_FNDR_HASBEENINITED to have tidy desktop ?? */ ent->fdflags &= 0xfeff; } } /* * set_Finfo: set file info */ static void set_Finfo(byte *ptr, hfsdirent *ent) { Finfo *finfo = (Finfo *)ptr; /* type and creator from finder info */ set_ct(ent, finfo->fdCreator, finfo->fdType); /* finder flags */ ent->fdflags = d_getw((unsigned char *) finfo->fdFlags); if (icon_pos) { ent->fdlocation.v = d_getw((unsigned char *) finfo->fdLocation[0]); ent->fdlocation.h = d_getw((unsigned char *) finfo->fdLocation[1]); } else { /* * clear HFS_FNDR_HASBEENINITED to have tidy desktop ?? */ ent->fdflags &= 0xfeff; } } /* * get_none_dir: ordinary Unix directory */ static int get_none_dir(char *hname, char *dname, dir_ent *s_entry, int ret) { /* just copy the given name */ hstrncpy((unsigned char *) (s_entry->hfs_ent->name), dname, HFS_MAX_FLEN); return (ret); } /* * get_none_info: ordinary Unix file - try to map extension */ static int get_none_info(char *hname, char *dname, dir_ent *s_entry, int ret) { char *t, *c; hfsdirent *hfs_ent = s_entry->hfs_ent; map_ext(dname, &t, &c, &s_entry->hfs_ent->fdflags, s_entry->whole_name); /* just copy the given name */ hstrncpy((unsigned char *) (hfs_ent->name), dname, HFS_MAX_FLEN); set_ct(hfs_ent, c, t); return (ret); } /* * read_info_file: open and read a finderinfo file for an HFS file * or directory */ static int read_info_file(char *name, /* finderinfo filename */ void *info, /* info buffer */ int len /* length of above */) { FILE *fp; int num; /* clear out any old finderinfo stuf */ memset(info, 0, len); if ((fp = fopen(name, "rb")) == NULL) return (-1); /* read and ignore if the file is short - checked later */ num = fread(info, 1, len, fp); fclose(fp); return (num); } /* * get_cap_dir: get the CAP name for a directory */ static int get_cap_dir(char *hname, /* whole path */ char *dname, /* this dir name */ dir_ent *s_entry, /* directory entry */ int ret) { FileInfo info; /* finderinfo struct */ int num = -1; /* bytes read */ hfsdirent *hfs_ent = s_entry->hfs_ent; num = read_info_file(hname, &info, sizeof (FileInfo)); /* check finder info is OK */ if (num > 0 && info.fi_magic1 == FI_MAGIC1 && info.fi_magic == FI_MAGIC && info.fi_bitmap & FI_BM_MACINTOSHFILENAME) { /* use the finderinfo name if it exists */ cstrncpy((char *) (hfs_ent->name), (char *) (info.fi_macfilename), HFS_MAX_FLEN); set_Dinfo(info.finderinfo, hfs_ent); return (ret); } else { /* otherwise give it it's Unix name */ hstrncpy((unsigned char *) (s_entry->hfs_ent->name), dname, HFS_MAX_FLEN); return (TYPE_NONE); } } /* ** get_cap_info: get CAP finderinfo for a file */ static int get_cap_info(char *hname, /* whole path */ char *dname, /* this dir name */ dir_ent *s_entry, /* directory entry */ int ret) { FileInfo info; /* finderinfo struct */ int num = -1; /* bytes read */ hfsdirent *hfs_ent = s_entry->hfs_ent; num = read_info_file(hname, &info, sizeof (info)); /* check finder info is OK */ if (num > 0 && info.fi_magic1 == FI_MAGIC1 && info.fi_magic == FI_MAGIC) { if (info.fi_bitmap & FI_BM_MACINTOSHFILENAME) { /* use the finderinfo name if it exists */ cstrncpy((char *) (hfs_ent->name), (char *) (info.fi_macfilename), HFS_MAX_FLEN); } else { /* use Unix name */ hstrncpy((unsigned char *) (hfs_ent->name), dname, HFS_MAX_FLEN); } set_Finfo(info.finderinfo, hfs_ent); #ifdef USE_MAC_DATES /* * set created/modified dates - these date should have already * been set from the Unix data fork dates. The finderinfo dates * are in Mac format - but we have to convert them back to Unix * for the time being */ if ((info.fi_datemagic & FI_CDATE)) { /* use libhfs routines to get correct byte order */ hfs_ent->crdate = d_toutime(d_getl(info.fi_ctime)); } if (info.fi_datemagic & FI_MDATE) { hfs_ent->mddate = d_toutime(d_getl(info.fi_mtime)); } #endif /* USE_MAC_DATES */ } else { /* failed to open/read finderinfo - so try afpfile mapping */ if (verbose > 2) { fprintf(stderr, "warning: %s doesn't appear to be a %s file\n", s_entry->whole_name, hfs_types[ret].desc); } ret = get_none_info(hname, dname, s_entry, TYPE_NONE); } return (ret); } /* * get_es_dir: get EtherShare/UShare finderinfo for a directory * * based on code from Jens-Uwe Mager (jum@helios.de) and Phil Sylvester * */ static int get_es_dir(char *hname, /* whole path */ char *dname, /* this dir name */ dir_ent *s_entry, /* directory entry */ int ret) { es_FileInfo *einfo; /* EtherShare info struct */ us_FileInfo *uinfo; /* UShare info struct */ char info[ES_INFO_SIZE]; /* finderinfo buffer */ int num = -1; /* bytes read */ hfsdirent *hfs_ent = s_entry->hfs_ent; /* * the EtherShare and UShare file layout is the same, but they store * finderinfo differently */ einfo = (es_FileInfo *) info; uinfo = (us_FileInfo *) info; num = read_info_file(hname, info, sizeof (info)); /* check finder info for EtherShare finderinfo */ if (num >= (int)sizeof (es_FileInfo) && d_getl(einfo->magic) == ES_MAGIC && d_getw(einfo->version) == ES_VERSION) { set_Dinfo(einfo->finderinfo, hfs_ent); } else if (num >= (int)sizeof (us_FileInfo)) { /* * UShare has no magic number, so we assume that this is a valid * info/resource file ... */ set_Dinfo(uinfo->finderinfo, hfs_ent); } else { /* failed to open/read finderinfo - so try afpfile mapping */ if (verbose > 2) { fprintf(stderr, "warning: %s doesn't appear to be a %s file\n", s_entry->whole_name, hfs_types[ret].desc); } ret = get_none_dir(hname, dname, s_entry, TYPE_NONE); return (ret); } /* set name */ hstrncpy((unsigned char *) (hfs_ent->name), dname, HFS_MAX_FLEN); return (ret); } /* * get_es_info: get EtherShare/UShare finderinfo for a file * * based on code from Jens-Uwe Mager (jum@helios.de) and Phil Sylvester * */ static int get_es_info(char *hname, /* whole path */ char *dname, /* this dir name */ dir_ent *s_entry, /* directory entry */ int ret) { es_FileInfo *einfo; /* EtherShare info struct */ us_FileInfo *uinfo; /* UShare info struct */ char info[ES_INFO_SIZE]; /* finderinfo buffer */ int num = -1; /* bytes read */ hfsdirent *hfs_ent = s_entry->hfs_ent; dir_ent *s_entry1; /* * the EtherShare and UShare file layout is the same, but they store * finderinfo differently */ einfo = (es_FileInfo *) info; uinfo = (us_FileInfo *) info; num = read_info_file(hname, info, sizeof (info)); /* check finder info for EtherShare finderinfo */ if (num >= (int)sizeof (es_FileInfo) && d_getl(einfo->magic) == ES_MAGIC && d_getw(einfo->version) == ES_VERSION) { set_Finfo(einfo->finderinfo, hfs_ent); /* * set create date - modified date set from the Unix * data fork date */ hfs_ent->crdate = d_getl(einfo->createTime); } else if (num >= (int)sizeof (us_FileInfo)) { /* * UShare has no magic number, so we assume that this is a valid * info/resource file ... */ set_Finfo(uinfo->finderinfo, hfs_ent); /* set create and modified date - if they exist */ if (uinfo->ctime) hfs_ent->crdate = d_getl(uinfo->ctime); if (uinfo->mtime) hfs_ent->mddate = d_getl(uinfo->mtime); } else { /* failed to open/read finderinfo - so try afpfile mapping */ if (verbose > 2) { fprintf(stderr, "warning: %s doesn't appear to be a %s file\n", s_entry->whole_name, hfs_types[ret].desc); } ret = get_none_info(hname, dname, s_entry, TYPE_NONE); return (ret); } /* this should exist ... */ if ((s_entry1 = s_entry->assoc) == NULL) perr("TYPE_ESH error - shouldn't happen!"); /* set name */ hstrncpy((unsigned char *) (hfs_ent->name), dname, HFS_MAX_FLEN); /* real rsrc file starts ES_INFO_SIZE bytes into the file */ if (s_entry1->size <= ES_INFO_SIZE) { s_entry1->size = 0; hfs_ent->u.file.rsize = 0; } else { s_entry1->size -= ES_INFO_SIZE; hfs_ent->u.file.rsize = s_entry1->size; s_entry1->hfs_off = ES_INFO_SIZE; } set_733((char *) s_entry1->isorec.size, s_entry1->size); return (ret); } /* * calc_crc() -- * Compute the MacBinary II-style CRC for the data pointed to by p, with the * crc seeded to seed. * * Modified by Jim Van Verth to use the magic array for efficiency. */ #ifdef __used__ static unsigned short calc_mb_crc(unsigned char *p, long len, unsigned short seed) { unsigned short hold; /* crc computed so far */ long i; /* index into data */ hold = seed; /* start with seed */ for (i = 0; i < len; i++, p++) { hold ^= (*p << 8); hold = (hold << 8) ^ mb_magic[(unsigned char) (hold >> 8)]; } return (hold); }/* calc_mb_crc() */ #endif /* __used__ */ static int get_mb_info(char *hname, /* whole path */ char *dname, /* this dir name */ dir_ent *s_entry, /* directory entry */ int ret) { mb_info *info; /* finderinfo struct */ char *c; char *t; hfsdirent *hfs_ent; dir_ent *s_entry1; int i; #ifdef TEST_CODE unsigned short crc_file, crc_calc; #endif info = (mb_info *) p_buf; /* * routine called twice for each file - first to check that it is a * valid MacBinary file, second to fill in the HFS info. p_buf holds * the required raw data and it *should* remain the same between the * two calls */ if (s_entry == 0) { /* * test that the CRC is OK - not set for MacBinary I files (and * incorrect in some MacBinary II files!). If this fails, then * perform some other checks */ #ifdef TEST_CODE /* leave this out for the time being ... */ if (p_num >= MB_SIZE && info->version == 0 && info->zero1 == 0) { crc_calc = calc_mb_crc((unsigned char *) info, 124, 0); crc_file = d_getw(info->crc); #ifdef DEBUG fprintf(stderr, "%s: file %d, calc %d\n", hname, crc_file, crc_calc); #endif /* DEBUG */ if (crc_file == crc_calc) return (ret); } #endif /* TEST_CODE */ /* * check some of the fields for a valid MacBinary file not * zero1 and zero2 SHOULD be zero - but some files incorrect */ /* if (p_num < MB_SIZE || info->nlen > 63 || info->zero2 || */ if (p_num < MB_SIZE || info->zero1 || info->zero2 || info->nlen > 63 || info->version || info->nlen == 0 || *info->name == 0) return (TYPE_NONE); /* check that the filename is OKish */ for (i = 0; i < (int)info->nlen; i++) if (info->name[i] == 0) return (TYPE_NONE); /* check CREATOR and TYPE are valid */ for (i = 0; i < 4; i++) if (info->type[i] == 0 || info->auth[i] == 0) return (TYPE_NONE); } else { /* we have a vaild MacBinary file, so fill in the bits */ /* this should exist ... */ if ((s_entry1 = s_entry->assoc) == NULL) perr("TYPE_MBIN error - shouldn't happen!"); hfs_ent = s_entry->hfs_ent; /* type and creator from finder info */ t = (char *) (info->type); c = (char *) (info->auth); set_ct(hfs_ent, c, t); /* finder flags */ hfs_ent->fdflags = ((info->flags << 8) & 0xff00) | info->flags2; if (icon_pos) { hfs_ent->fdlocation.v = d_getw((unsigned char *) info->icon_vert); hfs_ent->fdlocation.h = d_getw((unsigned char *) info->icon_horiz); } else { /* * clear HFS_FNDR_HASBEENINITED to have tidy desktop ?? */ hfs_ent->fdflags &= 0xfeff; } /* * set created/modified dates - these date should have already * been set from the Unix data fork dates. The finderinfo dates * are in Mac format - but we have to convert them back to Unix * for the time being */ hfs_ent->crdate = d_toutime(d_getl(info->cdate)); hfs_ent->mddate = d_toutime(d_getl(info->mdate)); /* set name */ hstrncpy((unsigned char *) (hfs_ent->name), (char *) (info->name), MIN(HFS_MAX_FLEN, info->nlen)); /* set correct fork sizes */ hfs_ent->u.file.dsize = d_getl(info->dflen); hfs_ent->u.file.rsize = d_getl(info->rflen); /* update directory entries for data fork */ s_entry->size = hfs_ent->u.file.dsize; s_entry->hfs_off = MB_SIZE; set_733((char *) s_entry->isorec.size, s_entry->size); /* * real rsrc file starts after data fork (must be a multiple of * MB_SIZE) */ s_entry1->size = hfs_ent->u.file.rsize; s_entry1->hfs_off = MB_SIZE + ROUND_UP(hfs_ent->u.file.dsize, MB_SIZE); set_733((char *) s_entry1->isorec.size, s_entry1->size); } return (ret); } /* * get_dbl_dir: get Apple double finderinfo for a directory * * Based on code from cvt2cap.c (c) May 1988, Paul Campbell */ static int get_dbl_dir(char *hname, /* whole path */ char *dname, /* this dir name */ dir_ent *s_entry, /* directory entry */ int ret) { FileInfo info; /* finderinfo struct */ a_hdr *hp; a_entry *ep; int num = -1; /* bytes read */ int nentries; FILE *fp; hfsdirent *hfs_ent = s_entry->hfs_ent; char name[64]; int i; int fail = 0; int len = 0; hp = (a_hdr *) p_buf; memset(hp, 0, A_HDR_SIZE); memset(name, 0, sizeof (name)); /* open and read the info/rsrc file (it's the same file) */ if ((fp = fopen(hname, "rb")) != NULL) num = fread(hp, 1, A_HDR_SIZE, fp); /* * check finder info is OK - some Netatalk files don't have magic * or version set - ignore if it's a netatalk file */ if (num == A_HDR_SIZE && ((ret == TYPE_NETA) || (d_getl(hp->magic) == APPLE_DOUBLE && (d_getl(hp->version) == A_VERSION1 || d_getl(hp->version) == A_VERSION2)))) { /* read TOC of the AppleDouble file */ nentries = (int) d_getw(hp->nentries); if (fread(hp->entries, A_ENTRY_SIZE, nentries, fp) < 1) { fail = 1; nentries = 0; } /* extract what is needed */ for (i = 0, ep = hp->entries; i < nentries; i++, ep++) { switch ((int)d_getl(ep->id)) { case ID_FINDER: /* get the finder info */ fseek(fp, (off_t)d_getl(ep->offset), SEEK_SET); if (fread(&info, d_getl(ep->length), 1, fp) < 1) { fail = 1; } break; case ID_NAME: /* get Mac file name */ fseek(fp, (off_t)d_getl(ep->offset), SEEK_SET); if (fread(name, d_getl(ep->length), 1, fp) < 1) *name = '\0'; len = d_getl(ep->length); break; default: break; } } fclose(fp); /* skip this if we had a problem */ if (!fail) { set_Dinfo(info.finderinfo, hfs_ent); /* use stored name if it exists */ if (*name) { /* * In some cases the name is stored in the * Pascal string format - first char is the * length, the rest is the actual string. * The following *should* be OK */ if (len == 32 && (int) name[0] < 32) { cstrncpy(hfs_ent->name, &name[1], MIN(name[0], HFS_MAX_FLEN)); } else { cstrncpy(hfs_ent->name, name, HFS_MAX_FLEN); } } else { hstrncpy((unsigned char *) (hfs_ent->name), dname, HFS_MAX_FLEN); } } } else { /* failed to open/read finderinfo */ fail = 1; if (fp) fclose(fp); } if (fail) { /* problem with the file - try mapping/magic */ if (verbose > 2) { fprintf(stderr, "warning: %s doesn't appear to be a %s file\n", s_entry->whole_name, hfs_types[ret].desc); } ret = get_none_dir(hname, dname, s_entry, TYPE_NONE); } return (ret); } /* * Depending on the version, AppleDouble/Single stores dates * relative to 1st Jan 1904 (v1) or 1st Jan 2000 (v2) * * The d_toutime() function uses 1st Jan 1904 to convert to * Unix time (1st Jan 1970). * * The d_dtoutime() function uses 1st Jan 2000 to convert to * Unix time (1st Jan 1970). * * However, NetaTalk files seem to do their own thing - older * Netatalk files don't have a magic number of version and * store dates in ID=7 (don't know how). Newer Netatalk files * claim to be version 1, but store dates in ID=7 as if they * were version 2 files. */ /* * get_dbl_info: get Apple double finderinfo for a file * * Based on code from cvt2cap.c (c) May 1988, Paul Campbell */ static int get_dbl_info(char *hname, /* whole path */ char *dname, /* this dir name */ dir_ent *s_entry, /* directory entry */ int ret) { FileInfo info; /* finderinfo struct */ a_hdr *hp; a_entry *ep; int num = -1; /* bytes read */ int nentries; FILE *fp; hfsdirent *hfs_ent = s_entry->hfs_ent; dir_ent *s_entry1; char name[64]; int i; int fail = 0; int len = 0; unsigned char dates[A_DATE]; int ver = 0, dlen; hp = (a_hdr *) p_buf; memset(hp, 0, A_HDR_SIZE); memset(name, 0, sizeof (name)); memset(dates, 0, sizeof (dates)); /* get the rsrc file info - should exist ... */ if ((s_entry1 = s_entry->assoc) == NULL) perr("TYPE_DBL error - shouldn't happen!"); /* open and read the info/rsrc file (it's the same file) */ if ((fp = fopen(hname, "rb")) != NULL) num = fread(hp, 1, A_HDR_SIZE, fp); /* * check finder info is OK - some Netatalk files don't have magic * or version set - ignore if it's a netatalk file */ ver = d_getl(hp->version); if (num == A_HDR_SIZE && ((ret == TYPE_NETA) || (d_getl(hp->magic) == APPLE_DOUBLE && (ver == A_VERSION1 || ver == A_VERSION2)))) { /* read TOC of the AppleDouble file */ nentries = (int) d_getw(hp->nentries); if (fread(hp->entries, A_ENTRY_SIZE, nentries, fp) < 1) { fail = 1; nentries = 0; } /* extract what is needed */ for (i = 0, ep = hp->entries; i < nentries; i++, ep++) { switch ((int)d_getl(ep->id)) { case ID_FINDER: /* get the finder info */ fseek(fp, (off_t)d_getl(ep->offset), SEEK_SET); if (fread(&info, d_getl(ep->length), 1, fp) < 1) { fail = 1; } break; case ID_RESOURCE: /* set the offset and correct rsrc fork size */ s_entry1->size = d_getl(ep->length); hfs_ent->u.file.rsize = s_entry1->size; /* offset to start of real rsrc fork */ s_entry1->hfs_off = d_getl(ep->offset); set_733((char *) s_entry1->isorec.size, s_entry1->size); break; case ID_NAME: /* get Mac file name */ fseek(fp, (off_t)d_getl(ep->offset), SEEK_SET); if (fread(name, d_getl(ep->length), 1, fp) < 1) *name = '\0'; len = d_getl(ep->length); break; case ID_FILEI: /* Workround for NetaTalk files ... */ if (ret == TYPE_NETA && ver == A_VERSION1) ver = A_VERSION2; /* fall through */ case ID_FILEDATESI: /* get file info */ fseek(fp, d_getl(ep->offset), 0); dlen = MIN(d_getl(ep->length), A_DATE); if (fread(dates, dlen, 1, fp) < 1) { fail = 1; } else { /* get the correct Unix time */ switch (ver) { case (A_VERSION1): hfs_ent->crdate = d_toutime(d_getl(dates)); hfs_ent->mddate = d_toutime(d_getl(dates+4)); break; case (A_VERSION2): hfs_ent->crdate = d_dtoutime(d_getl(dates)); hfs_ent->mddate = d_dtoutime(d_getl(dates+4)); break; default: /* Use Unix dates */ break; } } break; default: break; } } fclose(fp); /* skip this if we had a problem */ if (!fail) { set_Finfo(info.finderinfo, hfs_ent); /* use stored name if it exists */ if (*name) { /* * In some cases the name is stored in the * Pascal string format - first char is the * length, the rest is the actual string. * The following *should* be OK */ if (len == 32 && (int) name[0] < 32) { cstrncpy(hfs_ent->name, &name[1], MIN(name[0], HFS_MAX_FLEN)); } else { cstrncpy(hfs_ent->name, name, HFS_MAX_FLEN); } } else { hstrncpy((unsigned char *) (hfs_ent->name), dname, HFS_MAX_FLEN); } } } else { /* failed to open/read finderinfo */ fail = 1; if (fp) fclose(fp); } if (fail) { /* problem with the file - try mapping/magic */ if (verbose > 2) { fprintf(stderr, "warning: %s doesn't appear to be a %s file\n", s_entry->whole_name, hfs_types[ret].desc); } ret = get_none_info(hname, dname, s_entry, TYPE_NONE); } return (ret); } /* * get_sgl_info: get Apple single finderinfo for a file * * Based on code from cvt2cap.c (c) May 1988, Paul Campbell */ static int get_sgl_info(char *hname, /* whole path */ char *dname, /* this dir name */ dir_ent *s_entry, /* directory entry */ int ret) { FileInfo *info = 0; /* finderinfo struct */ a_hdr *hp; static a_entry *entries; a_entry *ep; int nentries; hfsdirent *hfs_ent; dir_ent *s_entry1; char name[64]; int i; int len = 0; unsigned char *dates; int ver = 0; /* * routine called twice for each file * - first to check that it is a valid * AppleSingle file, second to fill in the HFS info. * p_buf holds the required * raw data and it *should* remain the same between the two calls */ hp = (a_hdr *) p_buf; if (s_entry == 0) { if (p_num < A_HDR_SIZE || d_getl(hp->magic) != APPLE_SINGLE || (d_getl(hp->version) != A_VERSION1 && d_getl(hp->version) != A_VERSION2)) return (TYPE_NONE); /* check we have TOC for the AppleSingle file */ nentries = (int) d_getw(hp->nentries); if (p_num < (int)(A_HDR_SIZE + nentries * A_ENTRY_SIZE)) return (TYPE_NONE); /* save the TOC */ entries = (a_entry *) e_malloc(nentries * A_ENTRY_SIZE); memcpy(entries, (p_buf + A_HDR_SIZE), nentries * A_ENTRY_SIZE); } else { /* have a vaild AppleSingle File */ memset(name, 0, sizeof (name)); /* get the rsrc file info - should exist ... */ if ((s_entry1 = s_entry->assoc) == NULL) perr("TYPE_SGL error - shouldn't happen!"); hfs_ent = s_entry->hfs_ent; nentries = (int) d_getw(hp->nentries); ver = d_getl(hp->version); /* extract what is needed */ for (i = 0, ep = entries; i < nentries; i++, ep++) { switch ((int)d_getl(ep->id)) { case ID_FINDER: /* get the finder info */ info = (FileInfo *) (p_buf + d_getl(ep->offset)); break; case ID_DATA: /* set the offset and correct data fork size */ hfs_ent->u.file.dsize = s_entry->size = d_getl(ep->length); /* offset to start of real data fork */ s_entry->hfs_off = d_getl(ep->offset); set_733((char *) s_entry->isorec.size, s_entry->size); break; case ID_RESOURCE: /* set the offset and correct rsrc fork size */ hfs_ent->u.file.rsize = s_entry1->size = d_getl(ep->length); /* offset to start of real rsrc fork */ s_entry1->hfs_off = d_getl(ep->offset); set_733((char *) s_entry1->isorec.size, s_entry1->size); break; case ID_NAME: /* get Mac file name */ strncpy(name, (p_buf + d_getl(ep->offset)), d_getl(ep->length)); len = d_getl(ep->length); break; case ID_FILEI: /* get file info - ignore at the moment*/ break; case ID_FILEDATESI: /* get file info */ dates = (unsigned char *)p_buf + d_getl(ep->offset); /* get the correct Unix time */ if (ver == A_VERSION1) { hfs_ent->crdate = d_toutime(d_getl(dates)); hfs_ent->mddate = d_toutime(d_getl(dates+4)); } else { hfs_ent->crdate = d_dtoutime(d_getl(dates)); hfs_ent->mddate = d_dtoutime(d_getl(dates+4)); } break; default: break; } } free(entries); if (info == NULL) { /* * failed to open/read finderinfo * - so try afpfile mapping */ if (verbose > 2) { fprintf(stderr, "warning: %s doesn't appear to be a %s file\n", s_entry->whole_name, hfs_types[ret].desc); } ret = get_none_info(hname, dname, s_entry, TYPE_NONE); return (ret); } set_Finfo(info->finderinfo, hfs_ent); /* use stored name if it exists */ if (*name) { /* * In some cases the name is stored in the Pascal string * format - first char is the length, the rest is the * actual string. The following *should* be OK */ if (len == 32 && (int) name[0] < 32) { cstrncpy(hfs_ent->name, &name[1], MIN(name[0], HFS_MAX_FLEN)); } else { cstrncpy(hfs_ent->name, name, HFS_MAX_FLEN); } } else { hstrncpy((unsigned char *) (hfs_ent->name), dname, HFS_MAX_FLEN); } } return (ret); } /* * get_hfs_fe_info: read in the whole finderinfo for a PC Exchange * directory - saves on reading this many times for each file. * * Based of information provided by Mark Weinstein * * Note: the FINDER.DAT file layout depends on the FAT cluster size * therefore, files should only be read directly from the FAT media * * Only tested with PC Exchange v2.1 - don't know if it will work * with v2.2 and above. */ static struct hfs_info * get_hfs_fe_info(struct hfs_info *hfs_info, char *name) { FILE *fp; int fe_num, fe_pad; fe_info info; int c = 0; struct hfs_info *hfs_info1 = NULL; char keyname[12]; char *s, *e, *k; int i; if ((fp = fopen(name, "rb")) == NULL) return (NULL); /* * no longer attempt to find out FAT cluster * - rely on command line parameter */ if (afe_size <= 0) return (NULL); fe_num = afe_size / FE_SIZE; fe_pad = afe_size % FE_SIZE; while (fread(&info, 1, FE_SIZE, fp) != 0) { /* the Mac name may be NULL - so ignore this entry */ if (info.nlen != 0) { hfs_info1 = (struct hfs_info *)e_malloc(sizeof (struct hfs_info)); /* add this entry to the list */ hfs_info1->next = hfs_info; hfs_info = hfs_info1; /* * get the bits we need * - ignore [cm]time for the moment */ cstrncpy(hfs_info->name, (char *) (info.name), info.nlen); memcpy(hfs_info->finderinfo, info.finderinfo, INFOLEN); s = (char *) (info.sname); e = (char *) (info.ext); k = keyname; /* * short (Unix) name is stored in PC format, * so needs to be mangled a bit */ /* name part */ for (i = 0; i < 8; i++, s++, k++) { if (*s == ' ') break; else *k = *s; } /* extension - if it exists */ if (strncmp((const char *) (info.ext), " ", 3)) { *k = '.'; k++; for (i = 0; i < 3; i++, e++, k++) { if (*e == ' ') break; else *k = *e; } } *k = '\0'; hfs_info1->keyname = strdup(keyname); } /* * each record is FE_SIZE long, and there are FE_NUM * per each "cluster size", so we may need to skip the padding */ if (++c == fe_num) { c = 0; fseek(fp, (off_t)fe_pad, SEEK_CUR); } } fclose(fp); return (hfs_info); } /* * get_hfs_sgi_info: read in the whole finderinfo for a SGI (XINET) * directory - saves on reading this many times for each * file. */ static struct hfs_info * get_hfs_sgi_info(struct hfs_info *hfs_info, char *name) { FILE *fp; sgi_info info; struct hfs_info *hfs_info1 = NULL; if ((fp = fopen(name, "rb")) == NULL) return (NULL); while (fread(&info, 1, SGI_SIZE, fp) != 0) { hfs_info1 = (struct hfs_info *)e_malloc(sizeof (struct hfs_info)); /* add this entry to the list */ hfs_info1->next = hfs_info; hfs_info = hfs_info1; /* get the bits we need - ignore [cm]time for the moment */ cstrncpy(hfs_info->name, (char *)info.name, HFS_MAX_FLEN); memcpy(hfs_info->finderinfo, info.finderinfo, INFOLEN); /* use the HFS name as the key */ hfs_info1->keyname = hfs_info->name; } fclose(fp); return (hfs_info); } /* * del_hfs_info: delete the info list and recover memory */ void del_hfs_info(struct hfs_info *hfs_info) { struct hfs_info *hfs_info1; while (hfs_info) { hfs_info1 = hfs_info; hfs_info = hfs_info->next; /* key may be the same as the HFS name - so don't free it */ *hfs_info1->name = '\0'; if (*hfs_info1->keyname) free(hfs_info1->keyname); free(hfs_info1); } } /* * match_key: find the correct hfs_ent using the Unix filename * as the key */ static struct hfs_info * match_key(struct hfs_info *hfs_info, char *key) { while (hfs_info) { if (strcasecmp(key, hfs_info->keyname) == 0) return (hfs_info); hfs_info = hfs_info->next; } return (NULL); } /* * get_fe_dir: get PC Exchange directory name * * base on probing with od ... */ static int get_fe_dir(char *hname, /* whole path */ char *dname, /* this dir name */ dir_ent *s_entry, /* directory entry */ int ret) { struct hfs_info *hfs_info; hfsdirent *hfs_ent = s_entry->hfs_ent; /* cached finderinfo stored with parent directory */ hfs_info = s_entry->filedir->hfs_info; /* if we have no cache, then make one and store it */ if (hfs_info == NULL) { if ((hfs_info = get_hfs_fe_info(hfs_info, hname)) == NULL) ret = TYPE_NONE; else s_entry->filedir->hfs_info = hfs_info; } if (ret != TYPE_NONE) { /* see if we can find the details of this file */ if ((hfs_info = match_key(hfs_info, dname)) != NULL) { strcpy(hfs_ent->name, hfs_info->name); set_Dinfo(hfs_info->finderinfo, hfs_ent); return (ret); } } /* can't find the entry, so use the Unix name */ hstrncpy((unsigned char *)(hfs_ent->name), dname, HFS_MAX_FLEN); return (TYPE_NONE); } /* * get_fe_info: get PC Exchange file details. * * base on probing with od and details from Mark Weinstein * */ static int get_fe_info(char *hname, /* whole path */ char *dname, /* this dir name */ dir_ent *s_entry, /* directory entry */ int ret) { struct hfs_info *hfs_info; hfsdirent *hfs_ent = s_entry->hfs_ent; /* cached finderinfo stored with parent directory */ hfs_info = s_entry->filedir->hfs_info; /* if we have no cache, then make one and store it */ if (hfs_info == NULL) { if ((hfs_info = get_hfs_fe_info(hfs_info, hname)) == NULL) ret = TYPE_NONE; else s_entry->filedir->hfs_info = hfs_info; } if (ret != TYPE_NONE) { char *dn = dname; #ifdef _WIN32_TEST /* * may have a problem here - v2.2 has long filenames, * but we need to key on the short filename, * so we need do go a bit of win32 stuff * ... */ char sname[1024]; char lname[1024]; cygwin32_conv_to_full_win32_path(s_entry->whole_name, lname); if (GetShortPathName(lname, sname, sizeof (sname))) { if (dn = strrchr(sname, '\\')) dn++; else dn = sname; } #endif /* _WIN32 */ /* see if we can find the details of this file */ if ((hfs_info = match_key(hfs_info, dn)) != NULL) { strcpy(hfs_ent->name, hfs_info->name); set_Finfo(hfs_info->finderinfo, hfs_ent); return (ret); } } /* no entry found - use extension mapping */ if (verbose > 2) { fprintf(stderr, "warning: %s doesn't appear to be a %s file\n", s_entry->whole_name, hfs_types[ret].desc); } ret = get_none_info(hname, dname, s_entry, TYPE_NONE); return (TYPE_NONE); } /* * get_sgi_dir: get SGI (XINET) HFS directory name * * base on probing with od ... */ static int get_sgi_dir(char *hname, /* whole path */ char *dname, /* this dir name */ dir_ent *s_entry, /* directory entry */ int ret) { struct hfs_info *hfs_info; hfsdirent *hfs_ent = s_entry->hfs_ent; /* cached finderinfo stored with parent directory */ hfs_info = s_entry->filedir->hfs_info; /* if we haven't got a cache, then make one */ if (hfs_info == NULL) { if ((hfs_info = get_hfs_sgi_info(hfs_info, hname)) == NULL) ret = TYPE_NONE; else s_entry->filedir->hfs_info = hfs_info; } /* find the matching entry in the cache */ if (ret != TYPE_NONE) { /* key is (hopefully) the real Mac name */ cstrncpy(tmp, dname, strlen(dname)); if ((hfs_info = match_key(hfs_info, tmp)) != NULL) { strcpy(hfs_ent->name, hfs_info->name); set_Dinfo(hfs_info->finderinfo, hfs_ent); return (ret); } } /* no entry found - use Unix name */ hstrncpy((unsigned char *)(hfs_ent->name), dname, HFS_MAX_FLEN); return (TYPE_NONE); } /* * get_sgi_info: get SGI (XINET) HFS finder info * * base on probing with od ... */ static int get_sgi_info(char *hname, /* whole path */ char *dname, /* this dir name */ dir_ent *s_entry, /* directory entry */ int ret) { struct hfs_info *hfs_info; hfsdirent *hfs_ent = s_entry->hfs_ent; /* cached finderinfo stored with parent directory */ hfs_info = s_entry->filedir->hfs_info; /* if we haven't got a cache, then make one */ if (hfs_info == NULL) { if ((hfs_info = get_hfs_sgi_info(hfs_info, hname)) == NULL) ret = TYPE_NONE; else s_entry->filedir->hfs_info = hfs_info; } if (ret != TYPE_NONE) { /* * tmp is the same as hname here, but we don't need hname * anymore in this function ... see if we can find the * details of this file using the Unix name as the key */ cstrncpy(tmp, dname, strlen(dname)); if ((hfs_info = match_key(hfs_info, tmp)) != NULL) { strcpy(hfs_ent->name, hfs_info->name); set_Finfo(hfs_info->finderinfo, hfs_ent); return (ret); } } /* no entry found, so try file extension */ if (verbose > 2) { fprintf(stderr, "warning: %s doesn't appear to be a %s file\n", s_entry->whole_name, hfs_types[ret].desc); } ret = get_none_info(hname, dname, s_entry, TYPE_NONE); return (TYPE_NONE); } /* * get_sfm_info: get SFM finderinfo for a file */ static byte sfm_magic[4] = {0x41, 0x46, 0x50, 0x00}; static byte sfm_version[4] = {0x00, 0x00, 0x01, 0x00}; static int get_sfm_info(char *hname, /* whole path */ char *dname, /* this dir name */ dir_ent *s_entry, /* directory entry */ int ret) { sfm_info info; /* finderinfo struct */ int num = -1; /* bytes read */ hfsdirent *hfs_ent = s_entry->hfs_ent; num = read_info_file(hname, &info, sizeof (info)); /* check finder info is OK */ if (num == sizeof (info) && !memcmp((char *)info.afpi_Signature, (char *)sfm_magic, 4) && !memcmp((char *)info.afpi_Version, (char *)sfm_version, 4)) { /* use Unix name */ hstrncpy((unsigned char *)(hfs_ent->name), dname, HFS_MAX_FLEN); set_Finfo(info.finderinfo, hfs_ent); } else { /* failed to open/read finderinfo - so try afpfile mapping */ if (verbose > 2) { fprintf(stderr, "warning: %s doesn't appear to be a %s file\n", s_entry->whole_name, hfs_types[ret].desc); } ret = get_none_info(hname, dname, s_entry, TYPE_NONE); } return (ret); } #ifdef IS_MACOS_X /* * get_xhfs_dir: get MacOS X HFS finderinfo for a directory * * Code ideas from 'hfstar' by Marcel Weiher marcel@metaobject.com * and another GNU hfstar by Torres Vedras paulotex@yahoo.com * * Here we are dealing with actual HFS files - not some encoding * we have to use a system call to get the finderinfo * * The file name here is the pseudo name for the resource fork */ static int get_xhfs_dir(char *hname, /* whole path */ char *dname, /* this dir name */ dir_ent *s_entry, /* directory entry */ int ret) { int err; hfsdirent *hfs_ent = s_entry->hfs_ent; attrinfo ainfo; struct attrlist attrs; int i; memset(&attrs, 0, sizeof (attrs)); /* set flags we need to get info from getattrlist() */ attrs.bitmapcount = ATTR_BIT_MAP_COUNT; attrs.commonattr = ATTR_CMN_CRTIME | ATTR_CMN_MODTIME | ATTR_CMN_FNDRINFO; /* get the info */ err = getattrlist(hname, &attrs, &ainfo, sizeof (ainfo), 0); if (err == 0) { /* * If the Finfo is blank then we assume it's not a * 'true' HFS directory ... */ err = 1; for (i = 0; i < sizeof (ainfo.info); i++) { if (ainfo.info[i] != 0) { err = 0; break; } } } /* check finder info is OK */ if (err == 0) { hstrncpy((unsigned char *) (s_entry->hfs_ent->name), dname, HFS_MAX_FLEN); set_Dinfo(ainfo.info, hfs_ent); return (ret); } else { /* otherwise give it it's Unix name */ hstrncpy((unsigned char *) (s_entry->hfs_ent->name), dname, HFS_MAX_FLEN); return (TYPE_NONE); } } /* * get_xhfs_info: get MacOS X HFS finderinfo for a file * * Code ideas from 'hfstar' by Marcel Weiher marcel@metaobject.com, * another GNU hfstar by Torres Vedras paulotex@yahoo.com and * hfspax by Howard Oakley howard@quercus.demon.co.uk * * Here we are dealing with actual HFS files - not some encoding * we have to use a system call to get the finderinfo * * The file name here is the pseudo name for the resource fork */ static int get_xhfs_info(char *hname, /* whole path */ char *dname, /* this dir name */ dir_ent *s_entry, /* directory entry */ int ret) { int err; hfsdirent *hfs_ent = s_entry->hfs_ent; attrinfo ainfo; struct attrlist attrs; int i; int size; memset(&attrs, 0, sizeof (attrs)); /* set flags we need to get info from getattrlist() */ attrs.bitmapcount = ATTR_BIT_MAP_COUNT; attrs.commonattr = ATTR_CMN_CRTIME | ATTR_CMN_MODTIME | ATTR_CMN_FNDRINFO; /* get the info */ err = getattrlist(hname, &attrs, &ainfo, sizeof (ainfo), 0); /* check finder info is OK */ if (err == 0) { /* * If the Finfo is blank and the resource file is empty, * then we assume it's not a 'true' HFS file ... * There will be not associated file if the resource fork * is empty */ if (s_entry->assoc == NULL) { err = 1; for (i = 0; i < sizeof (ainfo.info); i++) { if (ainfo.info[i] != 0) { err = 0; break; } } } if (err == 0) { /* use Unix name */ hstrncpy((unsigned char *) (hfs_ent->name), dname, HFS_MAX_FLEN); set_Finfo(ainfo.info, hfs_ent); /* * dates have already been set - but we will * set them here as well from the HFS info * shouldn't need to check for byte order, as * the source is HFS ... but we will just in case */ hfs_ent->crdate = d_getl((byte *)&ainfo.ctime.tv_sec); hfs_ent->mddate = d_getl((byte *)&ainfo.mtime.tv_sec); } } if (err) { /* not a 'true' HFS file - so try afpfile mapping */ #if 0 /* * don't print a warning as we will get lots on HFS * file systems ... */ if (verbose > 2) { fprintf(stderr, "warning: %s doesn't appear to be a %s file\n", s_entry->whole_name, hfs_types[ret].desc); } #endif ret = get_none_info(hname, dname, s_entry, TYPE_NONE); } return (ret); } #endif /* IS_MACOS_X */ /* * get_hfs_itype: get the type of HFS info for a file */ static int get_hfs_itype(char *wname, char *dname, char *htmp) { int wlen, i; int no_type = TYPE_NONE; wlen = strlen(wname) - strlen(dname); /* search through the known types looking for matches */ for (i = 1; i < hfs_num; i++) { /* skip the ones that we don't care about */ if ((hfs_types[i].flags & PROBE) || *(hfs_types[i].info) == TYPE_NONE) { continue; } strcpy(htmp, wname); /* * special case - if the info file doesn't exist * for a requested type, then remember the type - * we don't return here, as we _may_ find another type * so we save the type here in case - we will have * problems if more than one of this type ever exists ... */ if (hfs_types[i].flags & NOINFO) { no_type = i; } else { /* append or insert finderinfo filename part */ if (hfs_types[i].flags & APPEND) strcat(htmp, hfs_types[i].info); else sprintf(htmp + wlen, "%s%s", hfs_types[i].info, (hfs_types[i].flags & NOPEND) ? "" : dname); /* hack time ... Netatalk is a special case ... */ if (i == TYPE_NETA) { strcpy(htmp, wname); strcat(htmp, "/.AppleDouble/.Parent"); } if (!access(htmp, R_OK)) return (hfs_types[i].type); } } return (no_type); } /* * set_root_info: set the root folder hfs_ent from given file */ void set_root_info(char *name) { dir_ent *s_entry; hfsdirent *hfs_ent; int i; s_entry = root->self; hfs_ent = (hfsdirent *) e_malloc(sizeof (hfsdirent)); memset(hfs_ent, 0, sizeof (hfsdirent)); /* make sure root has a valid hfs_ent */ s_entry->hfs_ent = root->hfs_ent = hfs_ent; /* search for correct type of root info data */ for (i = 1; i < hfs_num; i++) { if ((hfs_types[i].flags & PROBE) || (hfs_types[i].get_info == get_none_info)) continue; if ((*(hfs_types[i].get_dir))(name, "", s_entry, i) == i) return; } } /* * get_hfs_dir: set the HFS directory name */ int get_hfs_dir(char *wname, char *dname, dir_ent *s_entry) { int type; /* get the HFS file type from the info file (if it exists) */ type = get_hfs_itype(wname, dname, tmp); /* try to get the required info */ type = (*(hfs_types[type].get_dir)) (tmp, dname, s_entry, type); return (type); } /* * get_hfs_info: set the HFS info for a file */ int get_hfs_info(char *wname, char *dname, dir_ent *s_entry) { int type, wlen, i; wlen = strlen(wname) - strlen(dname); /* we may already know the type of Unix/HFS file - so process */ if (s_entry->hfs_type != TYPE_NONE) { type = s_entry->hfs_type; strcpy(tmp, wname); /* append or insert finderinfo filename part */ if (hfs_types[type].flags & APPEND) strcat(tmp, hfs_types[type].info); else sprintf(tmp + wlen, "%s%s", hfs_types[type].info, (hfs_types[type].flags & NOPEND) ? "" : dname); type = (*(hfs_types[type].get_info))(tmp, dname, s_entry, type); /* if everything is as expected, then return */ if (s_entry->hfs_type == type) return (type); } /* we don't know what type we have so, find out */ for (i = 1; i < hfs_num; i++) { if ((hfs_types[i].flags & PROBE) || *(hfs_types[i].info) == TYPE_NONE) { continue; } strcpy(tmp, wname); /* append or insert finderinfo filename part */ if (hfs_types[i].flags & APPEND) { strcat(tmp, hfs_types[i].info); } else { sprintf(tmp + wlen, "%s%s", hfs_types[i].info, (hfs_types[i].flags & NOPEND) ? "" : dname); } /* if the file exists - and not a type we've already tried */ if (!access(tmp, R_OK) && i != s_entry->hfs_type) { type = (*(hfs_types[i].get_info))(tmp, dname, s_entry, i); s_entry->hfs_type = type; return (type); } } /* nothing found, so just a Unix file */ type = (*(hfs_types[TYPE_NONE].get_info))(wname, dname, s_entry, TYPE_NONE); return (type); } /* * get_hfs_rname: set the name of the Unix rsrc file for a file * * For the time being we ignore the 'NOINFO' flag - the only case * at the moment is for MacOS X HFS files - for files the resource * fork exists - so testing the "filename/rsrc" pseudo file as * the 'info' filename is OK ... */ int get_hfs_rname(char *wname, char *dname, char *rname) { int wlen, type, i; int p_fd = -1; wlen = strlen(wname) - strlen(dname); /* try to find what sort of Unix HFS file type we have */ for (i = 1; i < hfs_num; i++) { /* skip if don't want to probe the files - (default) */ if (hfs_types[i].flags & PROBE) continue; strcpy(rname, wname); /* if we have a different info file, the find out it's type */ if (*(hfs_types[i].rsrc) && *(hfs_types[i].info)) { /* first test the Info file */ /* append or insert finderinfo filename part */ if (hfs_types[i].flags & APPEND) { strcat(rname, hfs_types[i].info); } else { sprintf(rname + wlen, "%s%s", hfs_types[i].info, (hfs_types[i].flags & NOPEND) ? "" : dname); } /* if it exists, then check the Rsrc file */ if (!access(rname, R_OK)) { if (hfs_types[i].flags & APPEND) { sprintf(rname + wlen, "%s%s", dname, hfs_types[i].rsrc); } else { sprintf(rname + wlen, "%s%s", hfs_types[i].rsrc, dname); } /* * for some types, a rsrc fork may not exist, * so just return the current type * in these cases */ if (hfs_types[i].flags & NORSRC || !access(rname, R_OK)) return (hfs_types[i].type); } } else { /* * if we are probing, * then have a look at the contents to find type */ if (p_fd < 0) { /* open file, if not already open */ if ((p_fd = open(wname, O_RDONLY | O_BINARY)) < 0) { /* can't open it, then give up */ return (TYPE_NONE); } else { if ((p_num = read(p_fd, p_buf, sizeof (p_buf))) <= 0) { /* * can't read, or zero length * - give up */ close(p_fd); return (TYPE_NONE); } /* get file pointer and close file */ p_fp = fdopen(p_fd, "rb"); close(p_fd); if (p_fp == NULL) return (TYPE_NONE); } } /* * call routine to do the work * - use the given dname as this * is the name we may use on the CD */ type = (*(hfs_types[i].get_info)) (rname, dname, 0, i); if (type != 0) { fclose(p_fp); return (type); } if (p_fp) { /* * close file * - just use contents of buffer next time */ fclose(p_fp); p_fp = NULL; } } } return (0); } /* * hfs_exclude: file/directory names that hold finder/resource * information that we want to exclude from the tree. * These files/directories are processed later ... */ int hfs_exclude(char *d_name) { /* we don't exclude "." and ".." */ if (strcmp(d_name, ".") == 0) return (0); if (strcmp(d_name, "..") == 0) return (0); /* do not add the following to our list of dir entries */ if (DO_CAP & hselect) { /* CAP */ if (strcmp(d_name, ".finderinfo") == 0) return (1); if (strcmp(d_name, ".resource") == 0) return (1); if (strcmp(d_name, ".ADeskTop") == 0) return (1); if (strcmp(d_name, ".IDeskTop") == 0) return (1); if (strcmp(d_name, "Network Trash Folder") == 0) return (1); /* * special case when HFS volume is mounted using Linux's hfs_fs * Brad Midgley */ if (strcmp(d_name, ".rootinfo") == 0) return (1); } if (DO_ESH & hselect) { /* Helios EtherShare files */ if (strcmp(d_name, ".rsrc") == 0) return (1); if (strcmp(d_name, ".Desktop") == 0) return (1); if (strcmp(d_name, ".DeskServer") == 0) return (1); if (strcmp(d_name, ".Label") == 0) return (1); } if (DO_DBL & hselect) { /* Apple Double */ /* * special case when HFS volume is mounted using Linux's hfs_fs */ if (strcmp(d_name, "%RootInfo") == 0) return (1); /* * have to be careful here - a filename starting with '%' * may be vaild if the next two letters are a hex character - * unfortunately '%' 'digit' 'digit' may be a valid resource * file name ... */ if (*d_name == '%') if (hex2char(d_name) == 0) return (1); } if (DO_NETA & hselect) { if (strcmp(d_name, ".AppleDouble") == 0) return (1); if (strcmp(d_name, ".AppleDesktop") == 0) return (1); } if ((DO_FEU & hselect) || (DO_FEL & hselect)) { /* PC Exchange */ if (strcmp(d_name, "RESOURCE.FRK") == 0) return (1); if (strcmp(d_name, "FINDER.DAT") == 0) return (1); if (strcmp(d_name, "DESKTOP") == 0) return (1); if (strcmp(d_name, "FILEID.DAT") == 0) return (1); if (strcmp(d_name, "resource.frk") == 0) return (1); if (strcmp(d_name, "finder.dat") == 0) return (1); if (strcmp(d_name, "desktop") == 0) return (1); if (strcmp(d_name, "fileid.dat") == 0) return (1); } if (DO_SGI & hselect) { /* SGI */ if (strcmp(d_name, ".HSResource") == 0) return (1); if (strcmp(d_name, ".HSancillary") == 0) return (1); } if (DO_DAVE & hselect) { /* DAVE */ if (strcmp(d_name, "resource.frk") == 0) return (1); if (strcmp(d_name, "DesktopFolderDB") == 0) return (1); } #ifndef _WIN32 /* * NTFS streams are not "seen" as files, * so WinNT will not see these files - * so ignore - used for testing under Unix */ if (DO_SFM & hselect) { /* SFM */ char *dn = strrchr(d_name, ':'); if (dn) { if (strcmp(dn, ":Afp_Resource") == 0) return (1); if (strcmp(dn, ":Comments") == 0) return (1); if (strcmp(dn, ":Afp_AfpInfo") == 0) return (1); } } #endif /* _WIN32 */ if (DO_XDBL & hselect) { /* XDB */ if (strncmp(d_name, "._", 2) == 0) return (1); } return (0); } /* * print_hfs_info: print info about the HFS files. * */ void print_hfs_info(dir_ent *s_entry) { fprintf(stderr, "Name: %s\n", s_entry->whole_name); fprintf(stderr, "\tFile type: %s\n", hfs_types[s_entry->hfs_type].desc); fprintf(stderr, "\tHFS Name: %s\n", s_entry->hfs_ent->name); fprintf(stderr, "\tISO Name: %s\n", s_entry->isorec.name); fprintf(stderr, "\tCREATOR: %s\n", s_entry->hfs_ent->u.file.creator); fprintf(stderr, "\tTYPE: %s\n", s_entry->hfs_ent->u.file.type); } /* * hfs_init: sets up the mapping list from the afpfile as well * the default mapping (with or without) an afpfile */ void hfs_init(char *name, Ushort fdflags, Uint hfs_select) { FILE *fp; /* File pointer */ int count = NUMMAP; /* max number of entries */ char buf[PATH_MAX]; /* working buffer */ afpmap *amap; /* mapping entry */ char *c, *t, *e; int i; /* setup number of Unix/HFS filetype - we may wish to not bother */ if (hfs_select) { hfs_num = sizeof (hfs_types) / sizeof (struct hfs_type); /* * code below needs to be tidied up * - most can be made redundant */ for (i = 0; i < hfs_num; i++) hfs_types[i].flags &= ~1; /* 0xfffffffe */ for (i = 1; i < hfs_num; i++) if (!((1 << i) & hfs_select)) hfs_types[i].flags |= PROBE; hselect = hfs_select; } else hfs_num = hselect = 0; #ifdef DEBUG for (i = 0; i < hfs_num; i++) fprintf(stderr, "type = %d flags = %d\n", i, hfs_types[i].flags); #endif /* DEBUG */ /* min length set to max to start with */ mlen = PATH_MAX; #ifdef USE_MAGIC /* initialise magic state */ if (magic_filename) { magic_state = magic_open(MAGIC_ERROR); if (magic_state == NULL) perr("failed to initialise libmagic"); if (magic_load(magic_state, magic_filename) == -1) { fprintf(stderr, "failed to open magic file: %s\n", magic_error(magic_state)); exit(1); } } #endif /* USE_MAGIC */ /* set defaults */ map_num = last_ent = 0; /* allocate memory for the default entry */ defmap = (afpmap *) e_malloc(sizeof (afpmap)); /* set default values */ defmap->extn = DEFMATCH; /* make sure creator and type are 4 chars long */ strcpy(defmap->type, BLANK); strcpy(defmap->creator, BLANK); e = deftype; t = defmap->type; while (*e && (e - deftype) < CT_SIZE) *t++ = *e++; e = defcreator; c = defmap->creator; while (*e && (e - defcreator) < CT_SIZE) *c++ = *e++; /* length is not important here */ defmap->elen = 0; /* no flags */ defmap->fdflags = fdflags; /* no afpfile - no mappings */ if (*name == '\0') { map = NULL; return; } if ((fp = fopen(name, "r")) == NULL) perr("unable to open mapping file"); map = (afpmap **) e_malloc(NUMMAP * sizeof (afpmap *)); /* read afpfile line by line */ while (fgets(buf, PATH_MAX, fp) != NULL) { /* ignore any comment lines */ c = tmp; *c = '\0'; if (sscanf(buf, "%1s", c) == EOF || *c == '#') continue; /* increase list size if needed */ if (map_num == count) { count += NUMMAP; map = (afpmap **)realloc(map, count * sizeof (afpmap *)); if (map == NULL) perr("not enough memory"); } /* allocate memory for this entry */ amap = (afpmap *) e_malloc(sizeof (afpmap)); t = amap->type; c = amap->creator; /* extract the info */ if (sscanf(buf, "%s%*s%*1s%c%c%c%c%*1s%*1s%c%c%c%c%*1s", tmp, c, c + 1, c + 2, c + 3, t, t + 1, t + 2, t + 3) != 9) { fprintf(stderr, "error scanning afpfile %s - continuing", name); free(amap); continue; } /* copy the extension found */ if ((amap->extn = (char *) strdup(tmp)) == NULL) perr("not enough memory"); /* set end-of-string */ *(t + 4) = *(c + 4) = '\0'; /* find the length of the extension */ amap->elen = strlen(amap->extn); /* set flags */ amap->fdflags = fdflags; /* see if we have the default creator/type */ if (strcmp(amap->extn, DEFMATCH) == 0) { /* get rid of the old default */ free(defmap); /* make this the default */ defmap = amap; continue; } /* update the smallest extension length */ mlen = MIN(mlen, amap->elen); /* add entry to the list */ map[map_num++] = amap; } /* free up some memory */ if (map_num != count) { map = (afpmap **) realloc(map, map_num * sizeof (afpmap *)); if (map == NULL) perr("not enough memory"); } } #ifdef USE_MAGIC static int try_map_magic(char *whole_name, char **type, /* set type */ char **creator /* set creator */) { const char * ret = magic_file(magic_state, whole_name); #ifdef DEBUG fprintf(stderr, "magic_file(magic_state, \"%s\"): %s\n", whole_name, ret ? ret : "NULL"); #endif /* * check that we found a match; ignore results in the * wrong format (probably due to libmagic's built-in rules) */ if (ret && strcspn(ret, " ") == CT_SIZE && ret[CT_SIZE] == ' ' && strcspn(ret + CT_SIZE + 1, " ") == CT_SIZE) { memcpy(tmp_type, ret, CT_SIZE); tmp_type[CT_SIZE] = 0; memcpy(tmp_creator, ret + CT_SIZE + 1, CT_SIZE); tmp_creator[CT_SIZE] = 0; #ifdef DEBUG fprintf(stderr, "tmp_type = \"%s\"; tmp_creator = \"%s\"\n", tmp_type, tmp_creator); #endif *type = tmp_type; *creator = tmp_creator; return (1); } return (0); } #endif /* USE_MAGIC */ /* * map_ext: map a files extension with the list to get type/creator */ static void map_ext(char *name, /* filename */ char **type, /* set type */ char **creator, /* set creator */ short *fdflags, /* set finder flags */ char *whole_name) { int i; /* loop counter */ int len; /* filename length */ afpmap *amap; /* mapping entry */ const char *ret; /* we don't take fdflags from the map or magic file */ *fdflags = defmap->fdflags; #ifdef USE_MAGIC /* * if we have a magic file and we want to search it first, * then try to get a match */ if (magic_state && hfs_last == MAP_LAST && try_map_magic(whole_name, type, creator)) return; #endif /* USE_MAGIC */ len = strlen(name); /* have an afpfile and filename if long enough */ if (map && len >= mlen) { /* * search through the list - we start where we left off * last time in case this file is of the same type as the * last one */ for (i = 0; i < map_num; i++) { amap = map[last_ent]; /* compare the end of the filename */ /* if (strcmp((name+len - amap->elen), amap->extn) == 0) { */ if (strcasecmp((name+len - amap->elen), amap->extn) == 0) { /* set the required info */ *type = amap->type; *creator = amap->creator; *fdflags = amap->fdflags; return; } /* * move on to the next entry - wrapping round * if neccessary */ last_ent++; last_ent %= map_num; } } /* * if no matches are found, file name too short, or no afpfile, * then take defaults */ *type = defmap->type; *creator = defmap->creator; #ifdef USE_MAGIC /* * if we have a magic file and we haven't searched yet, * then try to get a match */ if (magic_state && hfs_last == MAG_LAST) try_map_magic(whole_name, type, creator); #endif /* USE_MAGIC */ } void delete_rsrc_ent(dir_ent *s_entry) { dir_ent *s_entry1 = s_entry->next; if (s_entry1 == NULL) return; s_entry->next = s_entry1->next; s_entry->assoc = NULL; free(s_entry1->name); free(s_entry1->whole_name); free(s_entry1); } void clean_hfs() { if (map) free(map); if (defmap) free(defmap); #ifdef USE_MAGIC if (magic_state) { magic_close(magic_state); magic_state = NULL; } #endif /* USE_MAGIC */ } #endif /* APPLE_HYB */ void perr(char *a) { #ifdef USE_LIBSCHILY if (a) comerr("%s\n", a); else comerr("\n"); #else if (a) fprintf(stderr, "mkhybrid: %s\n", a); perror("mkhybrid"); exit(1); #endif }