FileDescriptor openFileHandle(const char *path, const OpenFileHandleOptions &opts) { #ifndef _WIN32 int flags = (!opts.followSymlinks ? O_NOFOLLOW : 0) | (opts.closeOnExec ? O_CLOEXEC : 0) | #ifdef O_PATH (opts.metaDataOnly ? O_PATH : 0) | #endif ((opts.readContents && opts.writeContents) ? O_RDWR : (opts.writeContents ? O_WRONLY : opts.readContents ? O_RDONLY : 0)) | (opts.create ? O_CREAT : 0) | (opts.exclusiveCreate ? O_EXCL : 0) | (opts.truncate ? O_TRUNC : 0); auto fd = open(path, flags); if (fd == -1) { int err = errno; throw std::system_error( err, std::generic_category(), to<std::string>("open: ", path)); } FileDescriptor file(fd); #else // _WIN32 DWORD access = 0, share = 0, create = 0, attrs = 0; DWORD err; auto sec = SECURITY_ATTRIBUTES(); if (!strcmp(path, "/dev/null")) { path = "NUL:"; } auto wpath = w_string_piece(path).asWideUNC(); if (opts.metaDataOnly) { access = 0; } else { if (opts.writeContents) { access |= GENERIC_WRITE; } if (opts.readContents) { access |= GENERIC_READ; } } // We want more posix-y behavior by default share = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE; sec.nLength = sizeof(sec); sec.bInheritHandle = TRUE; if (opts.closeOnExec) { sec.bInheritHandle = FALSE; } if (opts.create && opts.exclusiveCreate) { create = CREATE_NEW; } else if (opts.create && opts.truncate) { create = CREATE_ALWAYS; } else if (opts.create) { create = OPEN_ALWAYS; } else if (opts.truncate) { create = TRUNCATE_EXISTING; } else { create = OPEN_EXISTING; } attrs = FILE_FLAG_POSIX_SEMANTICS | FILE_FLAG_BACKUP_SEMANTICS; if (!opts.followSymlinks) { attrs |= FILE_FLAG_OPEN_REPARSE_POINT; } FileDescriptor file(intptr_t( CreateFileW(wpath.c_str(), access, share, &sec, create, attrs, nullptr))); err = GetLastError(); if (!file) { throw std::system_error( err, std::system_category(), std::string("CreateFileW for openFileHandle: ") + path); } #endif if (!opts.strictNameChecks) { return file; } auto opened = file.getOpenedPath(); if (w_string_piece(opened).pathIsEqual(path)) { #if !CAN_OPEN_SYMLINKS CaseSensitivity caseSensitive = opts.caseSensitive; if (caseSensitive == CaseSensitivity::Unknown) { caseSensitive = getCaseSensitivityForPath(path); } if (caseSensitive == CaseSensitivity::CaseInSensitive) { // We need to perform one extra check for case-insensitive // paths to make sure that we didn't accidentally open // the wrong case name. checkCanonicalBaseName(path); } #endif return file; } throw std::system_error( ENOENT, std::generic_category(), to<std::string>("open(", path, "): opened path doesn't match canonical path ", opened)); }
void test_split() { { std::vector<std::string> expected{"a", "b", "c"}; std::vector<std::string> result; w_string_piece("a:b:c").split(result, ':'); ok(expected == result, "split ok"); } { std::vector<w_string> expected{"a", "b", "c"}; std::vector<w_string> result; w_string_piece("a:b:c").split(result, ':'); ok(expected == result, "split ok (w_string)"); } { std::vector<std::string> expected{"a", "b", "c"}; std::vector<std::string> result; w_string_piece("a:b:c:").split(result, ':'); ok(expected == result, "split doesn't create empty last element"); } { std::vector<std::string> expected{"a", "b", "", "c"}; std::vector<std::string> result; w_string_piece("a:b::c:").split(result, ':'); ok(expected == result, "split does create empty element"); } }
/** Checks that the basename component of the input path exactly * matches the canonical case of the path on disk. * It only makes sense to call this function on a case insensitive filesystem. * If the case does not match, throws an exception. */ static void checkCanonicalBaseName(const char *path) { #ifdef __APPLE__ struct attrlist attrlist; struct { uint32_t len; attrreference_t ref; char canonical_name[WATCHMAN_NAME_MAX]; } vomit; w_string_piece pathPiece(path); auto base = pathPiece.baseName(); memset(&attrlist, 0, sizeof(attrlist)); attrlist.bitmapcount = ATTR_BIT_MAP_COUNT; attrlist.commonattr = ATTR_CMN_NAME; if (getattrlist(path, &attrlist, &vomit, sizeof(vomit), FSOPT_NOFOLLOW) == -1) { throw std::system_error(errno, std::generic_category(), to<std::string>("checkCanonicalBaseName(", path, "): getattrlist failed")); } w_string_piece name(((char *)&vomit.ref) + vomit.ref.attr_dataoffset); if (name != base) { throw std::system_error( ENOENT, std::generic_category(), to<std::string>("checkCanonicalBaseName(", path, "): (", name, ") doesn't match canonical base (", base, ")")); } #else // Older Linux and BSDish systems are in this category. // This is the awful portable fallback used in the absence of // a system specific way to detect this. w_string_piece pathPiece(path); auto parent = pathPiece.dirName().asWString(); auto dir = w_dir_open(parent.c_str()); auto base = pathPiece.baseName(); while (true) { auto ent = dir->readDir(); if (!ent) { // We didn't find an entry that exactly matched -> fail throw std::system_error( ENOENT, std::generic_category(), to<std::string>("checkCanonicalBaseName(", path, "): no match found in parent dir")); } // Note: we don't break out early if we get a case-insensitive match // because the dir may contain multiple representations of the same // name. For example, Bash-for-Windows has dirs that contain both // "pod" and "Pod" dirs in its perl installation. We want to make // sure that we've observed all of the entries in the dir before // giving up. if (w_string_piece(ent->d_name) == base) { // Exact match; all is good! return; } } #endif }
w_string_piece w_string_piece::dirName() const { for (auto end = e_; end >= s_; --end) { if (is_slash(*end)) { /* found the end of the parent dir */ return w_string_piece(s_, end - s_); } } return nullptr; }
w_string_piece w_string_piece::baseName() const { for (auto end = e_; end >= s_; --end) { if (is_slash(*end)) { /* found the end of the parent dir */ return w_string_piece(end + 1, e_ - (end + 1)); } } return *this; }
void test_basename_dirname() { auto str = w_string_piece("foo/bar").baseName().asWString(); ok(str == "bar", "basename of foo/bar is bar: %s", str.c_str()); str = w_string_piece("foo/bar").dirName().asWString(); ok(str == "foo", "dirname of foo/bar is foo: %s", str.c_str()); str = w_string_piece("foo").dirName().asWString(); ok(str == "", "dirname of foo is nothing: %s", str.c_str()); str = w_string_piece("foo").baseName().asWString(); ok(str == "foo", "basename of foo is foo: %s", str.c_str()); str = w_string_piece("foo\\bar").baseName().asWString(); #ifdef _WIN32 ok(str == "bar", "basename of foo\\bar is bar: %s", str.c_str()); #else ok(str == "foo\\bar", "basename of foo\\bar is foo\\bar: %s", str.c_str()); #endif str = w_string_piece("foo\\bar").dirName().asWString(); #ifdef _WIN32 ok(str == "foo", "dirname of foo\\bar is foo: %s", str.c_str()); #else ok(str == "", "dirname of foo\\bar is nothing: %s", str.c_str()); #endif }
void InMemoryView::globGenerator(w_query* query, struct w_query_ctx* ctx) const { w_string_t *relative_root; if (query->relative_root) { relative_root = query->relative_root; } else { relative_root = root_path; } auto view = view_.rlock(); const auto dir = resolveDir(view, relative_root); if (!dir) { throw QueryExecError(watchman::to<std::string>( "glob_generator could not resolve ", w_string_piece(relative_root), ", check your " "relative_root parameter!")); } globGeneratorTree(ctx, query->glob_tree.get(), dir); }
void test_operator() { ok(w_string_piece("a") < w_string_piece("b"), "a < b"); ok(w_string_piece("a") < w_string_piece("ba"), "a < ba"); ok(w_string_piece("aa") < w_string_piece("b"), "aa < b"); ok(!(w_string_piece("b") < w_string_piece("a")), "b not < a"); ok(!(w_string_piece("a") < w_string_piece("a")), "a not < a"); ok(w_string_piece("A") < w_string_piece("a"), "A < a"); }