OSErr PBCreateFileUnicodeSync(FSRefParam* paramBlock) { std::string path; if (!FSRefParamMakePath(paramBlock, path)) return fnfErr; if (open(path.c_str(), O_CREAT|O_EXCL, 0666) == -1) return makeOSStatus(errno); if (paramBlock->newRef) FSPathMakeRef((uint8_t*) path.c_str(), paramBlock->newRef, nullptr); return noErr; }
OSErr PBCreateDirectoryUnicodeSync(FSRefParam* paramBlock) { std::string path; if (!FSRefParamMakePath(paramBlock, path)) return fnfErr; if (mkdir(path.c_str(), 0777) == -1) return makeOSStatus(errno); if (paramBlock->newRef) FSPathMakeRef((uint8_t*) path.c_str(), paramBlock->newRef, nullptr); return noErr; }
OSStatus FSPathMakeRefWithOptions(const uint8_t* path, long options, FSRef* fsref, Boolean* isDirectory) { if (!path || !fsref) return paramErr; std::string fullPath; char* rpath; if (options & kFSPathMakeRefDoNotFollowLeafSymlink) rpath = realpath_ns(reinterpret_cast<const char*>(path)); else rpath = realpath(reinterpret_cast<const char*>(path), nullptr); if (!rpath) return fnfErr; if (std::count(rpath, rpath+strlen(rpath), '/') > FSRef_MAX_DEPTH) { free(rpath); return unimpErr; } fullPath = rpath; free(rpath); memset(fsref, 0, sizeof(*fsref)); if (fullPath == "/") { if (isDirectory) *isDirectory = true; return noErr; } std::vector<std::string> components = string_explode(fullPath, '/', false); std::string position = "/"; size_t pos; for (size_t pos = 0; pos < components.size(); pos++) { bool found = false; struct dirent* ent; DIR* dir = opendir(position.c_str()); if (!dir) return makeOSStatus(errno); while ((ent = readdir(dir))) { if (components[pos] == ent->d_name) { found = true; fsref->inodes[pos] = ent->d_ino; if (pos+1 == components.size() && isDirectory != nullptr) *isDirectory = ent->d_type == DT_DIR; break; } } closedir(dir); if (!found) return fnfErr; if (!string_endsWith(position, "/")) position += '/'; position += components[pos]; pos++; } return noErr; }
OSStatus FSGetCatalogInfo(const FSRef* ref, uint32_t infoBits, FSCatalogInfo* infoOut, HFSUniStr255* nameOut, FSSpecPtr fsspec, FSRef* parentDir) { std::string path; if (!FSRefMakePath(ref, path)) return fnfErr; if (nameOut) { iconv_t ic = iconv_open("UTF-16", "UTF-8"); size_t s; const char* inbuf = path.c_str(); char* outbuf = reinterpret_cast<char*>(nameOut->unicode); size_t inbytesleft = path.size(), outbytesleft = sizeof(nameOut->unicode); if (ic == iconv_t(-1)) return -1; memset(nameOut->unicode, 0, outbytesleft); s = iconv(ic, (char**) &inbuf, &inbytesleft, &outbuf, &outbytesleft); iconv_close(ic); if (s == size_t(-1)) return -1; } if (parentDir) { memcpy(parentDir, ref, sizeof(FSRef)); ino_t* last = std::find(parentDir->inodes, parentDir->inodes+FSRef_MAX_DEPTH, 0); if (last != parentDir->inodes) *(last-1) = 0; } if (infoOut && infoBits != kFSCatInfoNone) { struct stat st; memset(infoOut, 0, sizeof(*infoOut)); if (::stat(path.c_str(), &st) != 0) return makeOSStatus(errno); if (infoBits & kFSCatInfoNodeFlags) { if (S_ISDIR(st.st_mode)) infoOut->nodeFlags = 4; } if (infoBits & (kFSCatInfoParentDirID|kFSCatInfoNodeID)) { if (infoBits & kFSCatInfoNodeID) infoOut->nodeID = ref->inodes[0]; for (int i = FSRef_MAX_DEPTH-1; i > 0; i--) { if (ref->inodes[i] == 0) continue; if (infoBits & kFSCatInfoParentDirID) infoOut->parentDirID = ref->inodes[i-1]; if (infoBits & kFSCatInfoNodeID) infoOut->nodeID = ref->inodes[i]; } } if (infoBits & kFSCatInfoDataSizes) { infoOut->dataLogicalSize = st.st_size; infoOut->dataPhysicalSize = st.st_blocks*512; } int uaccess; if (st.st_uid == getuid()) uaccess = st.st_mode & 0700; else if (hasgid(st.st_gid)) uaccess = st.st_mode & 070; else uaccess = st.st_mode & 07; if (infoBits & kFSCatInfoPermissions) { const uid_t uid = getuid(); infoOut->fsPermissionInfo.userID = st.st_uid; infoOut->fsPermissionInfo.groupID = st.st_gid; infoOut->fsPermissionInfo.mode = st.st_mode & 07777; infoOut->fsPermissionInfo.userAccess = uaccess; } if (infoBits & kFSCatInfoUserPrivs) { if (!(uaccess & 2)) infoOut->userPrivileges |= 0x4; // kioACUserNoMakeChangesMask if (getuid() != st.st_uid) infoOut->userPrivileges |= 0x80; // kioACUserNotOwnerMask } if (infoBits & kFSCatInfoCreateDate) infoOut->createDate = Darling::time_tToUTC(st.st_ctime); if (infoBits & kFSCatInfoContentMod) infoOut->attributeModDate = infoOut->contentModDate = Darling::time_tToUTC(st.st_mtime); if (infoBits & kFSCatInfoAccessDate) infoOut->accessDate = Darling::time_tToUTC(st.st_atime); } return noErr; }
OSStatus LSOpenApplication(const LSApplicationParameters *appParams, ProcessSerialNumber *outPSN) { if (!appParams) return paramErr; std::string exePath; std::vector<char*> argv; std::unique_ptr<std::vector<char*>> envp; int pipefds[2]; pid_t pid; OSStatus rv = noErr; if (!FSRefMakePath(appParams->application, exePath)) return fnfErr; if (appParams->argv != nullptr) { CFIndex count = CFArrayGetCount(appParams->argv); for (CFIndex i = 0; i < count; i++) { CFStringRef ref = (CFStringRef) CFArrayGetValueAtIndex(appParams->argv, i); if (CFGetTypeID(ref) != CFStringGetTypeID()) return paramErr; argv.push_back((char*) CFStringGetCStringPtr((CFStringRef) CFArrayGetValueAtIndex(appParams->argv, i), kCFStringEncodingUTF8)); } argv.push_back(nullptr); } else { argv.push_back((char*) exePath.c_str()); argv.push_back(nullptr); } if (appParams->environment != nullptr) { CFIndex count = CFDictionaryGetCount(appParams->environment); envp.reset(new std::vector<char*>); CFDictionaryApplyFunction(appParams->environment, [](const void* key, const void* value, void* context) { CFStringRef skey = (CFStringRef) key; CFStringRef svalue = (CFStringRef) value; std::vector<const char*>* envp = (std::vector<const char*>*) context; if (CFGetTypeID(skey) == CFStringGetTypeID() && CFGetTypeID(svalue) == CFStringGetTypeID()) { char* str = new char[CFStringGetLength(skey) + CFStringGetLength(svalue) + 1]; strcpy(str, CFStringGetCStringPtr(skey, kCFStringEncodingUTF8)); strcat(str, "="); strcat(str, CFStringGetCStringPtr(svalue, kCFStringEncodingUTF8)); envp->push_back(str); } }, envp.get()); } // https://stackoverflow.com/questions/1584956/how-to-handle-execvp-errors-after-fork if (pipe(pipefds)) { if (envp) std::for_each(envp->begin(), envp->end(), [](char* p) { delete [] p; }); return makeOSStatus(errno); } fcntl(pipefds[1], F_SETFD, fcntl(pipefds[1], F_GETFD) | FD_CLOEXEC); switch (pid = fork()) { case -1: rv = makeOSStatus(errno); break; case 0: close(pipefds[0]); execvpe(exePath.c_str(), &argv[0], envp ? &(*envp)[0] : nullptr); write(pipefds[1], &errno, sizeof(int)); _exit(1); break; default: { int err, count; close(pipefds[1]); while ((count = read(pipefds[0], &err, sizeof(errno))) == -1) { if (errno != EAGAIN && errno != EINTR) break; } if (count > 0) rv = makeOSStatus(err); close(pipefds[0]); } } if (envp) std::for_each(envp->begin(), envp->end(), [](char* p) { delete [] p; }); return rv; }
OSStatus FSGetCatalogInfo(const FSRef* ref, uint32_t infoBits, FSCatalogInfo* infoOut, HFSUniStr255* nameOut, FSSpecPtr fsspec, FSRef* parentDir) { std::string path; if (!FSRefMakePath(ref, path)) return fnfErr; if (nameOut) { CFStringRef cfstr = CFStringCreateWithCString(NULL, path.c_str(), kCFStringEncodingUTF8); nameOut->length = std::min<size_t>(path.length(), 255); CFStringGetCharacters(cfstr, CFRangeMake(0, nameOut->length), nameOut->unicode); CFRelease(cfstr); } if (parentDir) { memcpy(parentDir, ref, sizeof(FSRef)); ino_t* last = std::find(parentDir->inodes, parentDir->inodes+FSRef_MAX_DEPTH, 0); if (last != parentDir->inodes) *(last-1) = 0; } if (infoOut && infoBits != kFSCatInfoNone) { struct stat st; memset(infoOut, 0, sizeof(*infoOut)); if (::stat(path.c_str(), &st) != 0) return makeOSStatus(errno); if (infoBits & kFSCatInfoNodeFlags) { if (S_ISDIR(st.st_mode)) infoOut->nodeFlags = 4; } if (infoBits & (kFSCatInfoParentDirID|kFSCatInfoNodeID)) { if (infoBits & kFSCatInfoNodeID) infoOut->nodeID = ref->inodes[0]; for (int i = FSRef_MAX_DEPTH-1; i > 0; i--) { if (ref->inodes[i] == 0) continue; if (infoBits & kFSCatInfoParentDirID) infoOut->parentDirID = ref->inodes[i-1]; if (infoBits & kFSCatInfoNodeID) infoOut->nodeID = ref->inodes[i]; } } if (infoBits & kFSCatInfoDataSizes) { infoOut->dataLogicalSize = st.st_size; infoOut->dataPhysicalSize = st.st_blocks*512; } int uaccess; if (st.st_uid == getuid()) uaccess = st.st_mode & 0700; else if (hasgid(st.st_gid)) uaccess = st.st_mode & 070; else uaccess = st.st_mode & 07; if (infoBits & kFSCatInfoPermissions) { const uid_t uid = getuid(); infoOut->fsPermissionInfo.userID = st.st_uid; infoOut->fsPermissionInfo.groupID = st.st_gid; infoOut->fsPermissionInfo.mode = st.st_mode & 07777; infoOut->fsPermissionInfo.userAccess = uaccess; } if (infoBits & kFSCatInfoUserPrivs) { if (!(uaccess & 2)) infoOut->userPrivileges |= 0x4; // kioACUserNoMakeChangesMask if (getuid() != st.st_uid) infoOut->userPrivileges |= 0x80; // kioACUserNotOwnerMask } if (infoBits & kFSCatInfoCreateDate) infoOut->createDate = Darling::time_tToUTC(st.st_ctime); if (infoBits & kFSCatInfoContentMod) infoOut->attributeModDate = infoOut->contentModDate = Darling::time_tToUTC(st.st_mtime); if (infoBits & kFSCatInfoAccessDate) infoOut->accessDate = Darling::time_tToUTC(st.st_atime); } return noErr; }