#include <errno.h> #include <libgen.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/statfs.h> #include <unistd.h> #include <dirent.h> #include <ctype.h> #include "applypatch.h" static int EliminateOpenFiles(char** files, int file_count) { DIR* d; struct dirent* de; d = opendir("/proc"); if (d == NULL) { fprintf(stderr, "error opening /proc: %s\n", strerror(errno)); return -1; } while ((de = readdir(d)) != 0) { int i; for (i = 0; de->d_name[i] != '\0' && isdigit(de->d_name[i]); ++i); if (de->d_name[i]) continue; // de->d_name[i] is numeric char path[FILENAME_MAX]; strcpy(path, "/proc/"); strcat(path, de->d_name); strcat(path, "/fd/"); DIR* fdd; struct dirent* fdde; fdd = opendir(path); if (fdd == NULL) { fprintf(stderr, "error opening %s: %s\n", path, strerror(errno)); continue; } while ((fdde = readdir(fdd)) != 0) { char fd_path[FILENAME_MAX]; char link[FILENAME_MAX]; strcpy(fd_path, path); strcat(fd_path, fdde->d_name); int count; count = readlink(fd_path, link, sizeof(link)-1); if (count >= 0) { link[count] = '\0'; // This is inefficient, but it should only matter if there are // lots of files in /cache, and lots of them are open (neither // of which should be true, especially in recovery). if (strncmp(link, "/cache/", 7) == 0) { int j; for (j = 0; j < file_count; ++j) { if (files[j] && strcmp(files[j], link) == 0) { printf("%s is open by %s\n", link, de->d_name); free(files[j]); files[j] = NULL; } } } } } closedir(fdd); } closedir(d); return 0; } int FindExpendableFiles(char*** names, int* entries) { DIR* d; struct dirent* de; int size = 32; *entries = 0; *names = malloc(size * sizeof(char*)); char path[FILENAME_MAX]; // We're allowed to delete unopened regular files in any of these // directories. const char* dirs[2] = {"/cache", "/cache/recovery/otatest"}; unsigned int i; for (i = 0; i < sizeof(dirs)/sizeof(dirs[0]); ++i) { d = opendir(dirs[i]); if (d == NULL) { fprintf(stderr, "error opening %s: %s\n", dirs[i], strerror(errno)); continue; } // Look for regular files in the directory (not in any subdirectories). while ((de = readdir(d)) != 0) { strcpy(path, dirs[i]); strcat(path, "/"); strcat(path, de->d_name); // We can't delete CACHE_TEMP_SOURCE; if it's there we might have // restarted during installation and could be depending on it to // be there. if (strcmp(path, CACHE_TEMP_SOURCE) == 0) continue; struct stat st; if (stat(path, &st) == 0 && S_ISREG(st.st_mode)) { if (*entries >= size) { size *= 2; *names = realloc(*names, size * sizeof(char*)); } (*names)[(*entries)++] = strdup(path); } } closedir(d); } printf("%d regular files in deletable directories\n", *entries); if (EliminateOpenFiles(*names, *entries) < 0) { return -1; } return 0; } int MakeFreeSpaceOnCache(size_t bytes_needed) { size_t free_now = FreeSpaceForFile("/cache"); printf("%ld bytes free on /cache (%ld needed)\n", (long)free_now, (long)bytes_needed); if (free_now >= bytes_needed) { return 0; } char** names; int entries; if (FindExpendableFiles(&names, &entries) < 0) { return -1; } if (entries == 0) { // nothing we can delete to free up space! fprintf(stderr, "no files can be deleted to free space on /cache\n"); return -1; } // We could try to be smarter about which files to delete: the // biggest ones? the smallest ones that will free up enough space? // the oldest? the newest? // // Instead, we'll be dumb. int i; for (i = 0; i < entries && free_now < bytes_needed; ++i) { if (names[i]) { unlink(names[i]); free_now = FreeSpaceForFile("/cache"); printf("deleted %s; now %ld bytes free\n", names[i], (long)free_now); free(names[i]); } } for (; i < entries; ++i) { free(names[i]); } free(names); return (free_now >= bytes_needed) ? 0 : -1; }