xref: /aosp_15_r20/external/icing/icing/file/filesystem.cc (revision 8b6cd535a057e39b3b86660c4aa06c99747c2136)
1 // Copyright (C) 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "icing/file/filesystem.h"
16 
17 #include <dirent.h>
18 #include <dlfcn.h>
19 #include <fcntl.h>
20 #include <fnmatch.h>
21 #include <pthread.h>
22 #include <sys/resource.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26 
27 #include <algorithm>
28 #include <cerrno>
29 #include <cstdint>
30 #include <unordered_set>
31 
32 #include "icing/absl_ports/str_cat.h"
33 #include "icing/legacy/core/icing-string-util.h"
34 #include "icing/util/logging.h"
35 
36 using std::vector;
37 
38 namespace icing {
39 namespace lib {
40 
41 namespace {
42 
43 // The size of the block for st_blksize returned by stat() and as a
44 // consequence also the granularity of GetDiskUsage(). It seems that there is
45 // no appropriate constant for this. See http://linux.die.net/man/2/stat
46 constexpr int kStatBlockSize = 512;
47 
48 // Logs information about open file descriptors.
49 //
50 // This function uses getrlimit() to find the maximum number of file
51 // descriptors, then calls readlink("/proc/self/fd/N") for each possible file
52 // descriptor number to get a description of the open file from procfs.
53 //
54 // We don't use readdir() to list the contents of /proc/self/fd (which would be
55 // the more obvious approach) because that would require a free file descriptor
56 // to open the directory, while we call this function when all file descriptors
57 // are in use.
LogOpenFileDescriptors()58 void LogOpenFileDescriptors() {
59   // Determine the limit on file descriptor numbers. RLIMIT_NOFILE should return
60   // the maximum file descriptor + 1, which is 1024 on Android by default. We
61   // restrict the limit to 4096 so we don't take too much time if the value
62   // turns out to be much higher for some reason.
63   constexpr int kMaxFileDescriptorsToStat = 4096;
64   struct rlimit rlim = {0, 0};
65   if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
66     ICING_LOG(ERROR) << "getrlimit() failed (errno=" << errno << ")";
67     return;
68   }
69   int fd_lim = rlim.rlim_cur;
70   if (fd_lim > kMaxFileDescriptorsToStat) {
71     ICING_LOG(ERROR) << "Maximum number of file descriptors (" << fd_lim
72         << ") too large.";
73     fd_lim = kMaxFileDescriptorsToStat;
74   }
75   ICING_LOG(INFO) << "Listing up to " << fd_lim << " file descriptors.";
76 
77   // Verify that /proc/self/fd is a directory. If not, procfs is not mounted or
78   // inaccessible for some other reason. In that case, there's no point trying
79   // to read from it.
80   struct stat statbuf;
81   if (stat("/proc/self/fd", &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) {
82     ICING_LOG(ERROR) << "/proc/self/fd not available. Giving up.";
83     return;
84   }
85 
86   // Now read each link individually.
87   const int path_size = 1024;
88   char path[path_size];
89   const int target_size = 1024;
90   char target[target_size];
91   for (int fd = 0; fd < fd_lim; ++fd) {
92     snprintf(path, path_size, "/proc/self/fd/%d", fd);
93     ssize_t len = readlink(path, target, target_size);
94     if (len >= 0) {
95       // Zero-terminate the buffer, because readlink() won't.
96       target[len < target_size ? len : target_size - 1] = '\0';
97       ICING_LOG(INFO) << "fd " << fd << " -> \"" << target << "\"";
98     } else if (errno != ENOENT) {
99       ICING_LOG(ERROR) << "fd " << fd << " -> ? (errno=" << errno << ")";
100     }
101   }
102   ICING_LOG(INFO) << "File descriptor list complete.";
103 }
104 
105 // Logs an error formatted as: desc1 + file_name + desc2 + strerror(errnum).
106 //
107 // If errnum == EMFILE (too many open files), then it also logs a list of open
108 // file descriptors (see LogOpenFileDescriptors() above).
LogOpenError(const char * desc1,const char * file_name,const char * desc2,int errnum)109 void LogOpenError(const char* desc1, const char* file_name, const char* desc2,
110                   int errnum) {
111   if (errnum == ENOENT) {
112     ICING_VLOG(1) << desc1 << file_name << desc2 << strerror(errnum);
113   } else {
114     ICING_LOG(ERROR) << desc1 << file_name << desc2 << strerror(errnum);
115   }
116   if (errnum == EMFILE) {
117     LogOpenFileDescriptors();
118   }
119 }
120 
121 // Recursive implementation of ListDirectory. Prefix is used to prepend the
122 // directory name during recursion.
123 // We cannot use scandir due to a bug in old platform versions. See b/7339844.
ListDirectoryInternal(const char * dir_name,const std::unordered_set<std::string> & exclude,bool recursive,const char * prefix,std::vector<std::string> * entries)124 bool ListDirectoryInternal(const char* dir_name,
125                            const std::unordered_set<std::string>& exclude,
126                            bool recursive, const char* prefix,
127                            std::vector<std::string>* entries) {
128   DIR* dir = opendir(dir_name);
129   if (!dir) {
130     LogOpenError("Unable to open directory ", dir_name, ": ", errno);
131     return false;
132   }
133 
134   // According to linux man page
135   // (https://man7.org/linux/man-pages/man3/readdir.3.html#RETURN_VALUE), dirent
136   // may be statically allocated, so don't free it.
137   dirent* p;
138   // readdir's implementation seems to be thread safe.
139   while ((p = readdir(dir)) != nullptr) {
140     std::string file_name(p->d_name);
141     if (file_name == "." || file_name == ".." ||
142         exclude.find(file_name) != exclude.end()) {
143       continue;
144     }
145     std::string relative_path = absl_ports::StrCat(prefix, p->d_name);
146     entries->push_back(relative_path);
147     // Recurse down directories, if requested.
148     if (recursive && (p->d_type == DT_DIR)) {
149       std::string sub_dir_name = absl_ports::StrCat(dir_name, "/", p->d_name);
150       std::string relative_path_with_slash =
151           absl_ports::StrCat(relative_path, "/");
152       if (!ListDirectoryInternal(sub_dir_name.c_str(), exclude, recursive,
153                                  relative_path_with_slash.c_str(), entries)) {
154         return false;
155       }
156     }
157   }
158   if (closedir(dir) != 0) {
159     ICING_LOG(ERROR) << "Error closing " << dir_name << " " << strerror(errno);
160   }
161   return true;
162 }
163 
164 }  // namespace
165 
~ScopedFd()166 ScopedFd::~ScopedFd() {
167   if (fd_ >= 0) {
168     close(fd_);
169   }
170 }
171 
reset(int fd)172 void ScopedFd::reset(int fd) {
173   if (fd_ >= 0) {
174     close(fd_);
175   }
176   fd_ = fd;
177 }
178 
179 const int64_t Filesystem::kBadFileSize;
180 
DeleteFile(const char * file_name) const181 bool Filesystem::DeleteFile(const char* file_name) const {
182   ICING_VLOG(1) << "Deleting file " << file_name;
183   int ret = unlink(file_name);
184   if (ret != 0 && errno != ENOENT) {
185     ICING_LOG(ERROR) << "Deleting file " << file_name << " failed: " << strerror(errno);
186     return false;
187   }
188   return true;
189 }
190 
DeleteDirectory(const char * dir_name) const191 bool Filesystem::DeleteDirectory(const char* dir_name) const {
192   int ret = rmdir(dir_name);
193   if (ret != 0 && errno != ENOENT) {
194     ICING_LOG(ERROR) << "Deleting directory " << dir_name << " failed: " << strerror(errno);
195     return false;
196   }
197   return true;
198 }
199 
DeleteDirectoryRecursively(const char * dir_name) const200 bool Filesystem::DeleteDirectoryRecursively(const char* dir_name) const {
201   // Ensure the dir_name really is a directory and exists.
202   struct stat st;
203   if (stat(dir_name, &st) < 0) {
204     if (errno == ENOENT) {
205       return true;  // If directory didn't exist, this was successful.
206     }
207     ICING_LOG(ERROR) << "Stat " << dir_name << " failed: " << strerror(errno);
208     return false;
209   }
210   vector<std::string> entries;
211   if (!ListDirectory(dir_name, &entries)) {
212     return false;
213   }
214 
215   bool success = true;
216   for (vector<std::string>::iterator i = entries.begin(); i != entries.end();
217        ++i) {
218     std::string filename = std::string(dir_name) + '/' + *i;
219     if (stat(filename.c_str(), &st) < 0) {
220       ICING_LOG(ERROR) << "Stat " << filename << " failed: " << strerror(errno);
221       success = false;
222     } else if (S_ISDIR(st.st_mode)) {
223       success = DeleteDirectoryRecursively(filename.c_str()) && success;
224     } else {
225       success = DeleteFile(filename.c_str()) && success;
226     }
227   }
228 
229   if (success) {
230     success = DeleteDirectory(dir_name);
231   }
232 
233   return success;
234 }
235 
FileExists(const char * file_name) const236 bool Filesystem::FileExists(const char* file_name) const {
237   bool exists = false;
238   struct stat st;
239   if (stat(file_name, &st) == 0) {
240     exists = S_ISREG(st.st_mode) != 0;
241   } else {
242     if (errno != ENOENT) {
243       ICING_LOG(ERROR) << "Unable to stat file " << file_name << ": " << strerror(errno);
244     }
245     exists = false;
246   }
247   return exists;
248 }
249 
DirectoryExists(const char * dir_name) const250 bool Filesystem::DirectoryExists(const char* dir_name) const {
251   bool exists = false;
252   struct stat st;
253   if (stat(dir_name, &st) == 0) {
254     exists = S_ISDIR(st.st_mode) != 0;
255   } else {
256     if (errno != ENOENT) {
257       ICING_LOG(ERROR) << "Unable to stat directory " << dir_name << ": " << strerror(errno);
258     }
259     exists = false;
260   }
261   return exists;
262 }
263 
GetBasenameIndex(const char * file_name) const264 int Filesystem::GetBasenameIndex(const char* file_name) const {
265   // Find final slash.
266   const char* last_slash = strrchr(file_name, '/');
267   if (!last_slash) {
268     // file_name is just basename.
269     return 0;
270   }
271 
272   // Skip slash.
273   return last_slash + 1 - file_name;
274 }
275 
GetBasename(const char * file_name) const276 std::string Filesystem::GetBasename(const char* file_name) const {
277   size_t len = strlen(file_name);
278   int idx = GetBasenameIndex(file_name);
279   return std::string(file_name + idx, len - idx);
280 }
281 
GetDirname(const char * file_name) const282 std::string Filesystem::GetDirname(const char* file_name) const {
283   int idx = GetBasenameIndex(file_name);
284   // Remove the trailing slash
285   if (idx > 0) {
286     idx -= 1;
287   }
288   return std::string(file_name, idx);
289 }
290 
ListDirectory(const char * dir_name,vector<std::string> * entries) const291 bool Filesystem::ListDirectory(const char* dir_name,
292                                vector<std::string>* entries) const {
293   entries->clear();
294   return ListDirectory(dir_name, /*exclude=*/{}, /*recursive=*/false, entries);
295 }
296 
ListDirectory(const char * dir_name,const std::unordered_set<std::string> & exclude,bool recursive,std::vector<std::string> * entries) const297 bool Filesystem::ListDirectory(const char* dir_name,
298                                const std::unordered_set<std::string>& exclude,
299                                bool recursive,
300                                std::vector<std::string>* entries) const {
301   return ListDirectoryInternal(dir_name, exclude, recursive, /*prefix=*/"",
302                                entries);
303 }
304 
GetMatchingFiles(const char * glob,vector<std::string> * matches) const305 bool Filesystem::GetMatchingFiles(const char* glob,
306                                   vector<std::string>* matches) const {
307   matches->clear();
308 
309   // Split dirname/basename.
310   int basename_idx = GetBasenameIndex(glob);
311   if (basename_idx == 0) {
312     // We need a directory.
313     ICING_VLOG(1) << "Expected directory, no matching files for: " << glob;
314     return true;
315   }
316   const char* basename_glob = glob + basename_idx;
317   std::string dirname(glob, basename_idx);
318   vector<std::string> entries;
319   if (!ListDirectory(dirname.c_str(), &entries) && errno != ENOENT) {
320     return false;
321   }
322 
323   for (vector<std::string>::iterator i = entries.begin(); i != entries.end();
324        ++i) {
325     // The filename needs to match glob following last_slash.
326     if (!fnmatch(basename_glob, i->c_str(), FNM_PATHNAME)) {
327       // Add it to the list.
328       matches->push_back(dirname + *i);
329     }
330   }
331   return true;
332 }
333 
OpenForWrite(const char * file_name) const334 int Filesystem::OpenForWrite(const char* file_name) const {
335   int fd = open(file_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
336   if (fd < 0) {
337     LogOpenError("Opening file ", file_name, " for write failed: ", errno);
338   }
339   return fd;
340 }
341 
OpenForAppend(const char * file_name) const342 int Filesystem::OpenForAppend(const char* file_name) const {
343   // Don't use the O_APPEND flag because, although it opens for
344   // append, it doesn't set the file cursor to at the end until
345   // first write occurs.  This can be confusing if you expect
346   // the file position at the end.  Instead, explicitly
347   // seek to end after opening.
348   int fd = open(file_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
349   if (fd < 0) {
350     LogOpenError("Opening file ", file_name, " for write failed: ", errno);
351   } else {
352     lseek(fd, 0, SEEK_END);
353   }
354   return fd;
355 }
356 
OpenForRead(const char * file_name) const357 int Filesystem::OpenForRead(const char* file_name) const {
358   int fd = open(file_name, O_RDONLY);
359   if (fd < 0) {
360     LogOpenError("Opening file ", file_name, " for read failed: ", errno);
361   }
362   return fd;
363 }
364 
GetFileSize(int fd) const365 int64_t Filesystem::GetFileSize(int fd) const {
366   struct stat st;
367   if (fstat(fd, &st) < 0) {
368     if (errno == ENOENT) {
369       ICING_VLOG(1) << "Unable to stat file: " << strerror(errno);
370     } else {
371       ICING_LOG(WARNING) << "Unable to stat file: " << strerror(errno);
372     }
373     return kBadFileSize;
374   }
375   return st.st_size;
376 }
377 
GetFileSize(const char * filename) const378 int64_t Filesystem::GetFileSize(const char* filename) const {
379   struct stat st;
380   if (stat(filename, &st) < 0) {
381     if (errno == ENOENT) {
382       ICING_VLOG(1) << "Unable to stat file " << filename << ": " << strerror(errno);
383     } else {
384       ICING_LOG(WARNING) << "Unable to stat file " << filename << ": " << strerror(errno);
385     }
386     return kBadFileSize;
387   }
388   return st.st_size;
389 }
390 
Truncate(int fd,int64_t new_size) const391 bool Filesystem::Truncate(int fd, int64_t new_size) const {
392   if (ftruncate(fd, new_size) != 0) {
393     ICING_LOG(ERROR) << "Unable to truncate file: " << strerror(errno);
394     return false;
395   }
396   lseek(fd, new_size, SEEK_SET);
397   return true;
398 }
399 
Truncate(const char * filename,int64_t new_size) const400 bool Filesystem::Truncate(const char* filename, int64_t new_size) const {
401   int fd = OpenForAppend(filename);
402   if (fd == -1) {
403     return false;
404   }
405   bool success = Truncate(fd, new_size);
406   close(fd);
407   return success;
408 }
409 
Grow(int fd,int64_t new_size) const410 bool Filesystem::Grow(int fd, int64_t new_size) const {
411   if (ftruncate(fd, new_size) != 0) {
412     ICING_LOG(ERROR) << "Unable to grow file: " << strerror(errno);
413     return false;
414   }
415 
416   return true;
417 }
418 
Grow(const char * filename,int64_t new_size) const419 bool Filesystem::Grow(const char* filename, int64_t new_size) const {
420   int fd = OpenForAppend(filename);
421   if (fd == -1) {
422     return false;
423   }
424 
425   bool grew = Grow(fd, new_size);
426   close(fd);
427   return grew;
428 }
429 
Write(int fd,const void * data,size_t data_size) const430 bool Filesystem::Write(int fd, const void* data, size_t data_size) const {
431   size_t write_len = data_size;
432   do {
433     // Don't try to write too much at once.
434     size_t chunk_size = std::min<size_t>(write_len, 64u * 1024);
435     ssize_t wrote = write(fd, data, chunk_size);
436     if (wrote < 0) {
437       ICING_LOG(ERROR) << "Bad write: " << strerror(errno);
438       return false;
439     }
440     data = static_cast<const uint8_t*>(data) + wrote;
441     write_len -= wrote;
442   } while (write_len > 0);
443   return true;
444 }
445 
Write(const char * filename,const void * data,size_t data_size) const446 bool Filesystem::Write(const char* filename, const void* data,
447                        size_t data_size) const {
448   int fd = OpenForWrite(filename);
449   if (fd == -1) {
450     return false;
451   }
452 
453   bool success = Write(fd, data, data_size);
454   close(fd);
455   return success;
456 }
457 
CopyFile(const char * src,const char * dst) const458 bool Filesystem::CopyFile(const char* src, const char* dst) const {
459   ScopedFd src_fd(OpenForRead(src));
460 
461   std::string dir = GetDirname(dst);
462   if (!CreateDirectoryRecursively(dir.c_str())) {
463     return false;
464   }
465   ScopedFd dst_fd(OpenForWrite(dst));
466 
467   if (!src_fd.is_valid() || !dst_fd.is_valid()) {
468     return false;
469   }
470   uint64_t size = GetFileSize(*src_fd);
471   std::unique_ptr<uint8_t[]> buf = std::make_unique<uint8_t[]>(size);
472   if (!Read(*src_fd, buf.get(), size)) {
473     return false;
474   }
475   return Write(*dst_fd, buf.get(), size);
476 }
477 
CopyDirectory(const char * src_dir,const char * dst_dir,bool recursive) const478 bool Filesystem::CopyDirectory(const char* src_dir, const char* dst_dir,
479                                bool recursive) const {
480   DIR* dir = opendir(src_dir);
481   if (!dir) {
482     LogOpenError("Unable to open directory ", src_dir, ": ", errno);
483     return false;
484   }
485 
486   dirent* p;
487   // readdir's implementation seems to be thread safe.
488   while ((p = readdir(dir)) != nullptr) {
489     std::string file_name(p->d_name);
490     if (file_name == "." || file_name == "..") {
491       continue;
492     }
493 
494     std::string full_src_path = absl_ports::StrCat(src_dir, "/", p->d_name);
495     std::string full_dst_path = absl_ports::StrCat(dst_dir, "/", p->d_name);
496 
497     // Directories are copied when writing a non-directory file, so no
498     // explicit copying of a directory is required.
499     if (p->d_type != DT_DIR) {
500       if (!CopyFile(full_src_path.c_str(), full_dst_path.c_str())) {
501         return false;
502       }
503     }
504 
505     // Recurse down directories, if requested.
506     if (recursive && (p->d_type == DT_DIR)) {
507       std::string src_sub_dir = absl_ports::StrCat(src_dir, "/", p->d_name);
508       std::string dst_sub_dir = absl_ports::StrCat(dst_dir, "/", p->d_name);
509       if (!CopyDirectory(src_sub_dir.c_str(), dst_sub_dir.c_str(), recursive)) {
510         return false;
511       }
512     }
513   }
514   if (closedir(dir) != 0) {
515     ICING_LOG(ERROR) << "Error closing " << src_dir << ": " << strerror(errno);
516   }
517   return true;
518 }
519 
PWrite(int fd,off_t offset,const void * data,size_t data_size) const520 bool Filesystem::PWrite(int fd, off_t offset, const void* data,
521                         size_t data_size) const {
522   size_t write_len = data_size;
523   do {
524     // Don't try to write too much at once.
525     size_t chunk_size = std::min<size_t>(write_len, 64u * 1024);
526     ssize_t wrote = pwrite(fd, data, chunk_size, offset);
527     if (wrote < 0) {
528       ICING_LOG(ERROR) << "Bad write: " << strerror(errno);
529       return false;
530     }
531     data = static_cast<const uint8_t*>(data) + wrote;
532     write_len -= wrote;
533     offset += wrote;
534   } while (write_len > 0);
535   return true;
536 }
537 
PWrite(const char * filename,off_t offset,const void * data,size_t data_size) const538 bool Filesystem::PWrite(const char* filename, off_t offset, const void* data,
539                         size_t data_size) const {
540   int fd = OpenForWrite(filename);
541   if (fd == -1) {
542     return false;
543   }
544 
545   bool success = PWrite(fd, offset, data, data_size);
546   close(fd);
547   return success;
548 }
549 
Read(int fd,void * buf,size_t buf_size) const550 bool Filesystem::Read(int fd, void* buf, size_t buf_size) const {
551   ssize_t read_status = read(fd, buf, buf_size);
552   if (read_status < 0) {
553     ICING_LOG(ERROR) << "Bad read: " << strerror(errno);
554     return false;
555   }
556   return true;
557 }
558 
Read(const char * filename,void * buf,size_t buf_size) const559 bool Filesystem::Read(const char* filename, void* buf, size_t buf_size) const {
560   int fd = OpenForRead(filename);
561   if (fd == -1) {
562     return false;
563   }
564 
565   bool success = Read(fd, buf, buf_size);
566   close(fd);
567   return success;
568 }
569 
PRead(int fd,void * buf,size_t buf_size,off_t offset) const570 bool Filesystem::PRead(int fd, void* buf, size_t buf_size, off_t offset) const {
571   ssize_t read_status = pread(fd, buf, buf_size, offset);
572   if (read_status < 0) {
573     ICING_LOG(ERROR) << "Bad read: " << strerror(errno);
574     return false;
575   }
576   return true;
577 }
578 
PRead(const char * filename,void * buf,size_t buf_size,off_t offset) const579 bool Filesystem::PRead(const char* filename, void* buf, size_t buf_size,
580                        off_t offset) const {
581   int fd = OpenForRead(filename);
582   if (fd == -1) {
583     return false;
584   }
585 
586   bool success = PRead(fd, buf, buf_size, offset);
587   close(fd);
588   return success;
589 }
590 
DataSync(int fd) const591 bool Filesystem::DataSync(int fd) const {
592 #ifdef __APPLE__  // iOS has no fdatasync(), only fsync()
593   int result = fsync(fd);
594 #else
595   int result = fdatasync(fd);
596 #endif
597 
598   if (result < 0) {
599     ICING_LOG(ERROR) << "Unable to sync data: " << strerror(errno);
600     return false;
601   }
602   return true;
603 }
604 
RenameFile(const char * old_name,const char * new_name) const605 bool Filesystem::RenameFile(const char* old_name, const char* new_name) const {
606   if (rename(old_name, new_name) < 0) {
607     ICING_LOG(ERROR) << "Unable to rename file " << old_name << " to " << new_name << ": " << strerror(errno);
608     return false;
609   }
610   return true;
611 }
612 
SwapFiles(const char * one,const char * two) const613 bool Filesystem::SwapFiles(const char* one, const char* two) const {
614   std::string tmp_name = absl_ports::StrCat(one, ".tmp");
615   const char* tmp_cstr = tmp_name.c_str();
616 
617   // Blow away a tmp file if it already exists
618   if (FileExists(tmp_cstr) && !DeleteFile(tmp_cstr)) {
619     return false;
620   }
621   if (DirectoryExists(tmp_cstr) && !DeleteDirectoryRecursively(tmp_cstr)) {
622     return false;
623   }
624 
625   // Perform the swap
626   if (!RenameFile(one, tmp_cstr)) {
627     return false;
628   }
629   if (!RenameFile(two, one)) {
630     return false;
631   }
632   if (!RenameFile(tmp_cstr, two)) {
633     return false;
634   }
635 
636   return true;
637 }
638 
CreateDirectory(const char * dir_name) const639 bool Filesystem::CreateDirectory(const char* dir_name) const {
640   bool success = DirectoryExists(dir_name);
641   if (!success) {
642     if (mkdir(dir_name, S_IRUSR | S_IWUSR | S_IXUSR) == 0) {
643       success = true;
644     } else {
645       ICING_LOG(ERROR) << "Creating directory " << dir_name << " failed: " << strerror(errno);
646     }
647   }
648   return success;
649 }
650 
CreateDirectoryRecursively(const char * dir_name) const651 bool Filesystem::CreateDirectoryRecursively(const char* dir_name) const {
652   if ((strlen(dir_name) == 0) || DirectoryExists(dir_name)) {
653     return true;
654   }
655   std::string path_before = GetDirname(dir_name);
656   if (!CreateDirectoryRecursively(path_before.c_str())) {
657     return false;
658   }
659   return CreateDirectory(dir_name);
660 }
661 
GetDiskUsage(int fd) const662 int64_t Filesystem::GetDiskUsage(int fd) const {
663   struct stat st;
664   if (fstat(fd, &st) < 0) {
665     ICING_LOG(ERROR) << "Unable to stat file: " << strerror(errno);
666     return kBadFileSize;
667   }
668   return st.st_blocks * kStatBlockSize;
669 }
670 
GetFileDiskUsage(const char * path) const671 int64_t Filesystem::GetFileDiskUsage(const char* path) const {
672   struct stat st;
673   if (stat(path, &st) != 0) {
674     ICING_LOG(ERROR) << "Unable to stat " << path << ": " << strerror(errno);
675     return kBadFileSize;
676   }
677   return st.st_blocks * kStatBlockSize;
678 }
679 
GetDiskUsage(const char * path) const680 int64_t Filesystem::GetDiskUsage(const char* path) const {
681   struct stat st;
682   if (stat(path, &st) != 0) {
683     ICING_LOG(ERROR) << "Unable to stat " << path << ": " << strerror(errno);
684     return kBadFileSize;
685   }
686   int64_t result = st.st_blocks * kStatBlockSize;
687   if (S_ISDIR(st.st_mode)) {
688     vector<std::string> list;
689     if (!ListDirectory(path, &list)) {
690       return kBadFileSize;
691     }
692     for (vector<std::string>::iterator i = list.begin(); i != list.end(); ++i) {
693       std::string sub_path = std::string(path) + '/' + *i;
694       uint64_t sub_usage = GetDiskUsage(sub_path.c_str());
695       if (sub_usage != kBadFileSize) {
696         result += sub_usage;
697       }  // Else just ignore the failing entry.
698     }
699   }
700   return result;
701 }
702 
GetCurrentPosition(int fd) const703 int64_t Filesystem::GetCurrentPosition(int fd) const {
704   return lseek(fd, 0, SEEK_CUR);
705 }
706 
SetPosition(int fd,int offset) const707 int64_t Filesystem::SetPosition(int fd, int offset) const {
708   return lseek(fd, offset, SEEK_SET);
709 }
710 
IncrementByOrSetInvalid(int64_t size,int64_t * to_increment)711 void Filesystem::IncrementByOrSetInvalid(int64_t size, int64_t* to_increment) {
712   if (*to_increment == kBadFileSize) {
713     return;
714   }
715   if (size == kBadFileSize) {
716     *to_increment = kBadFileSize;
717     return;
718   }
719   *to_increment += size;
720 }
721 
722 }  // namespace lib
723 }  // namespace icing
724