libwreport 3.42
sys.h
1#ifndef WREPORT_SYS_H
2#define WREPORT_SYS_H
3
10
11#include <dirent.h>
12#include <fcntl.h>
13#include <filesystem>
14#include <iterator>
15#include <memory>
16#include <string>
17#include <sys/resource.h>
18#include <sys/stat.h>
19#include <sys/time.h>
20#include <sys/types.h>
21#include <unistd.h>
22
23namespace wreport::sys {
24
31std::filesystem::path with_suffix(const std::filesystem::path& path,
32 const std::string& suffix);
33
39std::unique_ptr<struct stat> stat(const char* pathname);
40
46std::unique_ptr<struct stat> stat(const std::string& pathname);
47
53std::unique_ptr<struct stat> stat(const std::filesystem::path& path);
54
59void stat(const char* pathname, struct stat& st);
60
65void stat(const std::string& pathname, struct stat& st);
66
71void stat(const std::filesystem::path& path, struct stat& st);
72
78[[deprecated("Use std::filesystem::is_directory")]] bool
79isdir(const std::string& pathname);
80
82[[deprecated("Use std::filesystem::is_block_file")]] bool
83isblk(const std::string& pathname);
84
86[[deprecated("Use std::filesystem::is_character_file")]] bool
87ischr(const std::string& pathname);
88
90[[deprecated("Use std::filesystem::is_fifo")]] bool
91isfifo(const std::string& pathname);
92
94[[deprecated("Use std::filesystem::is_symlink")]] bool
95islnk(const std::string& pathname);
96
98[[deprecated("Use std::filesystem::is_regular_file")]] bool
99isreg(const std::string& pathname);
100
102[[deprecated("Use std::filesystem::is_socket")]] bool
103issock(const std::string& pathname);
104
106time_t timestamp(const std::filesystem::path& file);
107
109time_t timestamp(const std::filesystem::path& file, time_t def);
110
112size_t size(const std::filesystem::path& file);
113
115size_t size(const std::filesystem::path& file, size_t def);
116
118ino_t inode(const std::filesystem::path& file);
119
121ino_t inode(const std::filesystem::path& file, ino_t def);
122
124bool access(const std::filesystem::path& s, int m);
125
127[[deprecated("Use std::filesystem::exists")]] bool exists(const std::string& s);
128
130[[deprecated("Use std::filesystem::current_path")]] std::string getcwd();
131
133[[deprecated("Use std::filesystem::current_path")]] void
134chdir(const std::string& dir);
135
137void chroot(const std::filesystem::path& dir);
138
140mode_t umask(mode_t mask);
141
143std::filesystem::path abspath(const std::filesystem::path& path);
144[[deprecated("Use abspath(const std::filesystem::path&)")]] std::string
145abspath(const std::string& pathname);
146std::filesystem::path abspath(const char* path);
147
153class MMap
154{
155 void* addr;
156 size_t length;
157
158public:
159 MMap(const MMap&) = delete;
160 MMap(MMap&&);
161 MMap(void* addr, size_t length);
162 ~MMap();
163
164 MMap& operator=(const MMap&) = delete;
165 MMap& operator=(MMap&&);
166
167 size_t size() const { return length; }
168
169 void munmap();
170
171 template <typename T> operator const T*() const
172 {
173 return reinterpret_cast<const T*>(addr);
174 }
175
176 template <typename T> operator T*() const
177 {
178 return reinterpret_cast<T*>(addr);
179 }
180};
181
193class FileDescriptor
194{
195protected:
196 int fd = -1;
197
198public:
199 FileDescriptor();
200 FileDescriptor(FileDescriptor&& o);
201 explicit FileDescriptor(int fd);
202 virtual ~FileDescriptor();
203
204 // We can copy at the FileDescriptor level because the destructor does not
205 // close fd
206 FileDescriptor(const FileDescriptor& o) = default;
207 FileDescriptor& operator=(const FileDescriptor& o) = default;
208
216 [[noreturn]] virtual void throw_error(const char* desc);
217
225 [[noreturn]] virtual void throw_error_string(const std::string& desc);
226
234 [[noreturn]] virtual void throw_runtime_error(const char* desc);
235
237 bool is_open() const;
238
244 void close();
245
246 void fstat(struct stat& st);
247 void fchmod(mode_t mode);
248
249 void futimens(const ::timespec ts[2]);
250
251 void fsync();
252 void fdatasync();
253
254 int dup();
255
256 size_t read(void* buf, size_t count);
257
265 bool read_all_or_retry(void* buf, size_t count);
266
271 void read_all_or_throw(void* buf, size_t count);
272
273 size_t write(const void* buf, size_t count);
274
275 template <typename Container> size_t write(const Container& c)
276 {
277 return write(c.data(),
278 c.size() * sizeof(typename Container::value_type));
279 }
280
282 void write_all_or_retry(const void* buf, size_t count);
283
284 template <typename Container> void write_all_or_retry(const Container& c)
285 {
286 write_all_or_retry(c.data(),
287 c.size() * sizeof(typename Container::value_type));
288 }
289
294 void write_all_or_throw(const void* buf, size_t count);
295
296 template <typename Container> void write_all_or_throw(const Container& c)
297 {
298 write_all_or_throw(c.data(),
299 c.size() * sizeof(typename Container::value_type));
300 }
301
302 off_t lseek(off_t offset, int whence = SEEK_SET);
303
304 size_t pread(void* buf, size_t count, off_t offset);
305 size_t pwrite(const void* buf, size_t count, off_t offset);
306
307 template <typename Container>
308 size_t pwrite(const Container& c, off_t offset)
309 {
310 return pwrite(c.data(),
311 c.size() * sizeof(typename Container::value_type),
312 offset);
313 }
314
315 void ftruncate(off_t length);
316
317 MMap mmap(size_t length, int prot, int flags, off_t offset = 0);
318
325 bool ofd_setlk(::flock&);
326
336 bool ofd_setlkw(::flock&, bool retry_on_signal = true);
337
343 bool ofd_getlk(::flock&);
344
346 int getfl();
347
349 void setfl(int flags);
350
351 operator int() const { return fd; }
352};
353
357class PreserveFileTimes
358{
359protected:
361 ::timespec ts[2];
362
363public:
364 explicit PreserveFileTimes(FileDescriptor fd);
365 ~PreserveFileTimes();
366};
367
371class NamedFileDescriptor : public FileDescriptor
372{
373protected:
374 std::filesystem::path path_;
375
376public:
377 NamedFileDescriptor(int fd, const std::filesystem::path& path);
378 NamedFileDescriptor(NamedFileDescriptor&&);
379 NamedFileDescriptor& operator=(NamedFileDescriptor&&);
380
381 // We can copy at the NamedFileDescriptor level because the destructor does
382 // not close fd
383 NamedFileDescriptor(const NamedFileDescriptor& o) = default;
384 NamedFileDescriptor& operator=(const NamedFileDescriptor& o) = default;
385
386 [[noreturn]] virtual void throw_error(const char* desc) override;
387 [[noreturn]] virtual void
388 throw_error_string(const std::string& desc) override;
389 [[noreturn]] virtual void throw_runtime_error(const char* desc) override;
390
392 [[deprecated("use path() instead")]] std::string name() const
393 {
394 return path_.string();
395 }
396 const std::filesystem::path& path() const { return path_; }
397};
398
402struct ManagedNamedFileDescriptor : public NamedFileDescriptor
403{
404 using NamedFileDescriptor::NamedFileDescriptor;
405
406 ManagedNamedFileDescriptor(ManagedNamedFileDescriptor&&) = default;
407 ManagedNamedFileDescriptor(const ManagedNamedFileDescriptor&) = delete;
408
417
418 ManagedNamedFileDescriptor&
419 operator=(const ManagedNamedFileDescriptor&) = delete;
420 ManagedNamedFileDescriptor& operator=(ManagedNamedFileDescriptor&&);
421};
422
426struct Path : public ManagedNamedFileDescriptor
427{
431 struct iterator
432 {
433 using iterator_category = std::input_iterator_tag;
434 using value_type = ::dirent;
435 using difference_type = int;
436 using pointer = ::dirent*;
437 using reference = ::dirent&;
438
439 Path* path = nullptr;
440 DIR* dir = nullptr;
441 ::dirent* cur_entry = nullptr;
442
443 // End iterator
444 iterator();
445 // Start iteration on dir
446 explicit iterator(Path& dir);
447 iterator(iterator&) = delete;
448 iterator(iterator&& o) : dir(o.dir), cur_entry(o.cur_entry)
449 {
450 o.dir = nullptr;
451 o.cur_entry = nullptr;
452 }
453 ~iterator();
454 iterator& operator=(iterator&) = delete;
455 iterator& operator=(iterator&&) = delete;
456
457 bool operator==(const iterator& i) const;
458 bool operator!=(const iterator& i) const;
459 ::dirent& operator*() const { return *cur_entry; }
460 ::dirent* operator->() const { return cur_entry; }
461 iterator& operator++();
462
464 bool isdir() const;
465
467 bool isblk() const;
468
470 bool ischr() const;
471
473 bool isfifo() const;
474
476 bool islnk() const;
477
479 bool isreg() const;
480
482 bool issock() const;
483
485 Path open_path(int flags = 0) const;
486 };
487
488 using ManagedNamedFileDescriptor::ManagedNamedFileDescriptor;
489
493 explicit Path(const std::filesystem::path& pathname, int flags = 0,
494 mode_t mode = 0777);
498 Path(Path& parent, const char* pathname, int flags = 0, mode_t mode = 0777);
499 Path(const Path&) = delete;
500 Path(Path&&) = default;
501 Path& operator=(const Path&) = delete;
502 Path& operator=(Path&&) = default;
503
505 void open(int flags, mode_t mode = 0777);
506
507 DIR* fdopendir();
508
511
514
515 int openat(const char* pathname, int flags, mode_t mode = 0777);
516
518 int openat_ifexists(const char* pathname, int flags, mode_t mode = 0777);
519
520 bool faccessat(const char* pathname, int mode, int flags = 0);
521
522 void fstatat(const char* pathname, struct stat& st);
523
525 bool fstatat_ifexists(const char* pathname, struct stat& st);
526
528 void lstatat(const char* pathname, struct stat& st);
529
531 bool lstatat_ifexists(const char* pathname, struct stat& st);
532
533 void unlinkat(const char* pathname);
534
535 void mkdirat(const char* pathname, mode_t mode = 0777);
536
538 void rmdirat(const char* pathname);
539
540 void symlinkat(const char* target, const char* linkpath);
541
542 std::string readlinkat(const char* pathname);
543
549 void rmtree();
550
551 static std::string mkdtemp(const std::filesystem::path& prefix);
552 [[deprecated(
553 "Use mkdtemp(const std::filesystem::path&)")]] static std::string
554 mkdtemp(const std::string& prefix);
555 static std::string mkdtemp(const char* prefix);
556 static std::string mkdtemp(char* pathname_template);
557};
558
562class File : public ManagedNamedFileDescriptor
563{
564public:
565 using ManagedNamedFileDescriptor::ManagedNamedFileDescriptor;
566
567 File(File&&) = default;
568 File(const File&) = delete;
569
573 explicit File(const std::filesystem::path& path);
574 explicit File(const char* path);
575 [[deprecated("Use File(const std::filesystem::path&)")]] explicit File(
576 const std::string& path);
577
579 File(const std::filesystem::path& path, int flags, mode_t mode = 0777);
580
581 File& operator=(const File&) = delete;
582 File& operator=(File&&) = default;
583
585 void open(int flags, mode_t mode = 0777);
586
591 bool open_ifexists(int flags, mode_t mode = 0777);
592
593 static File mkstemp(const std::filesystem::path& prefix);
594 [[deprecated("Use mkstemp(const std::filesysten::path&)")]] static File
595 mkstemp(const std::string& prefix);
596 static File mkstemp(const char* prefix);
597 static File mkstemp(char* pathname_template);
598};
599
605class Tempfile : public File
606{
607protected:
608 bool m_unlink_on_exit = true;
609
610public:
611 Tempfile();
612 explicit Tempfile(const std::filesystem::path& prefix);
613 [[deprecated("Use Tempfile(const std::string&)")]] explicit Tempfile(
614 const std::string& prefix);
615 explicit Tempfile(const char* prefix);
616 ~Tempfile();
617
619 void unlink_on_exit(bool val);
620
622 void unlink();
623};
624
631class Tempdir : public Path
632{
633protected:
634 bool m_rmtree_on_exit = true;
635
636public:
637 Tempdir();
638 explicit Tempdir(const std::filesystem::path& prefix);
639 [[deprecated(
640 "Use Tempdir(const "
641 "std::filesystem::path&)")]] explicit Tempdir(const std::string&
642 prefix);
643 explicit Tempdir(const char* prefix);
644 ~Tempdir();
645
647 void rmtree_on_exit(bool val);
648};
649
651std::string read_file(const std::filesystem::path& file);
652[[deprecated("Use read_file(const std::filesystem::path&)")]] std::string
653read_file(const std::string& file);
654std::string read_file(const char* file);
655
662void write_file(const std::filesystem::path& file, const std::string& data,
663 mode_t mode = 0777);
664[[deprecated("Use write_file(const std::filesystem::path&, …)")]] void
665write_file(const std::string& file, const std::string& data,
666 mode_t mode = 0777);
667void write_file(const char* file, const std::string& data, mode_t mode = 0777);
668
675void write_file(const std::filesystem::path& file, const void* data,
676 size_t size, mode_t mode = 0777);
677[[deprecated("Use write_file(const std::filesystem::path&, …)")]] void
678write_file(const std::string& file, const void* data, size_t size,
679 mode_t mode = 0777);
680void write_file(const char* file, const void* data, size_t size,
681 mode_t mode = 0777);
682
692void write_file_atomically(const std::filesystem::path& file,
693 const std::string& data, mode_t mode = 0777);
694[[deprecated(
695 "Use write_file_atomically(const std::filesystem::path&, …)")]] void
696write_file_atomically(const std::string& file, const std::string& data,
697 mode_t mode = 0777);
698void write_file_atomically(const char* file, const std::string& data,
699 mode_t mode = 0777);
700
710void write_file_atomically(const std::filesystem::path& file, const void* data,
711 size_t size, mode_t mode = 0777);
712[[deprecated(
713 "Use write_file_atomically(const std::filesystem::path&, …)")]] void
714write_file_atomically(const std::string& file, const void* data, size_t size,
715 mode_t mode = 0777);
716
717#if 0
718// Create a temporary directory based on a template.
719std::string mkdtemp(std::string templ);
720
723void mkFilePath(const std::string& file);
724#endif
725
731[[deprecated("use std::filesystem::remove")]] bool
732unlink_ifexists(const char* file);
733[[deprecated("use std::filesystem::remove")]] bool
734unlink_ifexists(const std::string& file);
735[[deprecated("use std::filesystem::remove")]] bool
736unlink_ifexists(const std::filesystem::path& file);
737
743bool rename_ifexists(const std::filesystem::path& src,
744 const std::filesystem::path& dst);
745
754[[deprecated("use std::filesystem::create_directory")]] bool
755mkdir_ifmissing(const std::filesystem::path& path);
756
763[[deprecated("use std::filesystem::create_directories")]] bool
764makedirs(const std::filesystem::path& path);
765
773std::filesystem::path which(const std::string& name);
774
776void unlink(const std::filesystem::path& pathname);
777
779void rmdir(const std::filesystem::path& pathname);
780
782void rmtree(const std::filesystem::path& pathname);
783
789bool rmtree_ifexists(const std::filesystem::path& pathname);
790[[deprecated("use rmtree_ifexists(const std::filesystem::path&)")]] bool
791rmtree_ifexists(const std::string& pathname);
792bool rmtree_ifexists(const char* pathname);
793
800[[deprecated("use std::filesystem::rename")]] void
801rename(const std::string& src_pathname, const std::string& dst_pathname);
802
808void touch(const std::filesystem::path& pathname, time_t ts);
809
815bool touch_ifexists(const std::filesystem::path& pathname, time_t ts);
816
820void clock_gettime(::clockid_t clk_id, ::timespec& ts);
821
825unsigned long long timesec_elapsed(const ::timespec& begin,
826 const ::timespec& until);
827
831struct Clock
832{
833 ::clockid_t clk_id;
834 ::timespec ts;
835
839 explicit Clock(::clockid_t clk_id);
840
845 unsigned long long elapsed();
846};
847
851
853void getrlimit(int resource, ::rlimit& rlim);
854
856void setrlimit(int resource, const ::rlimit& rlim);
857
859struct OverrideRlimit
860{
861 int resource;
862 ::rlimit orig;
863
864 OverrideRlimit(int resource, rlim_t rlim);
865 ~OverrideRlimit();
866
868 void set(rlim_t rlim);
869};
870
872struct OverrideEnvironment
873{
874 std::string name;
875 bool was_set = false;
876 std::string orig_value;
877
878 // Unset the variable
879 explicit OverrideEnvironment(const std::string& name);
880 // Set the variable to the given value
881 OverrideEnvironment(const std::string& name, const std::string& value);
882 ~OverrideEnvironment();
883};
884
886template <typename T = char> class TempBuffer
887{
888 T* buffer = nullptr;
889
890public:
891 explicit TempBuffer(size_t size) : buffer(new T[size]) {}
892 ~TempBuffer() { delete[] buffer; }
893 TempBuffer(const TempBuffer&) = delete;
894 TempBuffer(TempBuffer&&) = delete;
895 TempBuffer& operator=(const TempBuffer&) = delete;
896 TempBuffer& operator=(TempBuffer&&) = delete;
897
898 T* data() { return buffer; }
899 const T* data() const { return buffer; }
900 operator T*() { return buffer; }
901 operator const T*() const { return buffer; }
902};
903
906
907} // namespace wreport::sys
908
909#endif
Common operations on file descriptors.
Definition sys.h:194
virtual void throw_error_string(const std::string &desc)
Throw an exception based on errno and the given message.
void write_all_or_retry(const void *buf, size_t count)
Write all the data in buf, retrying partial writes.
bool ofd_setlkw(::flock &, bool retry_on_signal=true)
Open file description locks F_OFD_SETLKW operation.
void setfl(int flags)
Set open flags for the file.
bool read_all_or_retry(void *buf, size_t count)
Read count bytes into bufr, retrying partial reads, stopping at EOF.
void write_all_or_throw(const void *buf, size_t count)
Write all the data in buf, throwing runtime_error in case of a partial write.
void close()
Close the file descriptor, setting its value to -1.
void read_all_or_throw(void *buf, size_t count)
Read all the data into buf, throwing runtime_error in case of a partial read.
bool ofd_setlk(::flock &)
Open file description locks F_OFD_SETLK operation.
int getfl()
Get open flags for the file.
bool is_open() const
Check if the file descriptor is open (that is, if it is not -1)
bool ofd_getlk(::flock &)
Open file description locks F_OFD_GETLK operation.
virtual void throw_runtime_error(const char *desc)
Throw a runtime_error unrelated from errno.
virtual void throw_error(const char *desc)
Throw an exception based on errno and the given message.
void open(int flags, mode_t mode=0777)
Wrapper around open(2)
File(const std::filesystem::path &path, int flags, mode_t mode=0777)
Wrapper around open(2)
bool open_ifexists(int flags, mode_t mode=0777)
Wrap open(2) and return false instead of throwing an exception if open fails with ENOENT.
File(const std::filesystem::path &path)
Create an unopened File object for the given pathname.
std::string name() const
Return the file pathname.
Definition sys.h:392
virtual void throw_error_string(const std::string &desc) override
Throw an exception based on errno and the given message.
virtual void throw_error(const char *desc) override
Throw an exception based on errno and the given message.
virtual void throw_runtime_error(const char *desc) override
Throw a runtime_error unrelated from errno.
void rmtree_on_exit(bool val)
Change the rmtree-on-exit behaviour.
void unlink_on_exit(bool val)
Change the unlink-on-exit behaviour.
void unlink()
Unlink the file right now.
Operating system functions.
Definition sys.h:23
std::filesystem::path which(const std::string &name)
Compute the absolute path of an executable.
bool isdir(const std::string &pathname)
Returns true if the given pathname is a directory, else false.
ino_t inode(const std::filesystem::path &file)
File inode number.
bool rename_ifexists(const std::filesystem::path &src, const std::filesystem::path &dst)
Move src to dst, without raising exception if src does not exist.
std::string read_file(const std::filesystem::path &file)
Read whole file into memory. Throws exceptions on failure.
void rmtree(const std::filesystem::path &pathname)
Delete the directory pathname and all its contents.
void touch(const std::filesystem::path &pathname, time_t ts)
Set mtime and atime for the file.
bool exists(const std::string &s)
Same as access(s, F_OK);.
bool unlink_ifexists(const char *file)
Delete a file if it exists.
bool ischr(const std::string &pathname)
Same as isdir but checks for character devices.
std::string getcwd()
Get the absolute path of the current working directory.
void setrlimit(int resource, const ::rlimit &rlim)
Call setrlimit, raising an exception if it fails.
bool access(const std::filesystem::path &s, int m)
access() a filename
void clock_gettime(::clockid_t clk_id, ::timespec &ts)
Call clock_gettime, raising an exception if it fails.
void unlink(const std::filesystem::path &pathname)
Delete the file using unlink()
void chdir(const std::string &dir)
Change working directory.
bool makedirs(const std::filesystem::path &path)
Create all the component of the given directory, including the directory itself.
void rename(const std::string &src_pathname, const std::string &dst_pathname)
Rename src_pathname into dst_pathname.
bool isreg(const std::string &pathname)
Same as isdir but checks for regular files.
void write_file(const std::filesystem::path &file, const std::string &data, mode_t mode=0777)
Write data to file, replacing existing contents if it already exists.
void breakpoint()
Set a breakpoint at this code location.
std::unique_ptr< struct stat > stat(const char *pathname)
stat() the given file and return the struct stat with the results.
bool islnk(const std::string &pathname)
Same as isdir but checks for symbolic links.
void rmdir(const std::filesystem::path &pathname)
Remove the directory using rmdir(2)
void getrlimit(int resource, ::rlimit &rlim)
rlimit wrappers
bool rmtree_ifexists(const std::filesystem::path &pathname)
Delete the directory pathname and all its contents.
void chroot(const std::filesystem::path &dir)
Change root directory.
bool touch_ifexists(const std::filesystem::path &pathname, time_t ts)
Set mtime and atime for the file.
void write_file_atomically(const std::filesystem::path &file, const std::string &data, mode_t mode=0777)
Write data to file, replacing existing contents if it already exists.
std::filesystem::path abspath(const std::filesystem::path &path)
Get the absolute path of a file.
std::filesystem::path with_suffix(const std::filesystem::path &path, const std::string &suffix)
Return the path with suffix appended to its filename.
bool isblk(const std::string &pathname)
Same as isdir but checks for block devices.
mode_t umask(mode_t mask)
Change umask (always succeeds and returns the previous umask)
bool mkdir_ifmissing(const std::filesystem::path &path)
Create the given directory, if it does not already exists.
unsigned long long timesec_elapsed(const ::timespec &begin, const ::timespec &until)
Return the time elapsed between two timesec structures, in nanoseconds.
bool isfifo(const std::string &pathname)
Same as isdir but checks for FIFOs.
bool issock(const std::string &pathname)
Same as isdir but checks for sockets.
time_t timestamp(const std::filesystem::path &file)
File mtime.
size_t size(const std::filesystem::path &file)
File size.
Clock(::clockid_t clk_id)
Initialize ts with the value of the given clock.
unsigned long long elapsed()
Return the number of nanoseconds elapsed since the last time ts was updated.
~ManagedNamedFileDescriptor()
The destructor closes the file descriptor, but does not check errors on close().
void set(rlim_t rlim)
Change the limit value again.
Iterator for directory entries.
Definition sys.h:432
Path open_path(int flags=0) const
Return a Path object for this entry.
Path(const std::filesystem::path &pathname, int flags=0, mode_t mode=0777)
Open the given pathname with flags | O_PATH.
bool lstatat_ifexists(const char *pathname, struct stat &st)
lstatat, but in case of ENOENT returns false instead of throwing
void lstatat(const char *pathname, struct stat &st)
fstatat with the AT_SYMLINK_NOFOLLOW flag set
int openat_ifexists(const char *pathname, int flags, mode_t mode=0777)
Same as openat, but returns -1 if the file does not exist.
void open(int flags, mode_t mode=0777)
Wrapper around open(2) with flags | O_PATH.
iterator begin()
Begin iterator on all directory entries.
bool fstatat_ifexists(const char *pathname, struct stat &st)
fstatat, but in case of ENOENT returns false instead of throwing
void rmdirat(const char *pathname)
unlinkat with the AT_REMOVEDIR flag set
void rmtree()
Delete the directory pointed to by this Path, with all its contents.
iterator end()
End iterator on all directory entries.
Path(Path &parent, const char *pathname, int flags=0, mode_t mode=0777)
Open the given pathname calling parent.openat, with flags | O_PATH.