1 //===-- FileSystem.cpp ------------------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "lldb/Host/FileSystem.h" 11 12 // C includes 13 #include <dirent.h> 14 #include <sys/mount.h> 15 #include <sys/param.h> 16 #include <sys/stat.h> 17 #include <sys/types.h> 18 #ifdef __linux__ 19 #include <linux/magic.h> 20 #include <sys/mount.h> 21 #include <sys/statfs.h> 22 #endif 23 #if defined(__NetBSD__) 24 #include <sys/statvfs.h> 25 #endif 26 27 // lldb Includes 28 #include "lldb/Core/Error.h" 29 #include "lldb/Core/StreamString.h" 30 #include "lldb/Host/Host.h" 31 32 using namespace lldb; 33 using namespace lldb_private; 34 35 const char *FileSystem::DEV_NULL = "/dev/null"; 36 37 FileSpec::PathSyntax FileSystem::GetNativePathSyntax() { 38 return FileSpec::ePathSyntaxPosix; 39 } 40 41 Error FileSystem::MakeDirectory(const FileSpec &file_spec, 42 uint32_t file_permissions) { 43 if (file_spec) { 44 Error error; 45 if (::mkdir(file_spec.GetCString(), file_permissions) == -1) { 46 error.SetErrorToErrno(); 47 errno = 0; 48 switch (error.GetError()) { 49 case ENOENT: { 50 // Parent directory doesn't exist, so lets make it if we can 51 // Make the parent directory and try again 52 FileSpec parent_file_spec{file_spec.GetDirectory().GetCString(), false}; 53 error = MakeDirectory(parent_file_spec, file_permissions); 54 if (error.Fail()) 55 return error; 56 // Try and make the directory again now that the parent directory was 57 // made successfully 58 if (::mkdir(file_spec.GetCString(), file_permissions) == -1) { 59 error.SetErrorToErrno(); 60 } 61 return error; 62 } break; 63 case EEXIST: { 64 if (file_spec.IsDirectory()) 65 return Error(); // It is a directory and it already exists 66 } break; 67 } 68 } 69 return error; 70 } 71 return Error("empty path"); 72 } 73 74 Error FileSystem::DeleteDirectory(const FileSpec &file_spec, bool recurse) { 75 Error error; 76 if (file_spec) { 77 if (recurse) { 78 // Save all sub directories in a list so we don't recursively call this 79 // function 80 // and possibly run out of file descriptors if the directory is too deep. 81 std::vector<FileSpec> sub_directories; 82 83 FileSpec::ForEachItemInDirectory( 84 file_spec.GetCString(), 85 [&error, &sub_directories]( 86 FileSpec::FileType file_type, 87 const FileSpec &spec) -> FileSpec::EnumerateDirectoryResult { 88 if (file_type == FileSpec::eFileTypeDirectory) { 89 // Save all directorires and process them after iterating through 90 // this directory 91 sub_directories.push_back(spec); 92 } else { 93 // Update sub_spec to point to the current file and delete it 94 error = FileSystem::Unlink(spec); 95 } 96 // If anything went wrong, stop iterating, else process the next 97 // file 98 if (error.Fail()) 99 return FileSpec::eEnumerateDirectoryResultQuit; 100 else 101 return FileSpec::eEnumerateDirectoryResultNext; 102 }); 103 104 if (error.Success()) { 105 // Now delete all sub directories with separate calls that aren't 106 // recursively calling into this function _while_ this function is 107 // iterating through the current directory. 108 for (const auto &sub_directory : sub_directories) { 109 error = DeleteDirectory(sub_directory, recurse); 110 if (error.Fail()) 111 break; 112 } 113 } 114 } 115 116 if (error.Success()) { 117 if (::rmdir(file_spec.GetCString()) != 0) 118 error.SetErrorToErrno(); 119 } 120 } else { 121 error.SetErrorString("empty path"); 122 } 123 return error; 124 } 125 126 Error FileSystem::GetFilePermissions(const FileSpec &file_spec, 127 uint32_t &file_permissions) { 128 Error error; 129 struct stat file_stats; 130 if (::stat(file_spec.GetCString(), &file_stats) == 0) { 131 // The bits in "st_mode" currently match the definitions 132 // for the file mode bits in unix. 133 file_permissions = file_stats.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); 134 } else { 135 error.SetErrorToErrno(); 136 } 137 return error; 138 } 139 140 Error FileSystem::SetFilePermissions(const FileSpec &file_spec, 141 uint32_t file_permissions) { 142 Error error; 143 if (::chmod(file_spec.GetCString(), file_permissions) != 0) 144 error.SetErrorToErrno(); 145 return error; 146 } 147 148 lldb::user_id_t FileSystem::GetFileSize(const FileSpec &file_spec) { 149 return file_spec.GetByteSize(); 150 } 151 152 bool FileSystem::GetFileExists(const FileSpec &file_spec) { 153 return file_spec.Exists(); 154 } 155 156 Error FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst) { 157 Error error; 158 if (::link(dst.GetCString(), src.GetCString()) == -1) 159 error.SetErrorToErrno(); 160 return error; 161 } 162 163 int FileSystem::GetHardlinkCount(const FileSpec &file_spec) { 164 struct stat file_stat; 165 if (::stat(file_spec.GetCString(), &file_stat) == 0) 166 return file_stat.st_nlink; 167 168 return -1; 169 } 170 171 Error FileSystem::Symlink(const FileSpec &src, const FileSpec &dst) { 172 Error error; 173 if (::symlink(dst.GetCString(), src.GetCString()) == -1) 174 error.SetErrorToErrno(); 175 return error; 176 } 177 178 Error FileSystem::Unlink(const FileSpec &file_spec) { 179 Error error; 180 if (::unlink(file_spec.GetCString()) == -1) 181 error.SetErrorToErrno(); 182 return error; 183 } 184 185 Error FileSystem::Readlink(const FileSpec &src, FileSpec &dst) { 186 Error error; 187 char buf[PATH_MAX]; 188 ssize_t count = ::readlink(src.GetCString(), buf, sizeof(buf) - 1); 189 if (count < 0) 190 error.SetErrorToErrno(); 191 else { 192 buf[count] = '\0'; // Success 193 dst.SetFile(buf, false); 194 } 195 return error; 196 } 197 198 Error FileSystem::ResolveSymbolicLink(const FileSpec &src, FileSpec &dst) { 199 char resolved_path[PATH_MAX]; 200 if (!src.GetPath(resolved_path, sizeof(resolved_path))) { 201 return Error("Couldn't get the canonical path for %s", src.GetCString()); 202 } 203 204 char real_path[PATH_MAX + 1]; 205 if (realpath(resolved_path, real_path) == nullptr) { 206 Error err; 207 err.SetErrorToErrno(); 208 return err; 209 } 210 211 dst = FileSpec(real_path, false); 212 213 return Error(); 214 } 215 216 #if defined(__NetBSD__) 217 static bool IsLocal(const struct statvfs &info) { 218 return (info.f_flag & MNT_LOCAL) != 0; 219 } 220 #else 221 static bool IsLocal(const struct statfs &info) { 222 #ifdef __linux__ 223 #define CIFS_MAGIC_NUMBER 0xFF534D42 224 switch ((uint32_t)info.f_type) { 225 case NFS_SUPER_MAGIC: 226 case SMB_SUPER_MAGIC: 227 case CIFS_MAGIC_NUMBER: 228 return false; 229 default: 230 return true; 231 } 232 #else 233 return (info.f_flags & MNT_LOCAL) != 0; 234 #endif 235 } 236 #endif 237 238 #if defined(__NetBSD__) 239 bool FileSystem::IsLocal(const FileSpec &spec) { 240 struct statvfs statfs_info; 241 std::string path(spec.GetPath()); 242 if (statvfs(path.c_str(), &statfs_info) == 0) 243 return ::IsLocal(statfs_info); 244 return false; 245 } 246 #else 247 bool FileSystem::IsLocal(const FileSpec &spec) { 248 struct statfs statfs_info; 249 std::string path(spec.GetPath()); 250 if (statfs(path.c_str(), &statfs_info) == 0) 251 return ::IsLocal(statfs_info); 252 return false; 253 } 254 #endif 255 256 FILE *FileSystem::Fopen(const char *path, const char *mode) { 257 return ::fopen(path, mode); 258 } 259 260 int FileSystem::Stat(const char *path, struct stat *stats) { 261 return ::stat(path, stats); 262 } 263