llvm.org GIT mirror llvm / 2ae9d11
For PR1291: Implement the PathWithStatus class and its use throughout lib/System. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@35742 91177308-0d34-0410-b5e6-96231b3b80d8 Reid Spencer 13 years ago
5 changed file(s) with 161 addition(s) and 93 deletion(s). Raw diff Collapse all Expand all
151151 /// @name Data
152152 /// @{
153153 private:
154 sys::Path path_; ///< Path to the file.
154 sys::PathWithStatus path_; ///< Path to the file.
155155 int options_; ///< Options used to create the mapping
156156 void* base_; ///< Pointer to the base memory address
157157 mutable MappedFileInfo* info_; ///< Platform specific info for the mapping
2828 /// platform independent and eliminates many of the unix-specific fields.
2929 /// However, to support llvm-ar, the mode, user, and group fields are
3030 /// retained. These pertain to unix security and may not have a meaningful
31 /// value on non-Unix platforms. However, the fileSize and modTime fields
32 /// should always be applicable on all platforms. The structure is
33 /// filled in by the Path::getFileStatus method.
31 /// value on non-Unix platforms. However, the other fields fields should
32 /// always be applicable on all platforms. The structure is filled in by
33 /// the PathWithStatus class.
3434 /// @brief File status structure
3535 class FileStatus {
3636 public:
163163 /// provided so that they can be used to indicate null or error results in
164164 /// other lib/System functionality.
165165 /// @brief Construct an empty (and invalid) path.
166 Path() : path(), status(0) {}
167 ~Path() { delete status; }
168 Path(const Path &that) : path(that.path), status(0) {}
166 Path() : path() {}
167 Path(const Path &that) : path(that.path) {}
169168
170169 /// This constructor will accept a std::string as a path. No checking is
171170 /// done on this path to determine if it is valid. To determine validity
172171 /// of the path, use the isValid method.
173172 /// @param p The path to assign.
174173 /// @brief Construct a Path from a string.
175 explicit Path(const std::string& p) : path(p), status(0) {}
174 explicit Path(const std::string& p) : path(p) {}
176175
177176 /// @}
178177 /// @name Operators
183182 /// @brief Assignment Operator
184183 Path &operator=(const Path &that) {
185184 path = that.path;
186 if (status)
187 delete status;
188 status = 0;
189185 return *this;
190186 }
191187
229225 /// This function determines if the contents of the path name are empty.
230226 /// That is, the path name has a zero length. This does NOT determine if
231227 /// if the file is empty. To get the length of the file itself, Use the
232 /// getFileStatus() method and then the getSize() on the returned
233 /// FileStatus object
228 /// PathWithStatus::getFileStatus() method and then the getSize() method
229 /// on the returned FileStatus object.
234230 /// @returns true iff the path is empty.
235231 /// @brief Determines if the path name is empty (invalid).
236232 bool isEmpty() const { return path.empty(); }
358354 bool getDirectoryContents(
359355 std::set &paths, ///< The resulting list of file & directory names
360356 std::string* ErrMsg ///< Optional place to return an error message.
361 ) const;
362
363 /// This function returns status information about the file. The type of
364 /// path (file or directory) is updated to reflect the actual contents
365 /// of the file system.
366 /// @returns 0 on failure, with Error explaining why (if non-zero)
367 /// @returns a pointer to a FileStatus structure on success.
368 /// @brief Get file status.
369 const FileStatus *getFileStatus(
370 bool forceUpdate = false, ///< Force an update from the file system
371 std::string *Error = 0 ///< Optional place to return an error msg.
372357 ) const;
373358
374359 /// @}
526511 /// @}
527512 /// @name Data
528513 /// @{
514 protected:
515 mutable std::string path; ///< Storage for the path name.
516
517 /// @}
518 };
519
520 /// This class is identical to Path class except it allows you to obtain the
521 /// file status of the Path as well. The reason for the distinction is one of
522 /// efficiency. First, the file status requires additional space and the space
523 /// is incorporated directly into PathWithStatus without an additional malloc.
524 /// Second, obtaining status information is an expensive operation on most
525 /// operating systems so we want to be careful and explicity about where we
526 /// allow this operation in LLVM.
527 /// @brief Path with file status class.
528 class PathWithStatus : public Path {
529 /// @name Constructors
530 /// @{
531 public:
532 /// @brief Default constructor
533 PathWithStatus() : Path(), status(), fsIsValid(false) {}
534
535 /// @brief Copy constructor
536 PathWithStatus(const PathWithStatus &that)
537 : Path(static_cast(that)), status(that.status),
538 fsIsValid(that.fsIsValid) {}
539
540 /// This constructor allows construction from a Path object
541 /// @brief Path constructor
542 PathWithStatus(const Path &other)
543 : Path(other), status(), fsIsValid(false) {}
544
545 /// This constructor will accept a std::string as a path. No checking is
546 /// done on this path to determine if it is valid. To determine validity
547 /// of the path, use the isValid method.
548 /// @param p The path to assign.
549 /// @brief Construct a Path from a string.
550 explicit PathWithStatus(const std::string& p)
551 : Path(p), status(), fsIsValid(false) {}
552
553 /// Makes a copy of \p that to \p this.
554 /// @returns \p this
555 /// @brief Assignment Operator
556 PathWithStatus &operator=(const PathWithStatus &that) {
557 static_cast(*this) = static_cast(that);
558 status = that.status;
559 fsIsValid = that.fsIsValid;
560 return *this;
561 }
562
563 /// Makes a copy of \p that to \p this.
564 /// @returns \p this
565 /// @brief Assignment Operator
566 PathWithStatus &operator=(const Path &that) {
567 static_cast(*this) = static_cast(that);
568 fsIsValid = false;
569 return *this;
570 }
571
572 /// @}
573 /// @name Methods
574 /// @{
575 public:
576 /// This function returns status information about the file. The type of
577 /// path (file or directory) is updated to reflect the actual contents
578 /// of the file system.
579 /// @returns 0 on failure, with Error explaining why (if non-zero)
580 /// @returns a pointer to a FileStatus structure on success.
581 /// @brief Get file status.
582 const FileStatus *getFileStatus(
583 bool forceUpdate = false, ///< Force an update from the file system
584 std::string *Error = 0 ///< Optional place to return an error msg.
585 ) const;
586
587 /// @}
588 /// @name Data
589 /// @{
529590 private:
530 mutable std::string path; ///< Storage for the path name.
531 mutable FileStatus *status; ///< Status information.
591 mutable FileStatus status; ///< Status information.
592 mutable bool fsIsValid; ///< Whether we've obtained it or not
532593
533594 /// @}
534595 };
555616 bool CopyFile(const Path& Dest, const Path& Src, std::string* ErrMsg);
556617 }
557618
619
558620 std::ostream& operator<<(std::ostream& strm, const sys::Path& aPath);
621 std::ostream& operator<<(std::ostream& strm, const sys::PathWithStatus& aPath);
559622
560623 }
561624
332332 Path::canExecute() const {
333333 if (0 != access(path.c_str(), R_OK | X_OK ))
334334 return false;
335 if (const FileStatus *fs = getFileStatus(true, 0)) {
336 if (!S_ISREG(fs->mode))
337 return false;
338 } else
335 struct stat buf;
336 if (0 != stat(path.c_str(), &buf))
337 return false;
338 if (!S_ISREG(buf.st_mode))
339339 return false;
340340 return true;
341341 }
362362 return path.substr(pos+1);
363363 }
364364
365 const FileStatus*
366 Path::getFileStatus(bool update, std::string *ErrStr) const {
367 if (status == 0 || update) {
365 const FileStatus *
366 PathWithStatus::getFileStatus(bool update, std::string *ErrStr) const {
367 if (!fsIsValid || update) {
368368 struct stat buf;
369369 if (0 != stat(path.c_str(), &buf)) {
370370 MakeErrMsg(ErrStr, path + ": can't get status of file");
371371 return 0;
372372 }
373 if (status == 0)
374 status = new FileStatus;
375 status->fileSize = buf.st_size;
376 status->modTime.fromEpochTime(buf.st_mtime);
377 status->mode = buf.st_mode;
378 status->user = buf.st_uid;
379 status->group = buf.st_gid;
380 status->uniqueID = uint64_t(buf.st_ino);
381 status->isDir = S_ISDIR(buf.st_mode);
382 status->isFile = S_ISREG(buf.st_mode);
383 }
384 return status;
373 status.fileSize = buf.st_size;
374 status.modTime.fromEpochTime(buf.st_mtime);
375 status.mode = buf.st_mode;
376 status.user = buf.st_uid;
377 status.group = buf.st_gid;
378 status.uniqueID = uint64_t(buf.st_ino);
379 status.isDir = S_ISDIR(buf.st_mode);
380 status.isFile = S_ISREG(buf.st_mode);
381 fsIsValid = true;
382 }
383 return &status;
385384 }
386385
387386 static bool AddPermissionBits(const Path &File, int bits) {
393392 umask(mask); // Restore the umask.
394393
395394 // Get the file's current mode.
396 if (const FileStatus *fs = File.getFileStatus()) {
397 // Change the file to have whichever permissions bits from 'bits'
398 // that the umask would not disable.
399 if ((chmod(File.c_str(), (fs->getMode() | (bits & ~mask)))) == -1)
395 struct stat buf;
396 if (0 != stat(File.toString().c_str(), &buf))
397 return false;
398 // Change the file to have whichever permissions bits from 'bits'
399 // that the umask would not disable.
400 if ((chmod(File.c_str(), (buf.st_mode | (bits & ~mask)))) == -1)
400401 return false;
401 } else
402 return false;
403
404402 return true;
405403 }
406404
593591
594592 bool
595593 Path::eraseFromDisk(bool remove_contents, std::string *ErrStr) const {
596 FileStatus Status;
597 if (const FileStatus *Status = getFileStatus(false, ErrStr)) {
598 // Note: this check catches strange situations. In all cases, LLVM should
599 // only be involved in the creation and deletion of regular files. This
600 // check ensures that what we're trying to erase is a regular file. It
601 // effectively prevents LLVM from erasing things like /dev/null, any block
602 // special file, or other things that aren't "regular" files.
603 if (Status->isFile) {
604 if (unlink(path.c_str()) != 0)
605 return MakeErrMsg(ErrStr, path + ": can't destroy file");
606 return false;
607 }
608
609 if (!Status->isDir) {
610 if (ErrStr) *ErrStr = "not a file or directory";
611 return true;
612 }
613 } else
594 // Get the status so we can determin if its a file or directory
595 struct stat buf;
596 if (0 != stat(path.c_str(), &buf)) {
597 MakeErrMsg(ErrStr, path + ": can't get status of file");
614598 return true;
599 }
600
601 // Note: this check catches strange situations. In all cases, LLVM should
602 // only be involved in the creation and deletion of regular files. This
603 // check ensures that what we're trying to erase is a regular file. It
604 // effectively prevents LLVM from erasing things like /dev/null, any block
605 // special file, or other things that aren't "regular" files.
606 if (S_ISREG(buf.st_mode)) {
607 if (unlink(path.c_str()) != 0)
608 return MakeErrMsg(ErrStr, path + ": can't destroy file");
609 return false;
610 }
611
612 if (!S_ISDIR(buf.st_mode)) {
613 if (ErrStr) *ErrStr = "not a file or directory";
614 return true;
615 }
615616
616617 if (remove_contents) {
617618 // Recursively descend the directory to remove its contents.
643644 return false;
644645 }
645646
646 bool
647 bool
647648 Path::setStatusInfoOnDisk(const FileStatus &si, std::string *ErrStr) const {
648649 struct utimbuf utb;
649650 utb.actime = si.modTime.toPosixTime();
1919 #endif
2020 #if HAVE_SIGNAL_H
2121 #include
22 #endif
23 #if HAVE_SYS_STAT_H
24 #include
2225 #endif
2326 using namespace llvm;
2427
167170 // RemoveDirectoryOnSignal - The public API
168171 bool sys::RemoveDirectoryOnSignal(const sys::Path& path, std::string* ErrMsg) {
169172 // Not a directory?
170 const sys::FileStatus *Status = path.getFileStatus(false, ErrMsg);
171 if (!Status)
173 struct stat buf;
174 if (0 != stat(path.c_str(), &buf)) {
175 MakeErrMsg(ErrMsg, path.toString() + ": can't get status of file");
172176 return true;
173 if (!Status->isDir) {
177 }
178
179 if (!S_ISDIR(buf.st_mode)) {
174180 if (ErrMsg)
175181 *ErrMsg = path.toString() + " is not a directory";
176182 return true;
306306 }
307307
308308 const FileStatus *
309 Path::getFileStatus(bool update, std::string *ErrStr) const {
310 if (status == 0 || update) {
309 PathWithStatus::getFileStatus(bool update, std::string *ErrStr) const {
310 if (!fsIsValid || update) {
311311 WIN32_FILE_ATTRIBUTE_DATA fi;
312312 if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi)) {
313313 MakeErrMsg(ErrStr, "getStatusInfo():" + std::string(path) +
315315 return 0;
316316 }
317317
318 if (status == 0)
319 status = new FileStatus;
320
321 status->fileSize = fi.nFileSizeHigh;
322 status->fileSize <<= sizeof(fi.nFileSizeHigh)*8;
323 status->fileSize += fi.nFileSizeLow;
324
325 status->mode = fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY ? 0555 : 0777;
326 status->user = 9999; // Not applicable to Windows, so...
327 status->group = 9999; // Not applicable to Windows, so...
318 status.fileSize = fi.nFileSizeHigh;
319 status.fileSize <<= sizeof(fi.nFileSizeHigh)*8;
320 status.fileSize += fi.nFileSizeLow;
321
322 status.mode = fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY ? 0555 : 0777;
323 status.user = 9999; // Not applicable to Windows, so...
324 status.group = 9999; // Not applicable to Windows, so...
328325
329326 // FIXME: this is only unique if the file is accessed by the same file path.
330327 // How do we do this for C:\dir\file and ..\dir\file ? Unix has inode
331328 // numbers, but the concept doesn't exist in Windows.
332 status->uniqueID = 0;
329 status.uniqueID = 0;
333330 for (unsigned i = 0; i < path.length(); ++i)
334 status->uniqueID += path[i];
331 status.uniqueID += path[i];
335332
336333 __int64 ft = *reinterpret_cast<__int64*>(&fi.ftLastWriteTime);
337 status->modTime.fromWin32Time(ft);
338
339 status->isDir = fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
340 }
341 return status;
334 status.modTime.fromWin32Time(ft);
335
336 status.isDir = fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
337 fsIsValid = true;
338 }
339 return &status;
342340 }
343341
344342 bool Path::makeReadableOnDisk(std::string* ErrMsg) {