blob: c61e89d789ce40ca69af8e228070addc91793fd5 [file] [log] [blame]
Michael Ryleev7a2bc372016-03-08 15:16:11 -08001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#include <errno.h>
17#include <fcntl.h>
18#include <inttypes.h>
19#include <stdbool.h>
20#include <stdlib.h>
21#include <string.h>
22#include <sys/stat.h>
23#include <sys/syscall.h>
24#include <sys/types.h>
25#include <unistd.h>
26
27#include "log.h"
28#include "ipc.h"
29#include "storage.h"
30
31#define FD_TBL_SIZE 64
32#define MAX_READ_SIZE 4096
33
34enum sync_state {
35 SS_UNUSED = -1,
36 SS_CLEAN = 0,
37 SS_DIRTY = 1,
38};
39
40static int ssdir_fd = -1;
41static const char *ssdir_name;
42
43static enum sync_state fs_state;
44static enum sync_state dir_state;
45static enum sync_state fd_state[FD_TBL_SIZE];
46
47static struct {
48 struct storage_file_read_resp hdr;
49 uint8_t data[MAX_READ_SIZE];
50} read_rsp;
51
52static uint32_t insert_fd(int open_flags, int fd)
53{
54 uint32_t handle = fd;
55
56 if (open_flags & O_CREAT) {
57 dir_state = SS_DIRTY;
58 }
59
60 if (handle < FD_TBL_SIZE) {
61 fd_state[fd] = SS_CLEAN; /* fd clean */
62 if (open_flags & O_TRUNC) {
63 fd_state[fd] = SS_DIRTY; /* set fd dirty */
64 }
65 } else {
66 ALOGW("%s: untracked fd %u\n", __func__, fd);
67 if (open_flags & (O_TRUNC | O_CREAT)) {
68 fs_state = SS_DIRTY;
69 }
70 }
71 return handle;
72}
73
74static int lookup_fd(uint32_t handle, bool dirty)
75{
76 if (dirty) {
77 if (handle < FD_TBL_SIZE) {
78 fd_state[handle] = SS_DIRTY;
79 } else {
80 fs_state = SS_DIRTY;
81 }
82 }
83 return handle;
84}
85
86static int remove_fd(uint32_t handle)
87{
88 if (handle < FD_TBL_SIZE) {
89 fd_state[handle] = SS_UNUSED; /* set to uninstalled */
90 }
91 return handle;
92}
93
94static enum storage_err translate_errno(int error)
95{
96 enum storage_err result;
97 switch (error) {
98 case 0:
99 result = STORAGE_NO_ERROR;
100 break;
101 case EBADF:
102 case EINVAL:
103 case ENOTDIR:
104 case EISDIR:
105 case ENAMETOOLONG:
106 result = STORAGE_ERR_NOT_VALID;
107 break;
108 case ENOENT:
109 result = STORAGE_ERR_NOT_FOUND;
110 break;
111 case EEXIST:
112 result = STORAGE_ERR_EXIST;
113 break;
114 case EPERM:
115 case EACCES:
116 result = STORAGE_ERR_ACCESS;
117 break;
118 default:
119 result = STORAGE_ERR_GENERIC;
120 break;
121 }
122
123 return result;
124}
125
126static ssize_t write_with_retry(int fd, const void *buf_, size_t size, off_t offset)
127{
128 ssize_t rc;
129 const uint8_t *buf = buf_;
130
131 while (size > 0) {
132 rc = TEMP_FAILURE_RETRY(pwrite(fd, buf, size, offset));
133 if (rc < 0)
134 return rc;
135 size -= rc;
136 buf += rc;
137 offset += rc;
138 }
139 return 0;
140}
141
142static ssize_t read_with_retry(int fd, void *buf_, size_t size, off_t offset)
143{
144 ssize_t rc;
145 size_t rcnt = 0;
146 uint8_t *buf = buf_;
147
148 while (size > 0) {
149 rc = TEMP_FAILURE_RETRY(pread(fd, buf, size, offset));
150 if (rc < 0)
151 return rc;
152 if (rc == 0)
153 break;
154 size -= rc;
155 buf += rc;
156 offset += rc;
157 rcnt += rc;
158 }
159 return rcnt;
160}
161
162int storage_file_delete(struct storage_msg *msg,
163 const void *r, size_t req_len)
164{
165 char *path = NULL;
166 const struct storage_file_delete_req *req = r;
167
168 if (req_len < sizeof(*req)) {
169 ALOGE("%s: invalid request length (%zd < %zd)\n",
170 __func__, req_len, sizeof(*req));
171 msg->result = STORAGE_ERR_NOT_VALID;
172 goto err_response;
173 }
174
175 size_t fname_len = strlen(req->name);
176 if (fname_len != req_len - sizeof(*req)) {
177 ALOGE("%s: invalid filename length (%zd != %zd)\n",
178 __func__, fname_len, req_len - sizeof(*req));
179 msg->result = STORAGE_ERR_NOT_VALID;
180 goto err_response;
181 }
182
183 int rc = asprintf(&path, "%s/%s", ssdir_name, req->name);
184 if (rc < 0) {
185 ALOGE("%s: asprintf failed\n", __func__);
186 msg->result = STORAGE_ERR_GENERIC;
187 goto err_response;
188 }
189
190 dir_state = SS_DIRTY;
191 rc = unlink(path);
192 if (rc < 0) {
193 rc = errno;
194 if (errno == ENOENT) {
195 ALOGV("%s: error (%d) unlinking file '%s'\n",
196 __func__, rc, path);
197 } else {
198 ALOGE("%s: error (%d) unlinking file '%s'\n",
199 __func__, rc, path);
200 }
201 msg->result = translate_errno(rc);
202 goto err_response;
203 }
204
205 ALOGV("%s: \"%s\"\n", __func__, path);
206 msg->result = STORAGE_NO_ERROR;
207
208err_response:
209 if (path)
210 free(path);
211 return ipc_respond(msg, NULL, 0);
212}
213
214
215int storage_file_open(struct storage_msg *msg,
216 const void *r, size_t req_len)
217{
218 char *path = NULL;
219 const struct storage_file_open_req *req = r;
220 struct storage_file_open_resp resp = {0};
221
222 if (req_len < sizeof(*req)) {
223 ALOGE("%s: invalid request length (%zd < %zd)\n",
224 __func__, req_len, sizeof(*req));
225 msg->result = STORAGE_ERR_NOT_VALID;
226 goto err_response;
227 }
228
229 size_t fname_len = strlen(req->name);
230 if (fname_len != req_len - sizeof(*req)) {
231 ALOGE("%s: invalid filename length (%zd != %zd)\n",
232 __func__, fname_len, req_len - sizeof(*req));
233 msg->result = STORAGE_ERR_NOT_VALID;
234 goto err_response;
235 }
236
237 int rc = asprintf(&path, "%s/%s", ssdir_name, req->name);
238 if (rc < 0) {
239 ALOGE("%s: asprintf failed\n", __func__);
240 msg->result = STORAGE_ERR_GENERIC;
241 goto err_response;
242 }
243
244 int open_flags = O_RDWR;
245
246 if (req->flags & STORAGE_FILE_OPEN_TRUNCATE)
247 open_flags |= O_TRUNC;
248
249 if (req->flags & STORAGE_FILE_OPEN_CREATE) {
250 /* open or create */
251 if (req->flags & STORAGE_FILE_OPEN_CREATE_EXCLUSIVE) {
252 /* create exclusive */
253 open_flags |= O_CREAT | O_EXCL;
254 rc = TEMP_FAILURE_RETRY(open(path, open_flags, S_IRUSR | S_IWUSR));
255 } else {
256 /* try open first */
257 rc = TEMP_FAILURE_RETRY(open(path, open_flags, S_IRUSR | S_IWUSR));
258 if (rc == -1 && errno == ENOENT) {
259 /* then try open with O_CREATE */
260 open_flags |= O_CREAT;
261 rc = TEMP_FAILURE_RETRY(open(path, open_flags, S_IRUSR | S_IWUSR));
262 }
263
264 }
265 } else {
266 /* open an existing file */
267 rc = TEMP_FAILURE_RETRY(open(path, open_flags, S_IRUSR | S_IWUSR));
268 }
269
270 if (rc < 0) {
271 rc = errno;
272 if (errno == EEXIST || errno == ENOENT) {
273 ALOGV("%s: failed to open file \"%s\": %s\n",
274 __func__, path, strerror(errno));
275 } else {
276 ALOGE("%s: failed to open file \"%s\": %s\n",
277 __func__, path, strerror(errno));
278 }
279 msg->result = translate_errno(rc);
280 goto err_response;
281 }
282 free(path);
283
284 /* at this point rc contains storage file fd */
285 msg->result = STORAGE_NO_ERROR;
286 resp.handle = insert_fd(open_flags, rc);
287 ALOGV("%s: \"%s\": fd = %u: handle = %d\n",
288 __func__, path, rc, resp.handle);
289
290 return ipc_respond(msg, &resp, sizeof(resp));
291
292err_response:
293 if (path)
294 free(path);
295 return ipc_respond(msg, NULL, 0);
296}
297
298int storage_file_close(struct storage_msg *msg,
299 const void *r, size_t req_len)
300{
301 const struct storage_file_close_req *req = r;
302
303 if (req_len != sizeof(*req)) {
304 ALOGE("%s: invalid request length (%zd != %zd)\n",
305 __func__, req_len, sizeof(*req));
306 msg->result = STORAGE_ERR_NOT_VALID;
307 goto err_response;
308 }
309
310 int fd = remove_fd(req->handle);
311 ALOGV("%s: handle = %u: fd = %u\n", __func__, req->handle, fd);
312
313 int rc = fsync(fd);
314 if (rc < 0) {
315 rc = errno;
316 ALOGE("%s: fsync failed for fd=%u: %s\n",
317 __func__, fd, strerror(errno));
318 msg->result = translate_errno(rc);
319 goto err_response;
320 }
321
322 rc = close(fd);
323 if (rc < 0) {
324 rc = errno;
325 ALOGE("%s: close failed for fd=%u: %s\n",
326 __func__, fd, strerror(errno));
327 msg->result = translate_errno(rc);
328 goto err_response;
329 }
330
331 msg->result = STORAGE_NO_ERROR;
332
333err_response:
334 return ipc_respond(msg, NULL, 0);
335}
336
337
338int storage_file_write(struct storage_msg *msg,
339 const void *r, size_t req_len)
340{
341 int rc;
342 const struct storage_file_write_req *req = r;
343
344 if (req_len < sizeof(*req)) {
345 ALOGE("%s: invalid request length (%zd < %zd)\n",
346 __func__, req_len, sizeof(*req));
347 msg->result = STORAGE_ERR_NOT_VALID;
348 goto err_response;
349 }
350
351 int fd = lookup_fd(req->handle, true);
352 if (write_with_retry(fd, &req->data[0], req_len - sizeof(*req),
353 req->offset) < 0) {
354 rc = errno;
355 ALOGW("%s: error writing file (fd=%d): %s\n",
356 __func__, fd, strerror(errno));
357 msg->result = translate_errno(rc);
358 goto err_response;
359 }
360
361 msg->result = STORAGE_NO_ERROR;
362
363err_response:
364 return ipc_respond(msg, NULL, 0);
365}
366
367
368int storage_file_read(struct storage_msg *msg,
369 const void *r, size_t req_len)
370{
371 int rc;
372 const struct storage_file_read_req *req = r;
373
374 if (req_len != sizeof(*req)) {
375 ALOGE("%s: invalid request length (%zd != %zd)\n",
376 __func__, req_len, sizeof(*req));
377 msg->result = STORAGE_ERR_NOT_VALID;
378 goto err_response;
379 }
380
381 if (req->size > MAX_READ_SIZE) {
382 ALOGW("%s: request is too large (%zd > %zd) - refusing\n",
383 __func__, req->size, MAX_READ_SIZE);
384 msg->result = STORAGE_ERR_NOT_VALID;
385 goto err_response;
386 }
387
388 int fd = lookup_fd(req->handle, false);
389 ssize_t read_res = read_with_retry(fd, read_rsp.hdr.data, req->size,
390 (off_t)req->offset);
391 if (read_res < 0) {
392 rc = errno;
393 ALOGW("%s: error reading file (fd=%d): %s\n",
394 __func__, fd, strerror(errno));
395 msg->result = translate_errno(rc);
396 goto err_response;
397 }
398
399 msg->result = STORAGE_NO_ERROR;
400 return ipc_respond(msg, &read_rsp, read_res + sizeof(read_rsp.hdr));
401
402err_response:
403 return ipc_respond(msg, NULL, 0);
404}
405
406
407int storage_file_get_size(struct storage_msg *msg,
408 const void *r, size_t req_len)
409{
410 const struct storage_file_get_size_req *req = r;
411 struct storage_file_get_size_resp resp = {0};
412
413 if (req_len != sizeof(*req)) {
414 ALOGE("%s: invalid request length (%zd != %zd)\n",
415 __func__, req_len, sizeof(*req));
416 msg->result = STORAGE_ERR_NOT_VALID;
417 goto err_response;
418 }
419
420 struct stat stat;
421 int fd = lookup_fd(req->handle, false);
422 int rc = fstat(fd, &stat);
423 if (rc < 0) {
424 rc = errno;
425 ALOGE("%s: error stat'ing file (fd=%d): %s\n",
426 __func__, fd, strerror(errno));
427 msg->result = translate_errno(rc);
428 goto err_response;
429 }
430
431 resp.size = stat.st_size;
432 msg->result = STORAGE_NO_ERROR;
433 return ipc_respond(msg, &resp, sizeof(resp));
434
435err_response:
436 return ipc_respond(msg, NULL, 0);
437}
438
439
440int storage_file_set_size(struct storage_msg *msg,
441 const void *r, size_t req_len)
442{
443 const struct storage_file_set_size_req *req = r;
444
445 if (req_len != sizeof(*req)) {
446 ALOGE("%s: invalid request length (%zd != %zd)\n",
447 __func__, req_len, sizeof(*req));
448 msg->result = STORAGE_ERR_NOT_VALID;
449 goto err_response;
450 }
451
452 int fd = lookup_fd(req->handle, true);
453 int rc = TEMP_FAILURE_RETRY(ftruncate(fd, req->size));
454 if (rc < 0) {
455 rc = errno;
456 ALOGE("%s: error truncating file (fd=%d): %s\n",
457 __func__, fd, strerror(errno));
458 msg->result = translate_errno(rc);
459 goto err_response;
460 }
461
462 msg->result = STORAGE_NO_ERROR;
463
464err_response:
465 return ipc_respond(msg, NULL, 0);
466}
467
468int storage_init(const char *dirname)
469{
470 fs_state = SS_CLEAN;
471 dir_state = SS_CLEAN;
472 for (uint i = 0; i < FD_TBL_SIZE; i++) {
473 fd_state[i] = SS_UNUSED; /* uninstalled */
474 }
475
476 ssdir_fd = open(dirname, O_RDONLY);
477 if (ssdir_fd < 0) {
478 ALOGE("failed to open ss root dir \"%s\": %s\n",
479 dirname, strerror(errno));
480 return -1;
481 }
482 ssdir_name = dirname;
483 return 0;
484}
485
486int storage_sync_checkpoint(void)
487{
488 int rc;
489
490 /* sync fd table and reset it to clean state first */
491 for (uint fd = 0; fd < FD_TBL_SIZE; fd++) {
492 if (fd_state[fd] == SS_DIRTY) {
493 if (fs_state == SS_CLEAN) {
494 /* need to sync individual fd */
495 rc = fsync(fd);
496 if (rc < 0) {
497 ALOGE("fsync for fd=%d failed: %s\n", fd, strerror(errno));
498 return rc;
499 }
500 }
501 fd_state[fd] = SS_CLEAN; /* set to clean */
502 }
503 }
504
505 /* check if we need to sync the directory */
506 if (dir_state == SS_DIRTY) {
507 if (fs_state == SS_CLEAN) {
508 rc = fsync(ssdir_fd);
509 if (rc < 0) {
510 ALOGE("fsync for ssdir failed: %s\n", strerror(errno));
511 return rc;
512 }
513 }
514 dir_state = SS_CLEAN; /* set to clean */
515 }
516
517 /* check if we need to sync the whole fs */
518 if (fs_state == SS_DIRTY) {
519 rc = syscall(SYS_syncfs, ssdir_fd);
520 if (rc < 0) {
521 ALOGE("syncfs failed: %s\n", strerror(errno));
522 return rc;
523 }
524 fs_state = SS_CLEAN;
525 }
526
527 return 0;
528}
529