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