llvm.org GIT mirror llvm / 167b16d
[Support] Add a function to check if a file resides locally. Differential Revision: https://reviews.llvm.org/D30010 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@295768 91177308-0d34-0410-b5e6-96231b3b80d8 Zachary Turner 3 years ago
6 changed file(s) with 211 addition(s) and 31 deletion(s). Raw diff Collapse all Expand all
463463 return !equivalent(A, B, result) && result;
464464 }
465465
466 /// @brief Is the file mounted on a local filesystem?
467 ///
468 /// @param path Input path.
469 /// @param result Set to true if \a path is on fixed media such as a hard disk,
470 /// false if it is not.
471 /// @returns errc::success if result has been successfully set, otherwise a
472 /// platform specific error_code.
473 std::error_code is_local(const Twine &path, bool &result);
474
475 /// @brief Version of is_local accepting an open file descriptor.
476 std::error_code is_local(int FD, bool &result);
477
478 /// @brief Simpler version of is_local for clients that don't need to
479 /// differentiate between an error and false.
480 inline bool is_local(const Twine &Path) {
481 bool Result;
482 return !is_local(Path, Result) && Result;
483 }
484
485 /// @brief Simpler version of is_local accepting an open file descriptor for
486 /// clients that don't need to differentiate between an error and false.
487 inline bool is_local(int FD) {
488 bool Result;
489 return !is_local(FD, Result) && Result;
490 }
491
466492 /// @brief Does status represent a directory?
467493 ///
468494 /// @param status A file_status previously returned from status.
6868 /// means that the client knows that the file exists and that it has the
6969 /// specified size.
7070 ///
71 /// \param IsVolatileSize Set to true to indicate that the file size may be
72 /// changing, e.g. when libclang tries to parse while the user is
73 /// editing/updating the file.
71 /// \param IsVolatile Set to true to indicate that the contents of the file
72 /// can change outside the user's control, e.g. when libclang tries to parse
73 /// while the user is editing/updating the file or if the file is on an NFS.
7474 static ErrorOr>
7575 getFile(const Twine &Filename, int64_t FileSize = -1,
76 bool RequiresNullTerminator = true, bool IsVolatileSize = false);
76 bool RequiresNullTerminator = true, bool IsVolatile = false);
7777
7878 /// Read all of the specified file into a MemoryBuffer as a stream
7979 /// (i.e. until EOF reached). This is useful for special files that
8686 /// Since this is in the middle of a file, the buffer is not null terminated.
8787 static ErrorOr>
8888 getOpenFileSlice(int FD, const Twine &Filename, uint64_t MapSize,
89 int64_t Offset);
89 int64_t Offset, bool IsVolatile = false);
9090
9191 /// Given an already-open file descriptor, read the file and return a
9292 /// MemoryBuffer.
9393 ///
94 /// \param IsVolatileSize Set to true to indicate that the file size may be
95 /// changing, e.g. when libclang tries to parse while the user is
96 /// editing/updating the file.
94 /// \param IsVolatile Set to true to indicate that the contents of the file
95 /// can change outside the user's control, e.g. when libclang tries to parse
96 /// while the user is editing/updating the file or if the file is on an NFS.
9797 static ErrorOr>
9898 getOpenFile(int FD, const Twine &Filename, uint64_t FileSize,
99 bool RequiresNullTerminator = true, bool IsVolatileSize = false);
99 bool RequiresNullTerminator = true, bool IsVolatile = false);
100100
101101 /// Open the specified memory range as a MemoryBuffer. Note that InputData
102102 /// must be null terminated if RequiresNullTerminator is true.
135135
136136 /// Map a subrange of the specified file as a MemoryBuffer.
137137 static ErrorOr>
138 getFileSlice(const Twine &Filename, uint64_t MapSize, uint64_t Offset);
138 getFileSlice(const Twine &Filename, uint64_t MapSize, uint64_t Offset, bool IsVolatile = false);
139139
140140 //===--------------------------------------------------------------------===//
141141 // Provided for performance analysis.
102102
103103 static ErrorOr>
104104 getFileAux(const Twine &Filename, int64_t FileSize, uint64_t MapSize,
105 uint64_t Offset, bool RequiresNullTerminator, bool IsVolatileSize);
105 uint64_t Offset, bool RequiresNullTerminator, bool IsVolatile);
106106
107107 std::unique_ptr
108108 MemoryBuffer::getMemBuffer(StringRef InputData, StringRef BufferName,
177177
178178 ErrorOr>
179179 MemoryBuffer::getFileSlice(const Twine &FilePath, uint64_t MapSize,
180 uint64_t Offset) {
181 return getFileAux(FilePath, -1, MapSize, Offset, false, false);
180 uint64_t Offset, bool IsVolatile) {
181 return getFileAux(FilePath, -1, MapSize, Offset, false, IsVolatile);
182182 }
183183
184184
253253
254254 ErrorOr>
255255 MemoryBuffer::getFile(const Twine &Filename, int64_t FileSize,
256 bool RequiresNullTerminator, bool IsVolatileSize) {
256 bool RequiresNullTerminator, bool IsVolatile) {
257257 return getFileAux(Filename, FileSize, FileSize, 0,
258 RequiresNullTerminator, IsVolatileSize);
258 RequiresNullTerminator, IsVolatile);
259259 }
260260
261261 static ErrorOr>
262262 getOpenFileImpl(int FD, const Twine &Filename, uint64_t FileSize,
263263 uint64_t MapSize, int64_t Offset, bool RequiresNullTerminator,
264 bool IsVolatileSize);
264 bool IsVolatile);
265265
266266 static ErrorOr>
267267 getFileAux(const Twine &Filename, int64_t FileSize, uint64_t MapSize,
268 uint64_t Offset, bool RequiresNullTerminator, bool IsVolatileSize) {
268 uint64_t Offset, bool RequiresNullTerminator, bool IsVolatile) {
269269 int FD;
270270 std::error_code EC = sys::fs::openFileForRead(Filename, FD);
271271 if (EC)
273273
274274 ErrorOr> Ret =
275275 getOpenFileImpl(FD, Filename, FileSize, MapSize, Offset,
276 RequiresNullTerminator, IsVolatileSize);
276 RequiresNullTerminator, IsVolatile);
277277 close(FD);
278278 return Ret;
279279 }
284284 off_t Offset,
285285 bool RequiresNullTerminator,
286286 int PageSize,
287 bool IsVolatileSize) {
287 bool IsVolatile) {
288288 // mmap may leave the buffer without null terminator if the file size changed
289289 // by the time the last page is mapped in, so avoid it if the file size is
290290 // likely to change.
291 if (IsVolatileSize)
291 if (IsVolatile)
292292 return false;
293293
294294 // We don't use mmap for small files because this can severely fragment our
298298
299299 if (!RequiresNullTerminator)
300300 return true;
301
302301
303302 // If we don't know the file size, use fstat to find out. fstat on an open
304303 // file descriptor is cheaper than stat on a random path.
337336 static ErrorOr>
338337 getOpenFileImpl(int FD, const Twine &Filename, uint64_t FileSize,
339338 uint64_t MapSize, int64_t Offset, bool RequiresNullTerminator,
340 bool IsVolatileSize) {
339 bool IsVolatile) {
341340 static int PageSize = sys::Process::getPageSize();
342341
343342 // Default is to map the full file.
364363 }
365364
366365 if (shouldUseMmap(FD, FileSize, MapSize, Offset, RequiresNullTerminator,
367 PageSize, IsVolatileSize)) {
366 PageSize, IsVolatile)) {
368367 std::error_code EC;
369368 std::unique_ptr Result(
370369 new (NamedBufferAlloc(Filename))
414413
415414 ErrorOr>
416415 MemoryBuffer::getOpenFile(int FD, const Twine &Filename, uint64_t FileSize,
417 bool RequiresNullTerminator, bool IsVolatileSize) {
416 bool RequiresNullTerminator, bool IsVolatile) {
418417 return getOpenFileImpl(FD, Filename, FileSize, FileSize, 0,
419 RequiresNullTerminator, IsVolatileSize);
418 RequiresNullTerminator, IsVolatile);
420419 }
421420
422421 ErrorOr>
423422 MemoryBuffer::getOpenFileSlice(int FD, const Twine &Filename, uint64_t MapSize,
424 int64_t Offset) {
423 int64_t Offset, bool IsVolatile) {
425424 assert(MapSize != uint64_t(-1));
426 return getOpenFileImpl(FD, Filename, -1, MapSize, Offset, false,
427 /*IsVolatileSize*/ false);
425 return getOpenFileImpl(FD, Filename, -1, MapSize, Offset, false, IsVolatile);
428426 }
429427
430428 ErrorOr> MemoryBuffer::getSTDIN() {
6464 #endif
6565
6666 #include
67 #if !defined(__APPLE__) && !defined(__OpenBSD__) && !defined(__ANDROID__)
67 #if !defined(__APPLE__) && !defined(__OpenBSD__) && !defined(__FreeBSD__) && \
68 !defined(__linux__)
6869 #include
6970 #define STATVFS statvfs
71 #define FSTATVFS fstatvfs
7072 #define STATVFS_F_FRSIZE(vfs) vfs.f_frsize
7173 #else
72 #ifdef __OpenBSD__
74 #if defined(__OpenBSD__) || defined(__FreeBSD__)
7375 #include
7476 #include
75 #elif defined(__ANDROID__)
77 #elif defined(__linux__)
78 #include
7679 #include
7780 #else
7881 #include
7982 #endif
8083 #define STATVFS statfs
84 #define FSTATVFS fstatfs
8185 #define STATVFS_F_FRSIZE(vfs) static_cast(vfs.f_bsize)
8286 #endif
8387
88 #if defined(__NetBSD__)
89 #define STATVFS_F_FLAG(vfs) (vfs).f_flag
90 #else
91 #define STATVFS_F_FLAG(vfs) (vfs).f_flags
92 #endif
8493
8594 using namespace llvm;
8695
334343 return std::error_code();
335344 }
336345
346 static bool is_local_impl(struct STATVFS &Vfs) {
347 #if defined(__linux__)
348 constexpr uint32_t CIFS_MAGIC_NUMBER = 0xFF534D42;
349 switch ((uint32_t)Vfs.f_type) {
350 case NFS_SUPER_MAGIC:
351 case SMB_SUPER_MAGIC:
352 case CIFS_MAGIC_NUMBER:
353 return false;
354 default:
355 return true;
356 }
357 #else
358 return !!(STATVFS_F_FLAG(Vfs) & MNT_LOCAL);
359 #endif
360 }
361
362 std::error_code is_local(const Twine &Path, bool &Result) {
363 struct STATVFS Vfs;
364 if (::STATVFS(Path.str().c_str(), &Vfs))
365 return std::error_code(errno, std::generic_category());
366
367 Result = is_local_impl(Vfs);
368 return std::error_code();
369 }
370
371 std::error_code is_local(int FD, bool &Result) {
372 struct STATVFS Vfs;
373 if (::FSTATVFS(FD, &Vfs))
374 return std::error_code(errno, std::generic_category());
375
376 Result = is_local_impl(Vfs);
377 return std::error_code();
378 }
379
337380 std::error_code rename(const Twine &from, const Twine &to) {
338381 // Get arguments.
339382 SmallString<128> from_storage;
490533
491534 int flags = (Mode == readwrite) ? MAP_SHARED : MAP_PRIVATE;
492535 int prot = (Mode == readonly) ? PROT_READ : (PROT_READ | PROT_WRITE);
536 #if defined(__APPLE__)
537 //----------------------------------------------------------------------
538 // Newer versions of MacOSX have a flag that will allow us to read from
539 // binaries whose code signature is invalid without crashing by using
540 // the MAP_RESILIENT_CODESIGN flag. Also if a file from removable media
541 // is mapped we can avoid crashing and return zeroes to any pages we try
542 // to read if the media becomes unavailable by using the
543 // MAP_RESILIENT_MEDIA flag.
544 //----------------------------------------------------------------------
545 #if defined(MAP_RESILIENT_CODESIGN)
546 flags |= MAP_RESILIENT_CODESIGN;
547 #endif
548 #if defined(MAP_RESILIENT_MEDIA)
549 flags |= MAP_RESILIENT_MEDIA;
550 #endif
551 #endif // #if defined (__APPLE__)
552
493553 Mapping = ::mmap(nullptr, Size, prot, flags, FD, Offset);
494554 if (Mapping == MAP_FAILED)
495555 return std::error_code(errno, std::generic_category());
276276 return std::error_code();
277277 }
278278
279 static std::error_code is_local_internal(SmallVectorImpl &Path,
280 bool &Result) {
281 SmallVector VolumePath;
282 size_t Len = 128;
283 while (true) {
284 VolumePath.resize(Len);
285 BOOL Success =
286 ::GetVolumePathNameW(Path.data(), VolumePath.data(), VolumePath.size());
287
288 if (Success)
289 break;
290
291 DWORD Err = ::GetLastError();
292 if (Err != ERROR_INSUFFICIENT_BUFFER)
293 return mapWindowsError(Err);
294
295 Len *= 2;
296 }
297 // If the output buffer has exactly enough space for the path name, but not
298 // the null terminator, it will leave the output unterminated. Push a null
299 // terminator onto the end to ensure that this never happens.
300 VolumePath.push_back(L'\0');
301 VolumePath.set_size(wcslen(VolumePath.data()));
302 const wchar_t *P = VolumePath.data();
303
304 UINT Type = ::GetDriveTypeW(P);
305 switch (Type) {
306 case DRIVE_FIXED:
307 Result = true;
308 return std::error_code();
309 case DRIVE_REMOTE:
310 case DRIVE_CDROM:
311 case DRIVE_RAMDISK:
312 case DRIVE_REMOVABLE:
313 Result = false;
314 return std::error_code();
315 default:
316 return make_error_code(errc::no_such_file_or_directory);
317 }
318 llvm_unreachable("Unreachable!");
319 }
320
321 std::error_code is_local(const Twine &path, bool &result) {
322 if (!llvm::sys::fs::exists(path) || !llvm::sys::path::has_root_path(path))
323 return make_error_code(errc::no_such_file_or_directory);
324
325 SmallString<128> Storage;
326 StringRef P = path.toStringRef(Storage);
327
328 // Convert to utf-16.
329 SmallVector WidePath;
330 if (std::error_code ec = widenPath(P, WidePath))
331 return ec;
332 return is_local_internal(WidePath, result);
333 }
334
335 std::error_code is_local(int FD, bool &Result) {
336 SmallVector FinalPath;
337 HANDLE Handle = reinterpret_cast(_get_osfhandle(FD));
338
339 size_t Len = 128;
340 do {
341 FinalPath.reserve(Len);
342 Len = ::GetFinalPathNameByHandleW(Handle, FinalPath.data(),
343 FinalPath.capacity() - 1, VOLUME_NAME_NT);
344 if (Len == 0)
345 return mapWindowsError(::GetLastError());
346 } while (Len > FinalPath.capacity());
347
348 FinalPath.set_size(Len);
349
350 return is_local_internal(FinalPath, Result);
351 }
352
279353 std::error_code rename(const Twine &from, const Twine &to) {
280354 // Convert to utf-16.
281355 SmallVector wide_from;
11351135 ::close(FileDescriptor);
11361136 }
11371137
1138 TEST_F(FileSystemTest, is_local) {
1139 SmallString<128> CurrentPath;
1140 ASSERT_NO_ERROR(fs::current_path(CurrentPath));
1141
1142 bool Result;
1143 ASSERT_NO_ERROR(fs::is_local(CurrentPath, Result));
1144 EXPECT_TRUE(Result);
1145 EXPECT_TRUE(fs::is_local(CurrentPath));
1146
1147 int FD;
1148 SmallString<64> TempPath;
1149 ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FD, TempPath));
1150 FileRemover Cleanup(TempPath);
1151
1152 // Make sure it exists.
1153 ASSERT_TRUE(sys::fs::exists(Twine(TempPath)));
1154
1155 ASSERT_NO_ERROR(fs::is_local(FD, Result));
1156 EXPECT_TRUE(Result);
1157 EXPECT_TRUE(fs::is_local(FD));
1158 }
1159
11381160 TEST_F(FileSystemTest, set_current_path) {
11391161 SmallString<128> path;
11401162