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