1/*
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
530