1 #include <errno.h>
2 #include <libgen.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <sys/stat.h>
7 #include <sys/statfs.h>
8 #include <unistd.h>
9 #include <dirent.h>
10 #include <ctype.h>
11
12 #include "applypatch.h"
13
EliminateOpenFiles(char ** files,int file_count)14 static int EliminateOpenFiles(char** files, int file_count) {
15 DIR* d;
16 struct dirent* de;
17 d = opendir("/proc");
18 if (d == NULL) {
19 fprintf(stderr, "error opening /proc: %s\n", strerror(errno));
20 return -1;
21 }
22 while ((de = readdir(d)) != 0) {
23 int i;
24 for (i = 0; de->d_name[i] != '\0' && isdigit(de->d_name[i]); ++i);
25 if (de->d_name[i]) continue;
26
27 // de->d_name[i] is numeric
28
29 char path[FILENAME_MAX];
30 strcpy(path, "/proc/");
31 strcat(path, de->d_name);
32 strcat(path, "/fd/");
33
34 DIR* fdd;
35 struct dirent* fdde;
36 fdd = opendir(path);
37 if (fdd == NULL) {
38 fprintf(stderr, "error opening %s: %s\n", path, strerror(errno));
39 continue;
40 }
41 while ((fdde = readdir(fdd)) != 0) {
42 char fd_path[FILENAME_MAX];
43 char link[FILENAME_MAX];
44 strcpy(fd_path, path);
45 strcat(fd_path, fdde->d_name);
46
47 int count;
48 count = readlink(fd_path, link, sizeof(link)-1);
49 if (count >= 0) {
50 link[count] = '\0';
51
52 // This is inefficient, but it should only matter if there are
53 // lots of files in /cache, and lots of them are open (neither
54 // of which should be true, especially in recovery).
55 if (strncmp(link, "/cache/", 7) == 0) {
56 int j;
57 for (j = 0; j < file_count; ++j) {
58 if (files[j] && strcmp(files[j], link) == 0) {
59 printf("%s is open by %s\n", link, de->d_name);
60 free(files[j]);
61 files[j] = NULL;
62 }
63 }
64 }
65 }
66 }
67 closedir(fdd);
68 }
69 closedir(d);
70
71 return 0;
72 }
73
FindExpendableFiles(char *** names,int * entries)74 int FindExpendableFiles(char*** names, int* entries) {
75 DIR* d;
76 struct dirent* de;
77 int size = 32;
78 *entries = 0;
79 *names = malloc(size * sizeof(char*));
80
81 char path[FILENAME_MAX];
82
83 // We're allowed to delete unopened regular files in any of these
84 // directories.
85 const char* dirs[2] = {"/cache", "/cache/recovery/otatest"};
86
87 unsigned int i;
88 for (i = 0; i < sizeof(dirs)/sizeof(dirs[0]); ++i) {
89 d = opendir(dirs[i]);
90 if (d == NULL) {
91 fprintf(stderr, "error opening %s: %s\n", dirs[i], strerror(errno));
92 continue;
93 }
94
95 // Look for regular files in the directory (not in any subdirectories).
96 while ((de = readdir(d)) != 0) {
97 strcpy(path, dirs[i]);
98 strcat(path, "/");
99 strcat(path, de->d_name);
100
101 // We can't delete CACHE_TEMP_SOURCE; if it's there we might have
102 // restarted during installation and could be depending on it to
103 // be there.
104 if (strcmp(path, CACHE_TEMP_SOURCE) == 0) continue;
105
106 struct stat st;
107 if (stat(path, &st) == 0 && S_ISREG(st.st_mode)) {
108 if (*entries >= size) {
109 size *= 2;
110 *names = realloc(*names, size * sizeof(char*));
111 }
112 (*names)[(*entries)++] = strdup(path);
113 }
114 }
115
116 closedir(d);
117 }
118
119 printf("%d regular files in deletable directories\n", *entries);
120
121 if (EliminateOpenFiles(*names, *entries) < 0) {
122 return -1;
123 }
124
125 return 0;
126 }
127
MakeFreeSpaceOnCache(size_t bytes_needed)128 int MakeFreeSpaceOnCache(size_t bytes_needed) {
129 size_t free_now = FreeSpaceForFile("/cache");
130 printf("%ld bytes free on /cache (%ld needed)\n",
131 (long)free_now, (long)bytes_needed);
132
133 if (free_now >= bytes_needed) {
134 return 0;
135 }
136
137 char** names;
138 int entries;
139
140 if (FindExpendableFiles(&names, &entries) < 0) {
141 return -1;
142 }
143
144 if (entries == 0) {
145 // nothing we can delete to free up space!
146 fprintf(stderr, "no files can be deleted to free space on /cache\n");
147 return -1;
148 }
149
150 // We could try to be smarter about which files to delete: the
151 // biggest ones? the smallest ones that will free up enough space?
152 // the oldest? the newest?
153 //
154 // Instead, we'll be dumb.
155
156 int i;
157 for (i = 0; i < entries && free_now < bytes_needed; ++i) {
158 if (names[i]) {
159 unlink(names[i]);
160 free_now = FreeSpaceForFile("/cache");
161 printf("deleted %s; now %ld bytes free\n", names[i], (long)free_now);
162 free(names[i]);
163 }
164 }
165
166 for (; i < entries; ++i) {
167 free(names[i]);
168 }
169 free(names);
170
171 return (free_now >= bytes_needed) ? 0 : -1;
172 }
173