xref: /aosp_15_r20/external/icing/icing/legacy/index/icing-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/legacy/index/icing-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/mman.h>
23 #include <sys/resource.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27 
28 #include <algorithm>
29 #include <cerrno>
30 #include <cstddef>
31 #include <cstdint>
32 #include <cstdio>
33 #include <cstring>
34 #include <memory>
35 #include <string>
36 #include <unordered_set>
37 #include <vector>
38 
39 #include "icing/absl_ports/str_cat.h"
40 #include "icing/legacy/index/icing-mmapper.h"
41 #include "icing/legacy/portable/icing-zlib.h"
42 #include "icing/util/logging.h"
43 
44 using std::vector;
45 
46 namespace icing {
47 namespace lib {
48 
49 namespace {
50 
51 // The size of the block for st_blksize returned by stat() and as a
52 // consequence also the granularity of GetDiskUsage(). It seems that there is
53 // no appropriate constant for this. See http://linux.die.net/man/2/stat
54 constexpr int kStatBlockSize = 512;
55 
56 // Logs information about open file descriptors.
57 //
58 // This function uses getrlimit() to find the maximum number of file
59 // descriptors, then calls readlink("/proc/self/fd/N") for each possible file
60 // descriptor number to get a description of the open file from procfs.
61 //
62 // We don't use readdir() to list the contents of /proc/self/fd (which would be
63 // the more obvious approach) because that would require a free file descriptor
64 // to open the directory, while we call this function when all file descriptors
65 // are in use.
LogOpenFileDescriptors()66 void LogOpenFileDescriptors() {
67   // Determine the limit on file descriptor numbers. RLIMIT_NOFILE should return
68   // the maximum file descriptor + 1, which is 1024 on Android by default. We
69   // restrict the limit to 4096 so we don't take too much time if the value
70   // turns out to be much higher for some reason.
71   constexpr int kMaxFileDescriptorsToStat = 4096;
72   struct rlimit rlim = {0, 0};
73   if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
74     ICING_LOG(ERROR) << "getrlimit() failed (errno=" << errno << ")";
75     return;
76   }
77   int fd_lim = rlim.rlim_cur;
78   if (fd_lim > kMaxFileDescriptorsToStat) {
79     ICING_LOG(ERROR) << "Maximum number of file descriptors (" << fd_lim
80                      << ") too large.";
81     fd_lim = kMaxFileDescriptorsToStat;
82   }
83   ICING_LOG(ERROR) << "Listing up to " << fd_lim << " file descriptors.";
84 
85   // Verify that /proc/self/fd is a directory. If not, procfs is not mounted or
86   // inaccessible for some other reason. In that case, there's no point trying
87   // to read from it.
88   struct stat statbuf;
89   if (stat("/proc/self/fd", &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) {
90     ICING_LOG(ERROR) << "/proc/self/fd not available. Giving up.";
91     return;
92   }
93 
94   // Now read each link individually.
95   const int path_size = 1024;
96   char path[path_size];
97   const int target_size = 1024;
98   char target[target_size];
99   for (int fd = 0; fd < fd_lim; ++fd) {
100     snprintf(path, path_size, "/proc/self/fd/%d", fd);
101     ssize_t len = readlink(path, target, target_size);
102     if (len >= 0) {
103       // Zero-terminate the buffer, because readlink() won't.
104       target[len < target_size ? len : target_size - 1] = '\0';
105       ICING_LOG(ERROR) << "fd " << fd << " -> \"" << target << "\"";
106     } else if (errno != ENOENT) {
107       ICING_LOG(ERROR) << "fd " << fd << " -> ? (errno=" << errno << ")";
108     }
109   }
110   ICING_LOG(ERROR) << "File descriptor list complete.";
111 }
112 
113 // Logs an error formatted as: desc1 + file_name + desc2 + strerror(errnum).
114 //
115 // If errnum == EMFILE (too many open files), then it also logs a list of open
116 // file descriptors (see LogOpenFileDescriptors() above).
LogOpenError(const char * desc1,const char * file_name,const char * desc2,int errnum)117 void LogOpenError(const char *desc1, const char *file_name, const char *desc2,
118                   int errnum) {
119   ICING_LOG(ERROR) << desc1 << file_name << desc2 << strerror(errnum);
120   if (errnum == EMFILE) {
121     LogOpenFileDescriptors();
122   }
123 }
124 
125 // Recursive implementation of ListDirectory. Prefix is used to prepend the
126 // directory name during recursion.
127 // 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)128 bool ListDirectoryInternal(const char *dir_name,
129                            const std::unordered_set<std::string> &exclude,
130                            bool recursive, const char *prefix,
131                            std::vector<std::string> *entries) {
132   DIR *dir = opendir(dir_name);
133   if (!dir) {
134     LogOpenError("Unable to open directory ", dir_name, ": ", errno);
135     return false;
136   }
137 
138   dirent *p;
139   // readdir's implementation seems to be thread safe.
140   while ((p = readdir(dir)) != nullptr) {
141     std::string file_name(p->d_name);
142     if (file_name == "." || file_name == ".." ||
143         exclude.find(file_name) != exclude.end()) {
144       continue;
145     }
146     std::string relative_path = absl_ports::StrCat(prefix, p->d_name);
147     entries->push_back(relative_path);
148     // Recurse down directories, if requested.
149     if (recursive && (p->d_type == DT_DIR)) {
150       std::string sub_dir_name = absl_ports::StrCat(dir_name, "/", p->d_name);
151       std::string relative_path_with_slash =
152           absl_ports::StrCat(relative_path, "/");
153       if (!ListDirectoryInternal(sub_dir_name.c_str(), exclude, recursive,
154                                  relative_path_with_slash.c_str(), entries)) {
155         return false;
156       }
157     }
158   }
159   if (closedir(dir) != 0) {
160     ICING_LOG(ERROR) << "Error closing " << dir_name << ": " << strerror(errno);
161   }
162   return true;
163 }
164 
165 }  // namespace
166 
~IcingScopedFd()167 IcingScopedFd::~IcingScopedFd() {
168   if (fd_ >= 0) {
169     close(fd_);
170   }
171 }
172 
reset(int fd)173 void IcingScopedFd::reset(int fd) {
174   if (fd_ >= 0) {
175     close(fd_);
176   }
177   fd_ = fd;
178 }
179 
180 const uint64_t IcingFilesystem::kBadFileSize;
181 
DeleteFile(const char * file_name) const182 bool IcingFilesystem::DeleteFile(const char *file_name) const {
183   ICING_VLOG(1) << "Deleting file " << file_name;
184   int ret = unlink(file_name);
185   bool success = (ret == 0) || (errno == ENOENT);
186   if (!success) {
187     ICING_LOG(ERROR) << "Deleting file " << file_name
188                      << " failed: " << strerror(errno);
189   }
190   return success;
191 }
192 
DeleteDirectory(const char * dir_name) const193 bool IcingFilesystem::DeleteDirectory(const char *dir_name) const {
194   int ret = rmdir(dir_name);
195   bool success = (ret == 0) || (errno == ENOENT);
196   if (!success) {
197     ICING_LOG(ERROR) << "Deleting directory " << dir_name
198                      << " failed: " << strerror(errno);
199   }
200   return success;
201 }
202 
DeleteDirectoryRecursively(const char * dir_name) const203 bool IcingFilesystem::DeleteDirectoryRecursively(const char *dir_name) const {
204   // Ensure the dir_name really is a directory and exists.
205   struct stat st;
206   if (stat(dir_name, &st) < 0) {
207     if (errno == ENOENT) {
208       return true;  // If directory didn't exist, this was successful.
209     }
210     ICING_LOG(ERROR) << "Stat " << dir_name << " failed: " << strerror(errno);
211     return false;
212   }
213   vector<std::string> entries;
214   if (!ListDirectory(dir_name, &entries)) {
215     return false;
216   }
217 
218   bool success = true;
219   for (vector<std::string>::iterator i = entries.begin(); i != entries.end();
220        ++i) {
221     std::string filename = std::string(dir_name) + '/' + *i;
222     if (stat(filename.c_str(), &st) < 0) {
223       ICING_LOG(ERROR) << "Stat " << filename << " failed: " << strerror(errno);
224       success = false;
225     } else if (S_ISDIR(st.st_mode)) {
226       success = DeleteDirectoryRecursively(filename.c_str()) && success;
227     } else {
228       success = DeleteFile(filename.c_str()) && success;
229     }
230   }
231 
232   if (success) {
233     success = DeleteDirectory(dir_name);
234   }
235 
236   return success;
237 }
238 
FileExists(const char * file_name) const239 bool IcingFilesystem::FileExists(const char *file_name) const {
240   bool exists = false;
241   struct stat st;
242   if (stat(file_name, &st) == 0) {
243     exists = S_ISREG(st.st_mode) != 0;
244   } else {
245     if (errno != ENOENT) {
246       ICING_LOG(ERROR) << "Unable to stat file " << file_name << ": "
247                        << strerror(errno);
248     }
249     exists = false;
250   }
251   return exists;
252 }
253 
DirectoryExists(const char * dir_name) const254 bool IcingFilesystem::DirectoryExists(const char *dir_name) const {
255   bool exists = false;
256   struct stat st;
257   if (stat(dir_name, &st) == 0) {
258     exists = S_ISDIR(st.st_mode) != 0;
259   } else {
260     if (errno != ENOENT) {
261       ICING_LOG(ERROR) << "Unable to stat directory " << dir_name << ": "
262                        << strerror(errno);
263     }
264     exists = false;
265   }
266   return exists;
267 }
268 
GetBasenameIndex(const char * file_name) const269 int IcingFilesystem::GetBasenameIndex(const char *file_name) const {
270   // Find final slash.
271   const char *last_slash = strrchr(file_name, '/');
272   if (!last_slash) {
273     // file_name is just basename.
274     return 0;
275   }
276 
277   // Skip slash.
278   return last_slash + 1 - file_name;
279 }
280 
GetBasename(const char * file_name) const281 std::string IcingFilesystem::GetBasename(const char *file_name) const {
282   size_t len = strlen(file_name);
283   int idx = GetBasenameIndex(file_name);
284   return std::string(file_name + idx, len - idx);
285 }
286 
GetDirname(const char * file_name) const287 std::string IcingFilesystem::GetDirname(const char *file_name) const {
288   int idx = GetBasenameIndex(file_name);
289   // Remove the trailing slash
290   if (idx > 0) {
291     idx -= 1;
292   }
293   return std::string(file_name, idx);
294 }
295 
ListDirectory(const char * dir_name,vector<std::string> * entries) const296 bool IcingFilesystem::ListDirectory(const char *dir_name,
297                                     vector<std::string> *entries) const {
298   entries->clear();
299   return ListDirectory(dir_name, /*exclude=*/{}, /*recursive=*/false, entries);
300 }
301 
ListDirectory(const char * dir_name,const std::unordered_set<std::string> & exclude,bool recursive,std::vector<std::string> * entries) const302 bool IcingFilesystem::ListDirectory(
303     const char *dir_name, const std::unordered_set<std::string> &exclude,
304     bool recursive, std::vector<std::string> *entries) const {
305   return ListDirectoryInternal(dir_name, exclude, recursive, /*prefix=*/"",
306                                entries);
307 }
308 
GetMatchingFiles(const char * glob,vector<std::string> * matches) const309 bool IcingFilesystem::GetMatchingFiles(const char *glob,
310                                        vector<std::string> *matches) const {
311   matches->clear();
312 
313   // Split dirname/basename.
314   int basename_idx = GetBasenameIndex(glob);
315   if (basename_idx == 0) {
316     // We need a directory.
317     ICING_VLOG(1) << "Expected directory, no matching files for: " << glob;
318     return true;
319   }
320   const char *basename_glob = glob + basename_idx;
321   std::string dirname(glob, basename_idx);
322   vector<std::string> entries;
323   if (!ListDirectory(dirname.c_str(), &entries) && errno != ENOENT) {
324     return false;
325   }
326 
327   for (vector<std::string>::iterator i = entries.begin(); i != entries.end();
328        ++i) {
329     // The filename needs to match glob following last_slash.
330     if (!fnmatch(basename_glob, i->c_str(), FNM_PATHNAME)) {
331       // Add it to the list.
332       matches->push_back(dirname + *i);
333     }
334   }
335   return true;
336 }
337 
OpenForWrite(const char * file_name) const338 int IcingFilesystem::OpenForWrite(const char *file_name) const {
339   int fd = open(file_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
340   if (fd < 0) {
341     LogOpenError("Opening file ", file_name, " for write failed: ", errno);
342   }
343   return fd;
344 }
345 
OpenForAppend(const char * file_name) const346 int IcingFilesystem::OpenForAppend(const char *file_name) const {
347   // Don't use the O_APPEND flag because, although it opens for
348   // append, it doesn't set the file cursor to at the end until
349   // first write occurs.  This can be confusing if you expect
350   // the file position at the end.  Instead, explicitly
351   // seek to end after opening.
352   int fd = open(file_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
353   if (fd < 0) {
354     LogOpenError("Opening file ", file_name, " for write failed: ", errno);
355   } else {
356     lseek(fd, 0, SEEK_END);
357   }
358   return fd;
359 }
360 
OpenForRead(const char * file_name) const361 int IcingFilesystem::OpenForRead(const char *file_name) const {
362   int fd = open(file_name, O_RDONLY);
363   if (fd < 0) {
364     LogOpenError("Opening file ", file_name, " for read failed: ", errno);
365   }
366   return fd;
367 }
368 
GetFileSize(int fd) const369 uint64_t IcingFilesystem::GetFileSize(int fd) const {
370   struct stat st;
371   uint64_t size = kBadFileSize;
372   if (fstat(fd, &st) < 0) {
373     ICING_LOG(ERROR) << "Unable to stat file: " << strerror(errno);
374   } else {
375     size = st.st_size;
376   }
377   return size;
378 }
379 
GetFileSize(const char * filename) const380 uint64_t IcingFilesystem::GetFileSize(const char *filename) const {
381   struct stat st;
382   uint64_t size = kBadFileSize;
383   if (stat(filename, &st) < 0) {
384     ICING_LOG(ERROR) << "Unable to stat file " << filename << ": "
385                      << strerror(errno);
386   } else {
387     size = st.st_size;
388   }
389   return size;
390 }
391 
Truncate(int fd,uint64_t new_size) const392 bool IcingFilesystem::Truncate(int fd, uint64_t new_size) const {
393   int ret = ftruncate(fd, new_size);
394   if (ret == 0) {
395     lseek(fd, new_size, SEEK_SET);
396   } else {
397     ICING_LOG(ERROR) << "Unable to truncate file: " << strerror(errno);
398   }
399   return (ret == 0);
400 }
401 
Truncate(const char * filename,uint64_t new_size) const402 bool IcingFilesystem::Truncate(const char *filename, uint64_t new_size) const {
403   int fd = OpenForAppend(filename);
404   if (fd == -1) {
405     return false;
406   }
407   bool success = Truncate(fd, new_size);
408   close(fd);
409   return success;
410 }
411 
Grow(int fd,uint64_t new_size) const412 bool IcingFilesystem::Grow(int fd, uint64_t new_size) const {
413   int ret = ftruncate(fd, new_size);
414   if (ret != 0) {
415     ICING_LOG(ERROR) << "Unable to grow file: " << strerror(errno);
416   }
417   return (ret == 0);
418 }
419 
GrowUsingPWrite(int fd,uint64_t new_size) const420 bool IcingFilesystem::GrowUsingPWrite(int fd, uint64_t new_size) const {
421   uint64_t curr_file_size = GetFileSize(fd);
422   if (curr_file_size == kBadFileSize) {
423     return false;
424   }
425   if (new_size <= curr_file_size) {
426     return true;
427   }
428 
429   uint64_t page_size = IcingMMapper::system_page_size();
430   auto buf = std::make_unique<uint8_t[]>(page_size);
431   uint64_t size_to_write = std::min(page_size - (curr_file_size % page_size),
432                                     new_size - curr_file_size);
433   while (size_to_write > 0 && curr_file_size < new_size) {
434     if (!PWrite(fd, curr_file_size, buf.get(), size_to_write)) {
435       ICING_LOG(ERROR) << "Failed to grow file using pwrite.";
436       return false;
437     }
438     curr_file_size += size_to_write;
439     size_to_write = std::min(page_size - (curr_file_size % page_size),
440                              new_size - curr_file_size);
441   }
442   return true;
443 }
444 
Write(int fd,const void * data,size_t data_size) const445 bool IcingFilesystem::Write(int fd, const void *data, size_t data_size) const {
446   size_t write_len = data_size;
447   do {
448     // Don't try to write too much at once.
449     size_t chunk_size = std::min<size_t>(write_len, 64u * 1024);
450     ssize_t wrote = write(fd, data, chunk_size);
451     if (wrote < 0) {
452       ICING_LOG(ERROR) << "Bad write: " << strerror(errno);
453       return false;
454     }
455     data = static_cast<const uint8_t *>(data) + wrote;
456     write_len -= wrote;
457   } while (write_len > 0);
458   return true;
459 }
460 
PWrite(int fd,off_t offset,const void * data,size_t data_size) const461 bool IcingFilesystem::PWrite(int fd, off_t offset, const void *data,
462                              size_t data_size) const {
463   size_t write_len = data_size;
464   do {
465     // Don't try to write too much at once.
466     size_t chunk_size = std::min<size_t>(write_len, 64u * 1024);
467     ssize_t wrote = pwrite(fd, data, chunk_size, offset);
468     if (wrote < 0) {
469       ICING_LOG(ERROR) << "Bad write: " << strerror(errno);
470       return false;
471     }
472     data = static_cast<const uint8_t *>(data) + wrote;
473     write_len -= wrote;
474     offset += wrote;
475   } while (write_len > 0);
476   return true;
477 }
478 
DataSync(int fd) const479 bool IcingFilesystem::DataSync(int fd) const {
480 #ifdef __APPLE__  // iOS has no fdatasync(), only fsync()
481   int result = fsync(fd);
482 #else
483   int result = fdatasync(fd);
484 #endif
485 
486   if (result < 0) {
487     ICING_LOG(ERROR) << "Unable to sync data: " << strerror(errno);
488     return false;
489   }
490   return true;
491 }
492 
RenameFile(const char * old_name,const char * new_name) const493 bool IcingFilesystem::RenameFile(const char *old_name,
494                                  const char *new_name) const {
495   if (rename(old_name, new_name) < 0) {
496     ICING_LOG(ERROR) << "Unable to rename file " << old_name << " to "
497                      << new_name << ": " << strerror(errno);
498     return false;
499   }
500   return true;
501 }
502 
SwapFiles(const char * one,const char * two) const503 bool IcingFilesystem::SwapFiles(const char *one, const char *two) const {
504   std::string tmp_name = absl_ports::StrCat(one, ".tmp");
505   const char *tmp_cstr = tmp_name.c_str();
506 
507   // Blow away a tmp file if it already exists
508   if (FileExists(tmp_cstr) && !DeleteFile(tmp_cstr)) {
509     return false;
510   }
511   if (DirectoryExists(tmp_cstr) && !DeleteDirectoryRecursively(tmp_cstr)) {
512     return false;
513   }
514 
515   // Perform the swap
516   if (!RenameFile(one, tmp_cstr)) {
517     return false;
518   }
519   if (!RenameFile(two, one)) {
520     return false;
521   }
522   if (!RenameFile(tmp_cstr, two)) {
523     return false;
524   }
525 
526   return true;
527 }
528 
CreateDirectory(const char * dir_name) const529 bool IcingFilesystem::CreateDirectory(const char *dir_name) const {
530   bool success = DirectoryExists(dir_name);
531   if (!success) {
532     if (mkdir(dir_name, S_IRUSR | S_IWUSR | S_IXUSR) == 0) {
533       success = true;
534     } else {
535       ICING_LOG(ERROR) << "Creating directory " << dir_name
536                        << " failed: " << strerror(errno);
537     }
538   }
539   return success;
540 }
541 
CreateDirectoryRecursively(const char * dir_name) const542 bool IcingFilesystem::CreateDirectoryRecursively(const char *dir_name) const {
543   if ((strlen(dir_name) == 0) || DirectoryExists(dir_name)) {
544     return true;
545   }
546   std::string path_before = GetDirname(dir_name);
547   if (!CreateDirectoryRecursively(path_before.c_str())) {
548     return false;
549   }
550   return CreateDirectory(dir_name);
551 }
552 
CopyFile(const char * src,const char * dst) const553 bool IcingFilesystem::CopyFile(const char *src, const char *dst) const {
554   bool success = false;
555 
556   int src_fd = -1;
557   int dst_fd = -1;
558   uint64_t size = 0;
559   IcingMMapper mapper(true, MAP_PRIVATE);
560 
561   if ((src_fd = OpenForRead(src)) < 0) {
562     goto end;
563   }
564   if ((dst_fd = OpenForWrite(dst)) < 0) {
565     goto end;
566   }
567   size = GetFileSize(src_fd);
568   mapper.Remap(src_fd, 0, size);
569   if (!mapper.is_valid()) {
570     goto end;
571   }
572   success = Write(dst_fd, mapper.address(), mapper.len());
573 
574 end:
575   if (src_fd > 0) close(src_fd);
576   if (dst_fd > 0) close(dst_fd);
577   if (!success) {
578     ICING_LOG(ERROR) << "Couldn't copy file " << src << " to " << dst;
579   }
580   return success;
581 }
582 
ComputeChecksum(int fd,uint32_t * checksum,uint64_t offset,uint64_t length) const583 bool IcingFilesystem::ComputeChecksum(int fd, uint32_t *checksum,
584                                       uint64_t offset, uint64_t length) const {
585   if (length == 0) {
586     return true;
587   }
588   IcingMMapper mapper(fd, true, offset, length, MAP_PRIVATE);
589   if (!mapper.is_valid()) {
590     return false;
591   }
592   *checksum = adler32(*checksum, mapper.address(), mapper.len());
593   return true;
594 }
595 
GetDiskUsage(int fd) const596 uint64_t IcingFilesystem::GetDiskUsage(int fd) const {
597   struct stat st;
598   if (fstat(fd, &st) < 0) {
599     ICING_LOG(ERROR) << "Unable to stat file: " << strerror(errno);
600     return kBadFileSize;
601   }
602   return st.st_blocks * kStatBlockSize;
603 }
604 
GetFileDiskUsage(const char * path) const605 uint64_t IcingFilesystem::GetFileDiskUsage(const char *path) const {
606   struct stat st;
607   if (stat(path, &st) != 0) {
608     ICING_LOG(ERROR) << "Unable to stat " << path << ": " << strerror(errno);
609     return kBadFileSize;
610   }
611   return st.st_blocks * kStatBlockSize;
612 }
613 
GetDiskUsage(const char * path) const614 uint64_t IcingFilesystem::GetDiskUsage(const char *path) const {
615   struct stat st;
616   if (stat(path, &st) != 0) {
617     ICING_LOG(ERROR) << "Unable to stat " << path << ": " << strerror(errno);
618     return kBadFileSize;
619   }
620   uint64_t result = st.st_blocks * kStatBlockSize;
621   if (S_ISDIR(st.st_mode)) {
622     vector<std::string> list;
623     if (!ListDirectory(path, &list)) {
624       return kBadFileSize;
625     }
626     for (vector<std::string>::iterator i = list.begin(); i != list.end(); ++i) {
627       std::string sub_path = std::string(path) + '/' + *i;
628       uint64_t sub_usage = GetDiskUsage(sub_path.c_str());
629       if (sub_usage != kBadFileSize) {
630         result += sub_usage;
631       }  // Else just ignore the failing entry.
632     }
633   }
634   return result;
635 }
636 
IncrementByOrSetInvalid(uint64_t size,uint64_t * to_increment)637 void IcingFilesystem::IncrementByOrSetInvalid(uint64_t size,
638                                               uint64_t *to_increment) {
639   if (*to_increment == kBadFileSize) {
640     return;
641   }
642   if (size == kBadFileSize) {
643     *to_increment = kBadFileSize;
644     return;
645   }
646   *to_increment += size;
647 }
648 
649 }  // namespace lib
650 }  // namespace icing
651