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