There were several memory leaks inside jfsck(), they've probably been there for a...
[libjio.git] / check.c
blobf0ce63e9710604cdfd0fd60b5b0120f0bc6cc680
2 /*
3 * libjio - A library for Journaled I/O
4 * Alberto Bertogli (albertogli@telpin.com.ar)
6 * Recovery functions
7 */
9 #include <sys/types.h>
10 #include <sys/stat.h>
11 #include <fcntl.h>
12 #include <unistd.h>
13 #include <stdlib.h>
14 #include <limits.h>
15 #include <string.h>
16 #include <stdio.h>
17 #include <dirent.h>
18 #include <errno.h>
19 #include <sys/mman.h>
21 #include "libjio.h"
22 #include "common.h"
25 /* fill a transaction structure from a mmapped transaction file */
26 static int fill_trans(unsigned char *map, off_t len, struct jtrans *ts)
28 int i;
29 unsigned char *p;
30 struct joper *op, *tmp;
32 if (len < J_DISKHEADSIZE)
33 return 0;
35 p = map;
37 ts->id = *( (uint32_t *) p);
38 p += 4;
40 ts->flags = *( (uint32_t *) p);
41 p += 4;
43 ts->numops = *( (uint32_t *) p);
44 p += 4;
46 for (i = 0; i < ts->numops; i++) {
47 if (len < (p - map) + J_DISKOPHEADSIZE)
48 goto error;
50 op = malloc(sizeof(struct joper));
51 if (op == NULL)
52 goto error;
54 op->len = *( (uint32_t *) p);
55 p += 4;
57 op->plen = *( (uint32_t *) p);
58 p += 4;
60 op->offset = *( (uint64_t *) p);
61 p += 8;
63 if (len < (p - map) + op->len)
64 goto error;
66 op->buf = (void *) p;
67 p += op->len;
69 op->pdata = NULL;
71 if (ts->op == NULL) {
72 ts->op = op;
73 op->prev = NULL;
74 op->next = NULL;
75 } else {
76 for(tmp = ts->op; tmp->next != NULL; tmp = tmp->next)
78 tmp->next = op;
79 op->prev = tmp;
80 op->next = NULL;
84 return 1;
86 error:
87 while (ts->op != NULL) {
88 tmp = ts->op->next;
89 free(ts->op);
90 ts->op = tmp;
92 return 0;
95 /* check the journal and rollback incomplete transactions */
96 int jfsck(const char *name, struct jfsck_result *res)
98 int fd, tfd, rv, i, ret;
99 unsigned int maxtid;
100 uint32_t csum1, csum2;
101 char jdir[PATH_MAX], jlockfile[PATH_MAX], tname[PATH_MAX];
102 struct stat sinfo;
103 struct jfs fs;
104 struct jtrans *curts;
105 struct joper *tmpop;
106 DIR *dir;
107 struct dirent *dent;
108 unsigned char *map;
109 off_t filelen;
111 fd = tfd = -1;
112 filelen = 0;
113 dir = NULL;
114 fs.jmap = NULL;
115 map = NULL;
116 ret = 0;
118 fd = open(name, O_RDWR | O_SYNC | O_LARGEFILE);
119 if (fd < 0) {
120 ret = J_ENOENT;
121 goto exit;
124 fs.fd = fd;
125 fs.name = (char *) name;
127 if (!get_jdir(name, jdir)) {
128 ret = J_ENOMEM;
129 goto exit;
131 rv = lstat(jdir, &sinfo);
132 if (rv < 0 || !S_ISDIR(sinfo.st_mode)) {
133 ret = J_ENOJOURNAL;
134 goto exit;
137 fs.jdirfd = open(jdir, O_RDONLY);
138 if (fs.jdirfd < 0) {
139 ret = J_ENOJOURNAL;
140 goto exit;
143 /* open the lock file, which is only used to complete the jfs
144 * structure */
145 snprintf(jlockfile, PATH_MAX, "%s/%s", jdir, "lock");
146 rv = open(jlockfile, O_RDWR | O_CREAT, 0600);
147 if (rv < 0) {
148 ret = J_ENOJOURNAL;
149 goto exit;
151 fs.jfd = rv;
153 fs.jmap = (unsigned int *) mmap(NULL, sizeof(unsigned int),
154 PROT_READ | PROT_WRITE, MAP_SHARED, fs.jfd, 0);
155 if (fs.jmap == MAP_FAILED) {
156 ret = J_ENOJOURNAL;
157 fs.jmap = NULL;
158 goto exit;
161 dir = opendir(jdir);
162 if (dir == NULL) {
163 ret = J_ENOJOURNAL;
164 goto exit;
167 /* loop for each file in the journal directory to find out the greater
168 * transaction number */
169 maxtid = 0;
170 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
171 /* see if the file is named like a transaction, ignore
172 * otherwise; as transactions are named as numbers > 0, a
173 * simple atoi() is enough testing */
174 rv = atoi(dent->d_name);
175 if (rv <= 0)
176 continue;
177 if (rv > maxtid)
178 maxtid = rv;
181 /* rewrite the lockfile, writing the new maxtid on it, so that when we
182 * rollback a transaction it doesn't step over existing ones */
183 rv = spwrite(fs.jfd, &maxtid, sizeof(maxtid), 0);
184 if (rv != sizeof(maxtid)) {
185 ret = J_ENOMEM;
186 goto exit;
189 /* we loop all the way up to the max transaction id */
190 for (i = 1; i <= maxtid; i++) {
191 curts = malloc(sizeof(struct jtrans));
192 if (curts == NULL) {
193 ret = J_ENOMEM;
194 goto exit;
197 jtrans_init(&fs, curts);
198 curts->id = i;
200 /* open the transaction file, using i as its name, so we are
201 * really looping in order (recovering transaction in a
202 * different order as they were applied means instant
203 * corruption) */
204 if (!get_jtfile(name, i, tname)) {
205 ret = J_ENOMEM;
206 goto exit;
208 tfd = open(tname, O_RDWR | O_SYNC | O_LARGEFILE, 0600);
209 if (tfd < 0) {
210 res->invalid++;
211 goto loop;
214 /* try to lock the transaction file, if it's locked then it is
215 * currently being used so we skip it */
216 rv = plockf(tfd, F_TLOCKW, 0, 0);
217 if (rv == -1) {
218 res->in_progress++;
219 goto loop;
222 filelen = lseek(tfd, 0, SEEK_END);
223 map = mmap(0, filelen, PROT_READ, MAP_SHARED, tfd, 0);
224 if (map == MAP_FAILED) {
225 res->broken++;
226 map = NULL;
227 goto loop;
229 rv = fill_trans(map, filelen, curts);
230 if (rv != 1) {
231 res->broken++;
232 goto loop;
235 /* verify the checksum */
236 csum1 = checksum_map(map, filelen - (sizeof(uint32_t)));
237 csum2 = * (uint32_t *) (map + filelen - (sizeof(uint32_t)));
238 if (csum1 != csum2) {
239 res->corrupt++;
240 goto loop;
243 /* remove flags from the transaction */
244 curts->flags = 0;
246 rv = jtrans_commit(curts);
248 if (rv < 0) {
249 res->apply_error++;
250 goto loop;
252 res->reapplied++;
254 loop:
255 if (tfd >= 0) {
256 close(tfd);
257 tfd = -1;
259 if (map != NULL)
260 munmap(map, filelen);
262 if (curts->name)
263 free(curts->name);
264 while (curts->op != NULL) {
265 tmpop = curts->op->next;
266 if (curts->op->pdata)
267 free(curts->op->pdata);
268 free(curts->op);
269 curts->op = tmpop;
271 pthread_mutex_destroy(&(curts->lock));
272 free(curts);
274 res->total++;
277 exit:
278 if (fs.fd >= 0)
279 close(fs.fd);
280 if (fs.jfd >= 0)
281 close(fs.jfd);
282 if (fs.jdirfd >= 0)
283 close(fs.jdirfd);
284 if (dir != NULL)
285 closedir(dir);
286 if (fs.jmap != NULL)
287 munmap(fs.jmap, sizeof(unsigned int));
289 return ret;
293 /* remove all the files in the journal directory (if any) */
294 int jfsck_cleanup(const char *name)
296 char jdir[PATH_MAX], tfile[PATH_MAX*3];
297 DIR *dir;
298 struct dirent *dent;
300 if (!get_jdir(name, jdir))
301 return 0;
303 dir = opendir(jdir);
304 if (dir == NULL && errno == ENOENT)
305 /* it doesn't exist, so it's clean */
306 return 1;
307 else if (dir == NULL)
308 return 0;
310 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
311 /* we only care about transactions (named as numbers > 0) and
312 * the lockfile (named "lock"); ignore everything else */
313 if (strcmp(dent->d_name, "lock") && atoi(dent->d_name) <= 0)
314 continue;
316 /* build the full path to the transaction file */
317 memset(tfile, 0, PATH_MAX * 3);
318 strcat(tfile, jdir);
319 strcat(tfile, "/");
320 strcat(tfile, dent->d_name);
322 /* the full filename is too large */
323 if (strlen(tfile) > PATH_MAX) {
324 closedir(dir);
325 return 0;
328 /* and remove it */
329 unlink(tfile);
331 closedir(dir);
333 rmdir(jdir);
335 return 1;