static int _delete_dir_contents(DIR *d, int (*exclusion_predicate)(const char *name, const int is_dir)) { int result = 0; struct dirent *de; int dfd; dfd = dirfd(d); if (dfd < 0) return -1; while ((de = readdir(d))) { const char *name = de->d_name; /* check using the exclusion predicate, if provided */ if (exclusion_predicate && exclusion_predicate(name, (de->d_type == DT_DIR))) { continue; } if (de->d_type == DT_DIR) { int r, subfd; DIR *subdir; /* always skip "." and ".." */ if (name[0] == '.') { if (name[1] == 0) continue; if ((name[1] == '.') && (name[2] == 0)) continue; } subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY); if (subfd < 0) { ALOGE("Couldn't openat %s: %s\n", name, strerror(errno)); result = -1; continue; } subdir = fdopendir(subfd); if (subdir == NULL) { ALOGE("Couldn't fdopendir %s: %s\n", name, strerror(errno)); close(subfd); result = -1; continue; } if (_delete_dir_contents(subdir, exclusion_predicate)) { result = -1; } closedir(subdir); if (unlinkat(dfd, name, AT_REMOVEDIR) < 0) { ALOGE("Couldn't unlinkat %s: %s\n", name, strerror(errno)); result = -1; } } else { if (unlinkat(dfd, name, 0) < 0) { ALOGE("Couldn't unlinkat %s: %s\n", name, strerror(errno)); result = -1; } } } return result; }
int delete_dir_contents_fd(int dfd, const char *name) { int fd, res; DIR *d; fd = openat(dfd, name, O_RDONLY | O_DIRECTORY); if (fd < 0) { ALOGE("Couldn't openat %s: %s\n", name, strerror(errno)); return -1; } d = fdopendir(fd); if (d == NULL) { ALOGE("Couldn't fdopendir %s: %s\n", name, strerror(errno)); close(fd); return -1; } res = _delete_dir_contents(d, 0); closedir(d); return res; }
int delete_dir_contents(const char *pathname, int also_delete_dir, const char *ignore) { int res = 0; DIR *d; d = opendir(pathname); if (d == NULL) { ALOGE("Couldn't opendir %s: %s\n", pathname, strerror(errno)); return -errno; } res = _delete_dir_contents(d, ignore); closedir(d); if (also_delete_dir) { if (rmdir(pathname)) { ALOGE("Couldn't rmdir %s: %s\n", pathname, strerror(errno)); res = -1; } } return res; }
static int _add_cache_files(cache_t *cache, cache_dir_t *parentDir, const char *dirName, DIR* dir, char *pathBase, char *pathPos, size_t pathAvailLen) { struct dirent *de; cache_dir_t* cacheDir = NULL; int dfd; CACHE_NOISY(ALOGI("_add_cache_files: parent=%p dirName=%s dir=%p pathBase=%s", parentDir, dirName, dir, pathBase)); dfd = dirfd(dir); if (dfd < 0) return 0; // Sub-directories always get added to the data structure, so if they // are empty we will know about them to delete them later. cacheDir = _add_cache_dir_t(cache, parentDir, dirName); while ((de = readdir(dir))) { const char *name = de->d_name; if (de->d_type == DT_DIR) { int subfd; DIR *subdir; /* always skip "." and ".." */ if (name[0] == '.') { if (name[1] == 0) continue; if ((name[1] == '.') && (name[2] == 0)) continue; } subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY); if (subfd < 0) { ALOGE("Couldn't openat %s: %s\n", name, strerror(errno)); continue; } subdir = fdopendir(subfd); if (subdir == NULL) { ALOGE("Couldn't fdopendir %s: %s\n", name, strerror(errno)); close(subfd); continue; } if (cacheDir == NULL) { cacheDir = _add_cache_dir_t(cache, parentDir, dirName); } if (cacheDir != NULL) { // Update pathBase for the new path... this may change dirName // if that is also pointing to the path, but we are done with it // now. size_t finallen = snprintf(pathPos, pathAvailLen, "/%s", name); CACHE_NOISY(ALOGI("Collecting dir %s\n", pathBase)); if (finallen < pathAvailLen) { _add_cache_files(cache, cacheDir, name, subdir, pathBase, pathPos+finallen, pathAvailLen-finallen); } else { // Whoops, the final path is too long! We'll just delete // this directory. ALOGW("Cache dir %s truncated in path %s; deleting dir\n", name, pathBase); _delete_dir_contents(subdir, NULL); if (unlinkat(dfd, name, AT_REMOVEDIR) < 0) { ALOGE("Couldn't unlinkat %s: %s\n", name, strerror(errno)); } } } closedir(subdir); } else if (de->d_type == DT_REG) { // Skip files that start with '.'; they will be deleted if // their entire directory is deleted. This allows for metadata // like ".nomedia" to remain in the directory until the entire // directory is deleted. if (cacheDir == NULL) { cacheDir = _add_cache_dir_t(cache, parentDir, dirName); } if (name[0] == '.') { cacheDir->hiddenCount++; continue; } if (cacheDir != NULL) { // Build final full path for file... this may change dirName // if that is also pointing to the path, but we are done with it // now. size_t finallen = snprintf(pathPos, pathAvailLen, "/%s", name); CACHE_NOISY(ALOGI("Collecting file %s\n", pathBase)); if (finallen < pathAvailLen) { struct stat s; if (stat(pathBase, &s) >= 0) { _add_cache_file_t(cache, cacheDir, s.st_mtime, name); } else { ALOGW("Unable to stat cache file %s; deleting\n", pathBase); if (unlink(pathBase) < 0) { ALOGE("Couldn't unlink %s: %s\n", pathBase, strerror(errno)); } } } else { // Whoops, the final path is too long! We'll just delete // this file. ALOGW("Cache file %s truncated in path %s; deleting\n", name, pathBase); if (unlinkat(dfd, name, 0) < 0) { *pathPos = 0; ALOGE("Couldn't unlinkat %s in %s: %s\n", name, pathBase, strerror(errno)); } } } } else { cacheDir->hiddenCount++; } } return 0; }