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