1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright 2018 Google LLC
4 */
5 #include <dirent.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <poll.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13
14 #include <sys/ioctl.h>
15 #include <sys/mount.h>
16 #include <sys/stat.h>
17 #include <sys/types.h>
18
19 #include <openssl/sha.h>
20 #include <openssl/md5.h>
21
22 #include "utils.h"
23
24 #ifndef __S_IFREG
25 #define __S_IFREG S_IFREG
26 #endif
27
rnd(unsigned int max,unsigned int * seed)28 unsigned int rnd(unsigned int max, unsigned int *seed)
29 {
30 return rand_r(seed) * ((uint64_t)max + 1) / RAND_MAX;
31 }
32
remove_dir(const char * dir)33 int remove_dir(const char *dir)
34 {
35 int err = rmdir(dir);
36
37 if (err && errno == ENOTEMPTY) {
38 err = delete_dir_tree(dir);
39 if (err)
40 return err;
41 return 0;
42 }
43
44 if (err && errno != ENOENT)
45 return -errno;
46
47 return 0;
48 }
49
drop_caches(void)50 int drop_caches(void)
51 {
52 int drop_caches =
53 open("/proc/sys/vm/drop_caches", O_WRONLY | O_CLOEXEC);
54 int i;
55
56 if (drop_caches == -1)
57 return -errno;
58 i = write(drop_caches, "3", 1);
59 close(drop_caches);
60
61 if (i != 1)
62 return -errno;
63
64 return 0;
65 }
66
mount_fs(const char * mount_dir,const char * backing_dir,int read_timeout_ms)67 int mount_fs(const char *mount_dir, const char *backing_dir,
68 int read_timeout_ms)
69 {
70 static const char fs_name[] = INCFS_NAME;
71 char mount_options[512];
72 int result;
73
74 snprintf(mount_options, ARRAY_SIZE(mount_options),
75 "read_timeout_ms=%u",
76 read_timeout_ms);
77
78 result = mount(backing_dir, mount_dir, fs_name, 0, mount_options);
79 if (result != 0)
80 perror("Error mounting fs.");
81 return result;
82 }
83
mount_fs_opt(const char * mount_dir,const char * backing_dir,const char * opt,bool remount)84 int mount_fs_opt(const char *mount_dir, const char *backing_dir,
85 const char *opt, bool remount)
86 {
87 static const char fs_name[] = INCFS_NAME;
88 int result;
89
90 result = mount(backing_dir, mount_dir, fs_name,
91 remount ? MS_REMOUNT : 0, opt);
92 if (result != 0)
93 perror("Error mounting fs.");
94 return result;
95 }
96
97 struct hash_section {
98 uint32_t algorithm;
99 uint8_t log2_blocksize;
100 uint32_t salt_size;
101 /* no salt */
102 uint32_t hash_size;
103 uint8_t hash[SHA256_DIGEST_SIZE];
104 } __packed;
105
106 struct signature_blob {
107 uint32_t version;
108 uint32_t hash_section_size;
109 struct hash_section hash_section;
110 uint32_t signing_section_size;
111 uint8_t signing_section[];
112 } __packed;
113
format_signature(void ** buf,const char * root_hash,const char * add_data)114 size_t format_signature(void **buf, const char *root_hash, const char *add_data)
115 {
116 size_t size = sizeof(struct signature_blob) + strlen(add_data) + 1;
117 struct signature_blob *sb = malloc(size);
118
119 if (!sb)
120 return 0;
121
122 *sb = (struct signature_blob){
123 .version = INCFS_SIGNATURE_VERSION,
124 .hash_section_size = sizeof(struct hash_section),
125 .hash_section =
126 (struct hash_section){
127 .algorithm = INCFS_HASH_TREE_SHA256,
128 .log2_blocksize = 12,
129 .salt_size = 0,
130 .hash_size = SHA256_DIGEST_SIZE,
131 },
132 .signing_section_size = strlen(add_data) + 1,
133 };
134
135 memcpy(sb->hash_section.hash, root_hash, SHA256_DIGEST_SIZE);
136 memcpy((char *)sb->signing_section, add_data, strlen(add_data) + 1);
137 *buf = sb;
138 return size;
139 }
140
crypto_emit_file(int fd,const char * dir,const char * filename,incfs_uuid_t * id_out,size_t size,const char * root_hash,const char * add_data)141 int crypto_emit_file(int fd, const char *dir, const char *filename,
142 incfs_uuid_t *id_out, size_t size, const char *root_hash,
143 const char *add_data)
144 {
145 int mode = __S_IFREG | 0555;
146 void *signature;
147 int error = 0;
148
149 struct incfs_new_file_args args = {
150 .size = size,
151 .mode = mode,
152 .file_name = ptr_to_u64(filename),
153 .directory_path = ptr_to_u64(dir),
154 .file_attr = 0,
155 .file_attr_len = 0
156 };
157
158 args.signature_size = format_signature(&signature, root_hash, add_data);
159 args.signature_info = ptr_to_u64(signature);
160
161 md5(filename, strlen(filename), (char *)args.file_id.bytes);
162
163 if (ioctl(fd, INCFS_IOC_CREATE_FILE, &args) != 0) {
164 error = -errno;
165 goto out;
166 }
167
168 *id_out = args.file_id;
169
170 out:
171 free(signature);
172 return error;
173 }
174
emit_file(int fd,const char * dir,const char * filename,incfs_uuid_t * id_out,size_t size,const char * attr)175 int emit_file(int fd, const char *dir, const char *filename,
176 incfs_uuid_t *id_out, size_t size, const char *attr)
177 {
178 int mode = __S_IFREG | 0555;
179 struct incfs_new_file_args args = { .size = size,
180 .mode = mode,
181 .file_name = ptr_to_u64(filename),
182 .directory_path = ptr_to_u64(dir),
183 .signature_info = ptr_to_u64(NULL),
184 .signature_size = 0,
185 .file_attr = ptr_to_u64(attr),
186 .file_attr_len =
187 attr ? strlen(attr) : 0 };
188
189 md5(filename, strlen(filename), (char *)args.file_id.bytes);
190
191 if (ioctl(fd, INCFS_IOC_CREATE_FILE, &args) != 0)
192 return -errno;
193
194 *id_out = args.file_id;
195 return 0;
196 }
197
get_file_bmap(int cmd_fd,int ino,unsigned char * buf,int buf_size)198 int get_file_bmap(int cmd_fd, int ino, unsigned char *buf, int buf_size)
199 {
200 return 0;
201 }
202
get_file_signature(int fd,unsigned char * buf,int buf_size)203 int get_file_signature(int fd, unsigned char *buf, int buf_size)
204 {
205 struct incfs_get_file_sig_args args = {
206 .file_signature = ptr_to_u64(buf),
207 .file_signature_buf_size = buf_size
208 };
209
210 if (ioctl(fd, INCFS_IOC_READ_FILE_SIGNATURE, &args) == 0)
211 return args.file_signature_len_out;
212 return -errno;
213 }
214
get_file_size(const char * name)215 loff_t get_file_size(const char *name)
216 {
217 struct stat st;
218
219 if (stat(name, &st) == 0)
220 return st.st_size;
221 return -ENOENT;
222 }
223
open_commands_file(const char * mount_dir)224 int open_commands_file(const char *mount_dir)
225 {
226 char cmd_file[255];
227 int cmd_fd;
228
229 snprintf(cmd_file, ARRAY_SIZE(cmd_file),
230 "%s/%s", mount_dir, INCFS_PENDING_READS_FILENAME);
231 cmd_fd = open(cmd_file, O_RDONLY | O_CLOEXEC);
232
233 if (cmd_fd < 0)
234 perror("Can't open commands file");
235 return cmd_fd;
236 }
237
open_log_file(const char * mount_dir)238 int open_log_file(const char *mount_dir)
239 {
240 char file[255];
241 int fd;
242
243 snprintf(file, ARRAY_SIZE(file), "%s/.log", mount_dir);
244 fd = open(file, O_RDWR | O_CLOEXEC);
245 if (fd < 0)
246 perror("Can't open log file");
247 return fd;
248 }
249
open_blocks_written_file(const char * mount_dir)250 int open_blocks_written_file(const char *mount_dir)
251 {
252 char file[255];
253 int fd;
254
255 snprintf(file, ARRAY_SIZE(file),
256 "%s/%s", mount_dir, INCFS_BLOCKS_WRITTEN_FILENAME);
257 fd = open(file, O_RDONLY | O_CLOEXEC);
258
259 if (fd < 0)
260 perror("Can't open blocks_written file");
261 return fd;
262 }
263
wait_for_pending_reads(int fd,int timeout_ms,struct incfs_pending_read_info * prs,int prs_count)264 int wait_for_pending_reads(int fd, int timeout_ms,
265 struct incfs_pending_read_info *prs, int prs_count)
266 {
267 ssize_t read_res = 0;
268
269 if (timeout_ms > 0) {
270 int poll_res = 0;
271 struct pollfd pollfd = {
272 .fd = fd,
273 .events = POLLIN
274 };
275
276 poll_res = poll(&pollfd, 1, timeout_ms);
277 if (poll_res < 0)
278 return -errno;
279 if (poll_res == 0)
280 return 0;
281 if (!(pollfd.revents | POLLIN))
282 return 0;
283 }
284
285 read_res = read(fd, prs, prs_count * sizeof(*prs));
286 if (read_res < 0)
287 return -errno;
288
289 return read_res / sizeof(*prs);
290 }
291
wait_for_pending_reads2(int fd,int timeout_ms,struct incfs_pending_read_info2 * prs,int prs_count)292 int wait_for_pending_reads2(int fd, int timeout_ms,
293 struct incfs_pending_read_info2 *prs, int prs_count)
294 {
295 ssize_t read_res = 0;
296
297 if (timeout_ms > 0) {
298 int poll_res = 0;
299 struct pollfd pollfd = {
300 .fd = fd,
301 .events = POLLIN
302 };
303
304 poll_res = poll(&pollfd, 1, timeout_ms);
305 if (poll_res < 0)
306 return -errno;
307 if (poll_res == 0)
308 return 0;
309 if (!(pollfd.revents | POLLIN))
310 return 0;
311 }
312
313 read_res = read(fd, prs, prs_count * sizeof(*prs));
314 if (read_res < 0)
315 return -errno;
316
317 return read_res / sizeof(*prs);
318 }
319
concat_file_name(const char * dir,const char * file)320 char *concat_file_name(const char *dir, const char *file)
321 {
322 char full_name[FILENAME_MAX] = "";
323
324 if (snprintf(full_name, ARRAY_SIZE(full_name), "%s/%s", dir, file) < 0)
325 return NULL;
326 return strdup(full_name);
327 }
328
delete_dir_tree(const char * dir_path)329 int delete_dir_tree(const char *dir_path)
330 {
331 DIR *dir = NULL;
332 struct dirent *dp;
333 int result = 0;
334
335 dir = opendir(dir_path);
336 if (!dir) {
337 result = -errno;
338 goto out;
339 }
340
341 while ((dp = readdir(dir))) {
342 char *full_path;
343
344 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
345 continue;
346
347 full_path = concat_file_name(dir_path, dp->d_name);
348 if (dp->d_type == DT_DIR)
349 result = delete_dir_tree(full_path);
350 else
351 result = unlink(full_path);
352 free(full_path);
353 if (result)
354 goto out;
355 }
356
357 out:
358 if (dir)
359 closedir(dir);
360 if (!result)
361 rmdir(dir_path);
362 return result;
363 }
364
sha256(const char * data,size_t dsize,char * hash)365 void sha256(const char *data, size_t dsize, char *hash)
366 {
367 SHA256_CTX ctx;
368
369 SHA256_Init(&ctx);
370 SHA256_Update(&ctx, data, dsize);
371 SHA256_Final((unsigned char *)hash, &ctx);
372 }
373
md5(const char * data,size_t dsize,char * hash)374 void md5(const char *data, size_t dsize, char *hash)
375 {
376 MD5_CTX ctx;
377
378 MD5_Init(&ctx);
379 MD5_Update(&ctx, data, dsize);
380 MD5_Final((unsigned char *)hash, &ctx);
381 }
382