#define _BSD_SOURCE #include #include #include #include #include #include #include #include #if defined(__APPLE__) #include #include #include #endif #ifdef __linux__ #include #endif #if defined(__FreeBSD__) #include #include #endif #if defined(_AIX) #include #endif #ifndef XATTR_FINDERINFO_NAME #define XATTR_FINDERINFO_NAME "com.apple.FinderInfo" #endif #ifndef XATTR_RESOURCEFORK_NAME #define XATTR_RESOURCEFORK_NAME "com.apple.ResourceFork" #endif #include "defc.h" #include "gsos.h" #include "host_common.h" static ino_t root_ino = 0; static dev_t root_dev = 0; unsigned host_startup(void) { struct stat st; if (!g_cfg_host_path) return invalidFSTop; if (!*g_cfg_host_path) return invalidFSTop; if (host_root) free(host_root); host_root = strdup(g_cfg_host_path); if (stat(host_root, &st) < 0) { fprintf(stderr, "%s does not exist\n", host_root); return invalidFSTop; } if (!S_ISDIR(st.st_mode)) { fprintf(stderr, "%s is not a directory\n", host_root); return invalidFSTop; } root_ino = st.st_ino; root_dev = st.st_dev; return 0; } void host_shutdown(void) { if (host_root) free(host_root); host_root = NULL; root_ino = 0; root_dev = 0; } int host_is_root(struct stat *st) { return st->st_ino == root_ino && st->st_dev == root_dev; } /* * Date/time conversion */ /* * converts time_t to a gs/os readhextime date/time record. */ void host_set_date_time_rec(word32 ptr, time_t time) { if (time == 0) { for (int i = 0; i < 8; ++i) set_memory_c(ptr++, 0, 0); return; } struct tm *tm = localtime(&time); if (tm->tm_sec == 60) tm->tm_sec = 59; /* leap second */ set_memory_c(ptr++, tm->tm_sec, 0); set_memory_c(ptr++, tm->tm_min, 0); set_memory_c(ptr++, tm->tm_hour, 0); set_memory_c(ptr++, tm->tm_year, 0); set_memory_c(ptr++, tm->tm_mday - 1, 0); set_memory_c(ptr++, tm->tm_mon, 0); set_memory_c(ptr++, 0, 0); set_memory_c(ptr++, tm->tm_wday + 1, 0); } /* * converts time_t to a prodos16 date/time record. */ void host_set_date_time(word32 ptr, time_t time) { if (time == 0) { for (int i = 0; i < 4; ++i) set_memory_c(ptr++, 0, 0); return; } struct tm *tm = localtime(&time); word16 tmp = 0; tmp |= (tm->tm_year % 100) << 9; tmp |= (tm->tm_mon + 1) << 5; tmp |= tm->tm_mday; set_memory16_c(ptr, tmp, 0); ptr += 2; tmp = 0; tmp |= tm->tm_hour << 8; tmp |= tm->tm_min; set_memory16_c(ptr, tmp, 0); } word32 host_convert_date_time(time_t time) { if (time == 0) return 0; struct tm *tm = localtime(&time); word16 dd = 0; dd |= (tm->tm_year % 100) << 9; dd |= (tm->tm_mon + 1) << 5; dd |= tm->tm_mday; word16 tt = 0; tt |= tm->tm_hour << 8; tt |= tm->tm_min; return (tt << 16) | dd; } time_t host_get_date_time(word32 ptr) { word16 a = get_memory16_c(ptr + 0, 0); word16 b = get_memory16_c(ptr + 2, 0); if (!a && !b) return 0; struct tm tm; memset(&tm, 0, sizeof(tm)); tm.tm_year = (a >> 9) & 0x7f; tm.tm_mon = ((a >> 5) & 0x0f) - 1; tm.tm_mday = (a >> 0) & 0x1f; tm.tm_hour = (b >> 8) & 0x1f; tm.tm_min = (b >> 0) & 0x3f; tm.tm_sec = 0; tm.tm_isdst = -1; // 00 - 39 => 2000-2039 // 40 - 99 => 1940-1999 if (tm.tm_year < 40) tm.tm_year += 100; return mktime(&tm); } time_t host_get_date_time_rec(word32 ptr) { byte buffer[8]; for (int i = 0; i < 8; ++i) buffer[i] = get_memory_c(ptr++, 0); if (!memcmp(buffer, "\x00\x00\x00\x00\x00\x00\x00\x00", 8)) return 0; struct tm tm; memset(&tm, 0, sizeof(tm)); tm.tm_sec = buffer[0]; tm.tm_min = buffer[1]; tm.tm_hour = buffer[2]; tm.tm_year = buffer[3]; tm.tm_mday = buffer[4] + 1; tm.tm_mon = buffer[5]; tm.tm_isdst = -1; return mktime(&tm); } #if defined(__APPLE__) void host_get_file_xinfo(const char *path, struct file_info *fi) { ssize_t tmp; tmp = getxattr(path, XATTR_RESOURCEFORK_NAME, NULL, 0, 0, 0); if (tmp < 0) tmp = 0; fi->resource_eof = tmp; fi->resource_blocks = (tmp + 511) / 512; tmp = getxattr(path, XATTR_FINDERINFO_NAME, fi->finder_info, 32, 0, 0); if (tmp == 16 || tmp == 32) { fi->has_fi = 1; host_finder_info_to_filetype(fi->finder_info, &fi->file_type, &fi->aux_type); } } #elif defined(__sun) void host_get_file_xinfo(const char *path, struct file_info *fi) { struct stat st; // can't stat an xattr directly? int fd; fd = attropen(path, XATTR_RESOURCEFORK_NAME, O_RDONLY); if (fd >= 0) { if (fstat(fd, &st) == 0) { fi->resource_eof = st.st_size; fi->resource_blocks = st.st_blocks; } close(fd); } fd = attropen(path, XATTR_FINDERINFO_NAME, O_RDONLY); if (fd >= 0) { int tmp = read(fd, fi->finder_info, 32); if (tmp == 16 || tmp == 32) { fi->has_fi = 1; host_finder_info_to_filetype(fi->finder_info, &fi->file_type, &fi->aux_type); } close(fd); } } #elif defined(__linux__) void host_get_file_xinfo(const char *path, struct file_info *fi) { ssize_t tmp; tmp = getxattr(path, "user.com.apple.ResourceFork", NULL, 0); if (tmp < 0) tmp = 0; fi->resource_eof = tmp; fi->resource_blocks = (tmp + 511) / 512; tmp = getxattr(path, "user.com.apple.FinderInfo", fi->finder_info, 32); if (tmp == 16 || tmp == 32) { fi->has_fi = 1; host_finder_info_to_filetype(fi->finder_info, &fi->file_type, &fi->aux_type); } } #else void host_get_file_xinfo(const char *path, struct file_info *fi) { } #endif word32 host_get_file_info(const char *path, struct file_info *fi) { struct stat st; memset(fi, 0, sizeof(*fi)); int ok = stat(path, &st); if (ok < 0) return host_map_errno(errno); fi->eof = st.st_size; fi->blocks = st.st_blocks; fi->create_date = st.st_ctime; fi->modified_date = st.st_mtime; #if defined(__APPLE__) fi->create_date = st.st_birthtime; #endif if (S_ISDIR(st.st_mode)) { fi->storage_type = directoryFile; fi->file_type = 0x0f; if (host_is_root(&st)) fi->storage_type = 0x0f; } else if (S_ISREG(st.st_mode)) { fi->file_type = 0x06; if (st.st_size < 0x200) fi->storage_type = seedling; else if (st.st_size < 0x20000) fi->storage_type = sapling; else fi->storage_type = tree; } else { fi->storage_type = st.st_mode & S_IFMT; fi->file_type = 0; } // 0x01 = read enable // 0x02 = write enable // 0x04 = invisible // 0x08 = reserved // 0x10 = reserved // 0x20 = backup needed // 0x40 = rename enable // 0x80 = destroy enable fi->access = 0xc3; // placeholder... if (S_ISREG(st.st_mode)) { host_get_file_xinfo(path, fi); if (!fi->has_fi) { host_synthesize_file_xinfo(path, fi); } } // get file type/aux type if (fi->resource_eof) fi->storage_type = extendedFile; return 0; } #if defined(__APPLE__) word32 host_set_file_info(const char *path, struct file_info *fi) { int ok; struct attrlist list; unsigned i = 0; struct timespec dates[2]; if (fi->has_fi && fi->storage_type != 0x0d) { ok = setxattr(path, XATTR_FINDERINFO_NAME, fi->finder_info, 32, 0, 0); if (ok < 0) return host_map_errno(errno); } memset(&list, 0, sizeof(list)); memset(dates, 0, sizeof(dates)); list.bitmapcount = ATTR_BIT_MAP_COUNT; list.commonattr = 0; if (fi->create_date) { dates[i++].tv_sec = fi->create_date; list.commonattr |= ATTR_CMN_CRTIME; } if (fi->modified_date) { dates[i++].tv_sec = fi->modified_date; list.commonattr |= ATTR_CMN_MODTIME; } ok = 0; if (i) ok = setattrlist(path, &list, dates, i * sizeof(struct timespec), 0); return 0; } #elif defined(__sun) word32 host_set_file_info(const char *path, struct file_info *fi) { if (fi->has_fi && fi->storage_type != 0x0d) { int fd = attropen(path, XATTR_FINDERINFO_NAME, O_WRONLY | O_CREAT, 0666); if (fd < 0) return host_map_errno(errno); write(fd, fi->finder_info, 32); close(fd); } if (fi->modified_date) { struct timeval times[2]; memset(times, 0, sizeof(times)); //times[0] = 0; // access times[1].tv_sec = fi.modified_date; // modified int ok = utimes(path, times); if (ok < 0) return host_map_errno(errno); } return 0; } #elif defined(__linux__) word32 host_set_file_info(const char *path, struct file_info *fi) { if (fi->has_fi && fi->storage_type != 0x0d) { int ok = setxattr(path, "user.apple.FinderInfo", fi->finder_info, 32, 0); if (ok < 0) return host_map_errno(errno); } if (fi->modified_date) { struct timeval times[2]; memset(times, 0, sizeof(times)); //times[0] = 0; // access times[1].tv_sec = fi->modified_date; // modified int ok = utimes(path, times); if (ok < 0) return host_map_errno(errno); } return 0; } #else word32 host_set_file_info(const char *path, struct file_info *fi) { if (fi->modified_date) { struct timeval times[2]; memset(times, 0, sizeof(times)); times[0] = 0; // access times[1].tv_sec = fi->modified_date; // modified int ok = utimes(path, times); if (ok < 0) return host_map_errno(errno); } return 0; } #endif static int qsort_callback(const void *a, const void *b) { return strcasecmp(*(const char **)a, *(const char **)b); } unsigned host_scan_directory(const char *path, char ***out, size_t *entries, unsigned p8) { DIR *dp; char **data = NULL; size_t capacity = 0; size_t count = 0; dp = opendir(path); if (!dp) return host_map_errno_path(errno, path); for(;;) { struct dirent *d = readdir(dp); if (!d) break; const char *name = d->d_name; if (name[0] == 0) continue; if (name[0] == '.') continue; if (p8) { int ok = 1; int n = strlen(name); if (n > 15) continue; /* check for invalid characters? */ for (int i = 0; i < n; ++i) { unsigned char c = name[i]; if (isalpha(c) || isdigit(c) || c == '.') continue; ok = 0; break; } if (!ok) continue; } if (count == capacity) { char **tmp; tmp = realloc(data, (capacity + 100) * sizeof(char *)); if (!tmp) { closedir(dp); host_free_directory(data, count); return outOfMem; } data = tmp; for (int i = count; i < capacity; ++i) data[i] = 0; capacity += 100; } data[count++] = strdup(name); } closedir(dp); qsort(data, count, sizeof(char *), qsort_callback); *entries = count; *out = data; return 0; } unsigned host_storage_type(const char *path, word16 *error) { struct stat st; if (!path) { *error = badPathSyntax; return 0; } if (stat(path, &st) < 0) { *error = host_map_errno_path(errno, path); return 0; } if (S_ISDIR(st.st_mode)) { return host_is_root(&st) ? 0x0f : directoryFile; } if (S_ISREG(st.st_mode)) return standardFile; *error = badStoreType; return 0; } /* * error remapping. * NOTE - GS/OS errors are a superset of P8 errors */ static word32 enoent(const char *path) { /* some op on path return ENOENT. check if it's fileNotFound or pathNotFound */ char *p = (char *)path; for(;;) { struct stat st; p = dirname(p); if (p == NULL) break; if (p[0] == '.' && p[1] == 0) break; if (p[0] == '/' && p[1] == 0) break; if (stat(p, &st) < 0) return pathNotFound; } return fileNotFound; } word32 host_map_errno(int xerrno) { switch(xerrno) { case 0: return 0; case EBADF: return invalidAccess; #ifdef EDQUOT case EDQUOT: #endif case EFBIG: return volumeFull; case ENOENT: return fileNotFound; case ENOTDIR: return pathNotFound; case ENOMEM: return outOfMem; case EEXIST: return dupPathname; case ENOTEMPTY: return invalidAccess; default: return drvrIOError; } } word32 host_map_errno_path(int xerrno, const char *path) { if (xerrno == ENOENT) return enoent(path); return host_map_errno(xerrno); }