aboutsummaryrefslogtreecommitdiffstats
path: root/tools/ota/check-lost+found.c
blob: f85627544cc514c9f0871b9b69d7d6a2bbe8b6d2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/klog.h>
#include <sys/reboot.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>

#include "private/android_filesystem_config.h"

// Sentinel file used to track whether we've forced a reboot
static const char *kMarkerFile = "/data/misc/check-lost+found-rebooted-2";

// Output file in tombstones directory (first 8K will be uploaded)
static const char *kOutputDir = "/data/tombstones";
static const char *kOutputFile = "/data/tombstones/check-lost+found-log";

// Partitions to check
static const char *kPartitions[] = { "/system", "/data", "/cache", NULL };

/*
 * 1. If /data/misc/forced-reboot is missing, touch it & force "unclean" boot.
 * 2. Write a log entry with the number of files in lost+found directories.
 */

int main(int argc, char **argv) {
    mkdir(kOutputDir, 0755);
    chown(kOutputDir, AID_SYSTEM, AID_SYSTEM);
    FILE *out = fopen(kOutputFile, "a");
    if (out == NULL) {
        fprintf(stderr, "Can't write %s: %s\n", kOutputFile, strerror(errno));
        return 1;
    }

    // Note: only the first 8K of log will be uploaded, so be terse.
    time_t start = time(NULL);
    fprintf(out, "*** check-lost+found ***\nStarted: %s", ctime(&start));

    struct stat st;
    if (stat(kMarkerFile, &st)) {
        // No reboot marker -- need to force an unclean reboot.
        // But first, try to create the marker file.  If that fails,
        // skip the reboot, so we don't get caught in an infinite loop.

        int fd = open(kMarkerFile, O_WRONLY|O_CREAT, 0444);
        if (fd >= 0 && close(fd) == 0) {
            fprintf(out, "Wrote %s, rebooting\n", kMarkerFile);
            fflush(out);
            sync();  // Make sure the marker file is committed to disk

            // If possible, dirty each of these partitions before rebooting,
            // to make sure the filesystem has to do a scan on mount.
            int i;
            for (i = 0; kPartitions[i] != NULL; ++i) {
                char fn[PATH_MAX];
                snprintf(fn, sizeof(fn), "%s/%s", kPartitions[i], "dirty");
                fd = open(fn, O_WRONLY|O_CREAT, 0444);
                if (fd >= 0) {  // Don't sweat it if we can't write the file.
                    write(fd, fn, sizeof(fn));  // write, you know, some data
                    close(fd);
                    unlink(fn);
                }
            }

            reboot(RB_AUTOBOOT);  // reboot immediately, with dirty filesystems
            fprintf(out, "Reboot failed?!\n");
            exit(1);
        } else {
            fprintf(out, "Can't write %s: %s\n", kMarkerFile, strerror(errno));
        }
    } else {
        fprintf(out, "Found %s\n", kMarkerFile);
    }

    int i;
    for (i = 0; kPartitions[i] != NULL; ++i) {
        char fn[PATH_MAX];
        snprintf(fn, sizeof(fn), "%s/%s", kPartitions[i], "lost+found");
        DIR *dir = opendir(fn);
        if (dir == NULL) {
            fprintf(out, "Can't open %s: %s\n", fn, strerror(errno));
        } else {
            int count = 0;
            struct dirent *ent;
            while ((ent = readdir(dir))) {
                if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, ".."))
                    ++count;
            }
            closedir(dir);
            if (count > 0) {
                fprintf(out, "OMGZ FOUND %d FILES IN %s\n", count, fn);
            } else {
                fprintf(out, "%s is clean\n", fn);
            }
        }
    }

    char dmesg[131073];
    int len = klogctl(KLOG_READ_ALL, dmesg, sizeof(dmesg) - 1);
    if (len < 0) {
        fprintf(out, "Can't read kernel log: %s\n", strerror(errno));
    } else {  // To conserve space, only write lines with certain keywords
        fprintf(out, "--- Kernel log ---\n");
        dmesg[len] = '\0';
        char *saveptr, *line;
        int in_yaffs = 0;
        for (line = strtok_r(dmesg, "\n", &saveptr); line != NULL;
             line = strtok_r(NULL, "\n", &saveptr)) {
            if (strstr(line, "yaffs: dev is")) in_yaffs = 1;

            if (in_yaffs ||
                    strstr(line, "yaffs") ||
                    strstr(line, "mtd") ||
                    strstr(line, "msm_nand")) {
                fprintf(out, "%s\n", line);
            }

            if (strstr(line, "yaffs_read_super: isCheckpointed")) in_yaffs = 0;
        }
    }

    return 0;
}