1// Copyright 2014 The Android Open Source Project
2//
3// This software is licensed under the terms of the GNU General Public
4// License version 2, as published by the Free Software Foundation, and
5// may be copied, distributed, and modified under those terms.
6//
7// This program is distributed in the hope that it will be useful,
8// but WITHOUT ANY WARRANTY; without even the implied warranty of
9// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10// GNU General Public License for more details.
11
12#include "android/utils/file_data.h"
13
14#include "android/utils/panic.h"
15
16#include <errno.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20
21// Use a magic value in the |flags| field to indicate that a FileData
22// value was properly initialized. Helps catch errors at runtime.
23#define FILE_DATA_MAGIC   ((size_t)0x87002013U)
24
25
26bool fileData_isValid(const FileData* data) {
27    if (!data)
28        return false;
29    if (data->flags == FILE_DATA_MAGIC)
30        return true;
31    if (data->flags == 0 && data->data == NULL && data->size == 0)
32        return true;
33    return false;
34}
35
36static inline void fileData_setValid(FileData* data) {
37    data->flags = FILE_DATA_MAGIC;
38}
39
40
41static inline void fileData_setInvalid(FileData* data) {
42    data->flags = (size_t)0xDEADBEEFU;
43}
44
45
46static void fileData_initWith(FileData* data,
47                              const void* buff,
48                              size_t size) {
49    data->data = size ? (uint8_t*)buff : NULL;
50    data->size = size;
51    fileData_setValid(data);
52}
53
54
55void fileData_initEmpty(FileData* data) {
56    fileData_initWith(data, NULL, 0);
57}
58
59
60int fileData_initFromFile(FileData* data, const char* filePath) {
61    FILE* f = fopen(filePath, "rb");
62    if (!f)
63        return -errno;
64
65    int ret = 0;
66    do {
67        if (fseek(f, 0, SEEK_END) < 0) {
68            ret = -errno;
69            break;
70        }
71
72        long fileSize = ftell(f);
73        if (fileSize < 0) {
74            ret = -errno;
75            break;
76        }
77
78        if (fileSize == 0) {
79            fileData_initEmpty(data);
80            break;
81        }
82
83        if (fseek(f, 0, SEEK_SET) < 0) {
84            ret = -errno;
85            break;
86        }
87
88        char* buffer = malloc((size_t)fileSize);
89        if (!buffer) {
90            ret = -errno;
91            break;
92        }
93
94        size_t readLen = fread(buffer, 1, (size_t)fileSize, f);
95        if (readLen != (size_t)fileSize) {
96            if (feof(f)) {
97                ret = -EIO;
98            } else {
99                ret = -ferror(f);
100            }
101            break;
102        }
103
104        fileData_initWith(data, buffer, readLen);
105
106    } while (0);
107
108    fclose(f);
109    return ret;
110}
111
112
113int fileData_initFrom(FileData* data, const FileData* other) {
114    if (!other || !fileData_isValid(other)) {
115        APANIC("Trying to copy an uninitialized FileData instance\n");
116    }
117    if (other->size == 0) {
118        fileData_initEmpty(data);
119        return 0;
120    }
121    void* copy = malloc(other->size);
122    if (!copy) {
123        return -errno;
124    }
125
126    memcpy(copy, other->data, other->size);
127    fileData_initWith(data, copy, other->size);
128    return 0;
129}
130
131
132int fileData_initFromMemory(FileData* data,
133                             const void* input,
134                             size_t inputLen) {
135    FileData other;
136    fileData_initWith(&other, input, inputLen);
137    memset(data, 0, sizeof(*data));  // make valgrind happy.
138    return fileData_initFrom(data, &other);
139}
140
141
142void fileData_swap(FileData* data, FileData* other) {
143    if (!fileData_isValid(data) || !fileData_isValid(data))
144        APANIC("Trying to swap un-initialized FileData instance\n");
145
146    uint8_t* buffer = data->data;
147    data->data = other->data;
148    other->data = buffer;
149
150    size_t size = data->size;
151    data->size = other->size;
152    other->size = size;
153}
154
155
156void fileData_done(FileData* data) {
157    if (!fileData_isValid(data)) {
158        APANIC("Trying to finalize an un-initialized FileData instance\n");
159    }
160
161    free(data->data);
162    fileData_initWith(data, NULL, 0);
163    fileData_setInvalid(data);
164}
165