1 //===----------------------------------------------------------------------===////
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===////
9 #ifndef FILESYSTEM_FILE_DESCRIPTOR_H
10 #define FILESYSTEM_FILE_DESCRIPTOR_H
15 #include <string_view>
16 #include <system_error>
20 #include "posix_compat.h"
21 #include "time_utils.h"
23 #if defined(_LIBCPP_WIN32API)
24 # define WIN32_LEAN_AND_MEAN
28 # include <dirent.h> // for DIR & friends
29 # include <fcntl.h> // values for fchmodat
30 # include <sys/stat.h>
31 # include <sys/statvfs.h>
33 #endif // defined(_LIBCPP_WIN32API)
35 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
39 #if !defined(_LIBCPP_WIN32API)
42 template <class DirEntT
, class = decltype(DirEntT::d_type
)>
43 file_type
get_file_type(DirEntT
* ent
, int) {
44 switch (ent
->d_type
) {
46 return file_type::block
;
48 return file_type::character
;
50 return file_type::directory
;
52 return file_type::fifo
;
54 return file_type::symlink
;
56 return file_type::regular
;
58 return file_type::socket
;
59 // Unlike in lstat, hitting "unknown" here simply means that the underlying
60 // filesystem doesn't support d_type. Report is as 'none' so we correctly
61 // set the cache to empty.
65 return file_type::none
;
67 #endif // defined(DT_BLK)
69 template <class DirEntT
>
70 file_type
get_file_type(DirEntT
*, long) {
71 return file_type::none
;
74 inline pair
<string_view
, file_type
> posix_readdir(DIR* dir_stream
,
76 struct dirent
* dir_entry_ptr
= nullptr;
77 errno
= 0; // zero errno in order to detect errors
79 if ((dir_entry_ptr
= ::readdir(dir_stream
)) == nullptr) {
84 return {dir_entry_ptr
->d_name
, get_file_type(dir_entry_ptr
, 0)};
88 #else // _LIBCPP_WIN32API
90 inline file_type
get_file_type(const WIN32_FIND_DATAW
& data
) {
91 if (data
.dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
&&
92 data
.dwReserved0
== IO_REPARSE_TAG_SYMLINK
)
93 return file_type::symlink
;
94 if (data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
95 return file_type::directory
;
96 return file_type::regular
;
98 inline uintmax_t get_file_size(const WIN32_FIND_DATAW
& data
) {
99 return (static_cast<uint64_t>(data
.nFileSizeHigh
) << 32) + data
.nFileSizeLow
;
101 inline file_time_type
get_write_time(const WIN32_FIND_DATAW
& data
) {
103 const FILETIME
& time
= data
.ftLastWriteTime
;
104 tmp
.u
.LowPart
= time
.dwLowDateTime
;
105 tmp
.u
.HighPart
= time
.dwHighDateTime
;
106 return file_time_type(file_time_type::duration(tmp
.QuadPart
));
109 #endif // !_LIBCPP_WIN32API
113 using value_type
= path::value_type
;
114 using string_type
= path::string_type
;
116 struct FileDescriptor
{
120 file_status m_status
;
122 template <class... Args
>
123 static FileDescriptor
create(const path
* p
, error_code
& ec
, Args
... args
) {
126 #ifdef _LIBCPP_WIN32API
127 // TODO: most of the filesystem implementation uses native Win32 calls
128 // (mostly via posix_compat.h). However, here we use the C-runtime APIs to
129 // open a file, because we subsequently pass the C-runtime fd to
130 // `std::[io]fstream::__open(int fd)` in order to implement copy_file.
132 // Because we're calling the windows C-runtime, win32 error codes are
133 // translated into C error numbers by the C runtime, and returned in errno,
134 // rather than being accessible directly via GetLastError.
136 // Ideally copy_file should be calling the Win32 CopyFile2 function, which
137 // works on paths, not open files -- at which point this FileDescriptor type
138 // will no longer be needed on windows at all.
139 fd
= ::_wopen(p
->c_str(), args
...);
141 fd
= open(p
->c_str(), args
...);
145 ec
= capture_errno();
146 return FileDescriptor
{p
};
148 return FileDescriptor(p
, fd
);
151 template <class... Args
>
152 static FileDescriptor
create_with_status(const path
* p
, error_code
& ec
,
154 FileDescriptor fd
= create(p
, ec
, args
...);
156 fd
.refresh_status(ec
);
161 file_status
get_status() const { return m_status
; }
162 StatT
const& get_stat() const { return m_stat
; }
164 bool status_known() const { return _VSTD_FS::status_known(m_status
); }
166 file_status
refresh_status(error_code
& ec
);
168 void close() noexcept
{
170 #ifdef _LIBCPP_WIN32API
175 // FIXME: shouldn't this return an error_code?
180 FileDescriptor(FileDescriptor
&& other
)
181 : name(other
.name
), fd(other
.fd
), m_stat(other
.m_stat
),
182 m_status(other
.m_status
) {
184 other
.m_status
= file_status
{};
187 ~FileDescriptor() { close(); }
189 FileDescriptor(FileDescriptor
const&) = delete;
190 FileDescriptor
& operator=(FileDescriptor
const&) = delete;
193 explicit FileDescriptor(const path
* p
, int descriptor
= -1) : name(*p
), fd(descriptor
) {}
196 inline perms
posix_get_perms(const StatT
& st
) noexcept
{
197 return static_cast<perms
>(st
.st_mode
) & perms::mask
;
200 inline file_status
create_file_status(error_code
& m_ec
, path
const& p
,
201 const StatT
& path_stat
, error_code
* ec
) {
204 if (m_ec
&& (m_ec
.value() == ENOENT
|| m_ec
.value() == ENOTDIR
)) {
205 return file_status(file_type::not_found
);
207 ErrorHandler
<void> err("posix_stat", ec
, &p
);
208 err
.report(m_ec
, "failed to determine attributes for the specified path");
209 return file_status(file_type::none
);
214 auto const mode
= path_stat
.st_mode
;
216 fs_tmp
.type(file_type::symlink
);
217 else if (S_ISREG(mode
))
218 fs_tmp
.type(file_type::regular
);
219 else if (S_ISDIR(mode
))
220 fs_tmp
.type(file_type::directory
);
221 else if (S_ISBLK(mode
))
222 fs_tmp
.type(file_type::block
);
223 else if (S_ISCHR(mode
))
224 fs_tmp
.type(file_type::character
);
225 else if (S_ISFIFO(mode
))
226 fs_tmp
.type(file_type::fifo
);
227 else if (S_ISSOCK(mode
))
228 fs_tmp
.type(file_type::socket
);
230 fs_tmp
.type(file_type::unknown
);
232 fs_tmp
.permissions(detail::posix_get_perms(path_stat
));
236 inline file_status
posix_stat(path
const& p
, StatT
& path_stat
, error_code
* ec
) {
238 if (detail::stat(p
.c_str(), &path_stat
) == -1)
239 m_ec
= detail::capture_errno();
240 return create_file_status(m_ec
, p
, path_stat
, ec
);
243 inline file_status
posix_stat(path
const& p
, error_code
* ec
) {
245 return posix_stat(p
, path_stat
, ec
);
248 inline file_status
posix_lstat(path
const& p
, StatT
& path_stat
, error_code
* ec
) {
250 if (detail::lstat(p
.c_str(), &path_stat
) == -1)
251 m_ec
= detail::capture_errno();
252 return create_file_status(m_ec
, p
, path_stat
, ec
);
255 inline file_status
posix_lstat(path
const& p
, error_code
* ec
) {
257 return posix_lstat(p
, path_stat
, ec
);
260 // http://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html
261 inline bool posix_ftruncate(const FileDescriptor
& fd
, off_t to_size
, error_code
& ec
) {
262 if (detail::ftruncate(fd
.fd
, to_size
) == -1) {
263 ec
= capture_errno();
270 inline bool posix_fchmod(const FileDescriptor
& fd
, const StatT
& st
, error_code
& ec
) {
271 if (detail::fchmod(fd
.fd
, st
.st_mode
) == -1) {
272 ec
= capture_errno();
279 inline bool stat_equivalent(const StatT
& st1
, const StatT
& st2
) {
280 return (st1
.st_dev
== st2
.st_dev
&& st1
.st_ino
== st2
.st_ino
);
283 inline file_status
FileDescriptor::refresh_status(error_code
& ec
) {
284 // FD must be open and good.
285 m_status
= file_status
{};
288 if (detail::fstat(fd
, &m_stat
) == -1)
289 m_ec
= capture_errno();
290 m_status
= create_file_status(m_ec
, name
, m_stat
, &ec
);
294 } // end namespace detail
296 _LIBCPP_END_NAMESPACE_FILESYSTEM
298 #endif // FILESYSTEM_FILE_DESCRIPTOR_H