aboutsummaryrefslogtreecommitdiff
path: root/bootctrl/aidl/GptUtils.cpp
blob: 34dec1132312e689948b54dc170ac37c1d50561b (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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/*
 * Copyright (C) 2019 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.
 */

#define LOG_TAG "bootcontrolhal"

#include "GptUtils.h"

#include <android-base/file.h>
#include <errno.h>
#include <linux/fs.h>
#include <log/log.h>
#include <zlib.h>

namespace aidl::android::hardware::boot {

namespace {

static int ValidateGptHeader(gpt_header *gpt) {
    if (gpt->signature != GPT_SIGNATURE) {
        ALOGE("invalid gpt signature 0x%lx\n", gpt->signature);
        return -1;
    }

    if (gpt->header_size != sizeof(gpt_header)) {
        ALOGE("invalid gpt header size %u\n", gpt->header_size);
        return -1;
    }

    if (gpt->entry_size != sizeof(gpt_entry)) {
        ALOGE("invalid gpt entry size %u\n", gpt->entry_size);
        return -1;
    }

    return 0;
}

}  // namespace

GptUtils::GptUtils(const std::string dev_path) : dev_path(dev_path), fd(0) {}

int GptUtils::Load(void) {
    fd = open(dev_path.c_str(), O_RDWR);
    if (fd < 0) {
        ALOGE("failed to open block dev %s, %d\n", dev_path.c_str(), errno);
        return -1;
    }

    int ret = ioctl(fd, BLKSSZGET, &block_size);
    if (ret < 0) {
        ALOGE("failed to get block size %d\n", errno);
        return -1;
    }

    // read primary header
    lseek64(fd, block_size, SEEK_SET);
    ret = read(fd, &gpt_primary, sizeof gpt_primary);
    if (ret < 0) {
        ALOGE("failed to read gpt primary header %d\n", errno);
        return -1;
    }

    if (ValidateGptHeader(&gpt_primary)) {
        ALOGE("error validating gpt header\n");
        return -1;
    }

    // read partition entries
    entry_array.resize(gpt_primary.entry_count);
    uint32_t entries_size = gpt_primary.entry_size * gpt_primary.entry_count;
    lseek64(fd, block_size * gpt_primary.start_lba, SEEK_SET);
    ret = read(fd, entry_array.data(), entries_size);
    if (ret < 0) {
        ALOGE("failed to read gpt partition entries %d\n", errno);
        return -1;
    }

    // read gpt back header
    lseek64(fd, block_size * gpt_primary.backup_lba, SEEK_SET);
    ret = read(fd, &gpt_backup, sizeof gpt_backup);
    if (ret < 0) {
        ALOGE("failed to read gpt backup header %d\n", errno);
        return -1;
    }

    if (ValidateGptHeader(&gpt_backup)) {
        ALOGW("error validating gpt backup\n");  // just warn about it, not fail
    }

    // Create map <partition name, gpt_entry pointer>
    auto get_name = [](const uint16_t *efi_name) {
        char name[37] = {};
        for (size_t i = 0; efi_name[i] && i < sizeof name - 1; ++i) name[i] = efi_name[i];
        return std::string(name);
    };

    for (auto const &e : entry_array) {
        if (e.name[0] == 0)
            break;  // stop at the first partition with no name
        std::string s = get_name(e.name);
        entries[s] = const_cast<gpt_entry *>(&e);
    }

    return 0;
}

gpt_entry *GptUtils::GetPartitionEntry(std::string name) {
    return entries.find(name) != entries.end() ? entries[name] : nullptr;
}

int GptUtils::Sync(void) {
    if (!fd)
        return -1;

    // calculate crc and check if we need to update gpt
    gpt_primary.entries_crc32 = crc32(0, reinterpret_cast<uint8_t *>(entry_array.data()),
                                      entry_array.size() * sizeof(gpt_entry));

    // save old crc
    uint32_t crc = gpt_primary.crc32;
    gpt_primary.crc32 = 0;

    gpt_primary.crc32 = crc32(0, reinterpret_cast<uint8_t *>(&gpt_primary), sizeof gpt_primary);
    if (crc == gpt_primary.crc32)
        return 0;  // nothing to do (no changes)

    ALOGI("updating GPT\n");

    lseek64(fd, block_size * gpt_primary.current_lba, SEEK_SET);
    int ret = write(fd, &gpt_primary, sizeof gpt_primary);
    if (ret < 0) {
        ALOGE("failed to write gpt primary header %d\n", errno);
        return -1;
    }

    lseek64(fd, block_size * gpt_primary.start_lba, SEEK_SET);
    ret = write(fd, entry_array.data(), entry_array.size() * sizeof(gpt_entry));
    if (ret < 0) {
        ALOGE("failed to write gpt partition entries %d\n", errno);
        return -1;
    }

    // update GPT backup entries and backup
    lseek64(fd, block_size * gpt_backup.start_lba, SEEK_SET);
    ret = write(fd, entry_array.data(), entry_array.size() * sizeof(gpt_entry));
    if (ret < 0) {
        ALOGE("failed to write gpt backup partition entries %d\n", errno);
        return -1;
    }

    gpt_backup.entries_crc32 = gpt_primary.entries_crc32;
    gpt_backup.crc32 = 0;
    gpt_backup.crc32 = crc32(0, reinterpret_cast<uint8_t *>(&gpt_backup), sizeof gpt_backup);
    lseek64(fd, block_size * gpt_primary.backup_lba, SEEK_SET);
    ret = write(fd, &gpt_backup, sizeof gpt_backup);
    if (ret < 0) {
        ALOGE("failed to write gpt backup header %d\n", errno);
        return -1;
    }

    fsync(fd);

    return 0;
}

GptUtils::~GptUtils() {
    if (fd) {
        Sync();
        close(fd);
    }
}

}  // namespace aidl::android::hardware::boot