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