source: subversion/applications/utils/mod_tile/store.c @ 25781

Last change on this file since 25781 was 25781, checked in by jonb, 9 years ago

mod_tile: prevent fd leak in some error paths

File size: 9.6 KB
Line 
1/* Meta-tile optimised file storage
2 *
3 * Instead of storing each individual tile as a file,
4 * bundle the 8x8 meta tile into a special meta-file.
5 * This reduces the Inode usage and more efficient
6 * utilisation of disk space.
7 */
8
9#include <stdio.h>
10#include <stdlib.h>
11#include <unistd.h>
12#include <limits.h>
13#include <string.h>
14#include <sys/types.h>
15#include <sys/stat.h>
16#include <utime.h>
17#include <fcntl.h>
18#include <assert.h>
19
20
21#include "store.h"
22#include "render_config.h"
23#include "dir_utils.h"
24#include "protocol.h"
25
26#ifdef METATILE
27int read_from_meta(const char *xmlconfig, int x, int y, int z, unsigned char *buf, size_t sz)
28{
29    char path[PATH_MAX];
30    int meta_offset, fd;
31    unsigned int pos;
32    char header[4096];
33    struct meta_layout *m = (struct meta_layout *)header;
34    size_t file_offset, tile_size;
35
36    meta_offset = xyz_to_meta(path, sizeof(path), HASH_PATH, xmlconfig, x, y, z);
37
38    fd = open(path, O_RDONLY);
39    if (fd < 0)
40        return -1;
41
42    pos = 0;
43    while (pos < sizeof(header)) {
44        size_t len = sizeof(header) - pos;
45        int got = read(fd, header + pos, len);
46        if (got < 0) {
47            close(fd);
48            return -2;
49        } else if (got > 0) {
50            pos += got;
51        } else {
52            break;
53        }
54    }
55    if (pos < sizeof(struct meta_layout)) {
56        fprintf(stderr, "Meta file %s too small to contain header\n", path);
57        close(fd);
58        return -3;
59    }
60    if (memcmp(m->magic, META_MAGIC, strlen(META_MAGIC))) {
61        fprintf(stderr, "Meta file %s header magic mismatch\n", path);
62        close(fd);
63        return -4;
64    }
65#if 1
66    // Currently this code only works with fixed metatile sizes (due to xyz_to_meta above)
67    if (m->count != (METATILE * METATILE)) {
68        fprintf(stderr, "Meta file %s header bad count %d != %d\n", path, m->count, METATILE * METATILE);
69        close(fd);
70        return -5;
71    }
72#else
73    if (m->count < 0 || m->count > 256) {
74        fprintf(stderr, "Meta file %s header bad count %d\n", path, m->count);
75        close(fd);
76        return -5;
77    }
78#endif
79    file_offset = m->index[meta_offset].offset;
80    tile_size   = m->index[meta_offset].size;
81
82    if (lseek(fd, file_offset, SEEK_SET) < 0) {
83        fprintf(stderr, "Meta file %s seek error %d\n", path, m->count);
84        close(fd);
85        return -6;
86    }
87    if (tile_size > sz) {
88        fprintf(stderr, "Truncating tile %zd to fit buffer of %zd\n", tile_size, sz);
89        tile_size = sz;
90    }
91    pos = 0;
92    while (pos < tile_size) {
93        size_t len = tile_size - pos;
94        int got = read(fd, buf + pos, len);
95        if (got < 0) {
96            close(fd);
97            return -7;
98        } else if (got > 0) {
99            pos += got;
100        } else {
101            break;
102        }
103    }
104    close(fd);
105    return pos;
106}
107#endif
108
109int read_from_file(const char *xmlconfig, int x, int y, int z, unsigned char *buf, size_t sz)
110{
111    char path[PATH_MAX];
112    int fd;
113    size_t pos;
114
115    xyz_to_path(path, sizeof(path), HASH_PATH, xmlconfig, x, y, z);
116
117    fd = open(path, O_RDONLY);
118    if (fd < 0)
119        return -1;
120
121    pos = 0;
122    while (pos < sz) {
123        size_t len = sz - pos;
124        int got = read(fd, buf + pos, len);
125        if (got < 0) {
126            close(fd);
127            return -2;
128        } else if (got > 0) {
129            pos += got;
130        } else {
131            break;
132        }
133    }
134    if (pos == sz) {
135        fprintf(stderr, "file %s truncated at %zd bytes\n", path, sz);
136    }
137    close(fd);
138    return pos;
139}
140
141int tile_read(const char *xmlconfig, int x, int y, int z, unsigned char *buf, int sz)
142{
143#ifdef METATILE
144    int r;
145
146    r = read_from_meta(xmlconfig, x, y, z, buf, sz);
147    if (r >= 0)
148        return r;
149#endif
150    return read_from_file(xmlconfig, x, y, z, buf, sz);
151}
152
153#ifdef METATILE
154void process_meta(const char *xmlconfig, int x, int y, int z)
155{
156    int fd;
157    int ox, oy, limit;
158    size_t offset, pos;
159    const int buf_len = 10 * MAX_SIZE; // To store all tiles in this .meta
160    unsigned char *buf;
161    struct meta_layout *m;
162    char meta_path[PATH_MAX];
163    char tmp[PATH_MAX];
164    struct stat s;
165
166    buf = (unsigned char *)malloc(buf_len);
167    if (!buf)
168        return;
169
170    m = (struct meta_layout *)buf;
171    offset = sizeof(struct meta_layout) + (sizeof(struct entry) * (METATILE * METATILE));
172    memset(buf, 0, offset);
173
174    limit = (1 << z);
175    limit = MIN(limit, METATILE);
176
177    for (ox=0; ox < limit; ox++) {
178        for (oy=0; oy < limit; oy++) {
179            //fprintf(stderr, "Process %d/%d/%d\n", num, ox, oy);
180            int len = read_from_file(xmlconfig, x + ox, y + oy, z, buf + offset, buf_len - offset);
181            int mt = xyz_to_meta(meta_path, sizeof(meta_path), HASH_PATH, xmlconfig, x + ox, y + oy, z);
182            if (len <= 0) {
183#if 1
184                fprintf(stderr, "Problem reading sub tiles for metatile xml(%s) x(%d) y(%d) z(%d), got %d\n", xmlconfig, x, y, z, len);
185                free(buf);
186                return;
187#else
188                 m->index[mt].offset = 0;
189                 m->index[mt].size = 0;
190#endif
191            } else {
192                 m->index[mt].offset = offset;
193                 m->index[mt].size = len;
194                 offset += len;
195            }
196        }
197    }
198    m->count = METATILE * METATILE;
199    memcpy(m->magic, META_MAGIC, strlen(META_MAGIC));
200    m->x = x;
201    m->y = y;
202    m->z = z;
203
204    xyz_to_meta(meta_path, sizeof(meta_path), HASH_PATH, xmlconfig, x, y, z);
205    if (mkdirp(meta_path)) {
206        fprintf(stderr, "Error creating directories for: %s\n", meta_path);
207        return;
208    }
209    snprintf(tmp, sizeof(tmp), "%s.tmp.%d", meta_path, getpid());
210
211    fd = open(tmp, O_WRONLY | O_TRUNC | O_CREAT, 0666);
212    if (fd < 0) {
213        fprintf(stderr, "Error creating file: %s\n", meta_path);
214        free(buf);
215        return;
216    }
217
218    pos = 0;
219    while (pos < offset) {
220        int len = write(fd, buf + pos, offset - pos);
221        if (len < 0) {
222            perror("Writing file");
223            free(buf);
224            close(fd);
225            return;
226        } else if (len > 0) {
227            pos += len;
228        } else {
229            break;
230        }
231    }
232    close(fd);
233    free(buf);
234
235    // Reset meta timestamp to match one of the original tiles
236    xyz_to_path(meta_path, sizeof(meta_path), HASH_PATH, xmlconfig, x, y, z);
237    if (stat(meta_path, &s) == 0) {
238        struct utimbuf b;
239        b.actime = s.st_atime;
240        b.modtime = s.st_mtime;
241        xyz_to_meta(meta_path, sizeof(meta_path), HASH_PATH, xmlconfig, x, y, z);
242        utime(tmp, &b);
243    }
244    rename(tmp, meta_path);
245    printf("Produced .meta: %s\n", meta_path);
246
247    // Remove raw .png's
248    for (ox=0; ox < limit; ox++) {
249        for (oy=0; oy < limit; oy++) {
250            xyz_to_path(meta_path, sizeof(meta_path), HASH_PATH, xmlconfig, x + ox, y + oy, z);
251            if (unlink(meta_path)<0)
252                perror(meta_path);
253        }
254    }
255}
256
257void process_pack(const char *name)
258{
259    char meta_path[PATH_MAX];
260    char xmlconfig[XMLCONFIG_MAX];
261    int x, y, z;
262    int meta_offset;
263
264    if (path_to_xyz(name, xmlconfig, &x, &y, &z))
265        return;
266 
267    // Launch the .meta creation for only 1 tile of the whole block
268    meta_offset = xyz_to_meta(meta_path, sizeof(meta_path), HASH_PATH, xmlconfig, x, y, z);
269    //fprintf(stderr,"Requesting x(%d) y(%d) z(%d) - mo(%d)\n", x, y, z, meta_offset);
270
271    if (meta_offset == 0)
272        process_meta(xmlconfig, x, y, z);
273}
274
275static void write_tile(const char *xmlconfig, int x, int y, int z, const unsigned char *buf, size_t sz)
276{
277    int fd;
278    char path[PATH_MAX];
279    size_t pos;
280
281    xyz_to_path(path, sizeof(path), HASH_PATH, xmlconfig, x, y, z);
282    if (mkdirp(path)) {
283        fprintf(stderr, "Error creating directories for: %s\n", path);
284        return;
285    }
286    fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, 0666);
287    if (fd < 0) {
288        fprintf(stderr, "Error creating file: %s\n", path);
289        return;
290    }
291
292    pos = 0;
293    while (pos < sz) {
294        int len = write(fd, buf + pos, sz - pos);
295        if (len < 0) {
296            perror("Writing file");
297            close(fd);
298            return;
299        } else if (len > 0) {
300            pos += len;
301        } else {
302            break;
303        }
304    }
305    close(fd);
306    printf("Produced tile: %s\n", path);
307}
308
309void process_unpack(const char *name)
310{
311    char meta_path[PATH_MAX];
312    char xmlconfig[XMLCONFIG_MAX];
313    int x, y, z;
314    int ox, oy, limit;
315    const int buf_len = 1024 * 1024;
316    unsigned char *buf;
317    struct stat s;
318
319    // path_to_xyz is valid for meta tile names as well
320    if (path_to_xyz(name, xmlconfig, &x, &y, &z))
321        return;
322
323    buf = (unsigned char *)malloc(buf_len);
324    if (!buf)
325        return;
326
327
328    limit = (1 << z);
329    limit = MIN(limit, METATILE);
330
331    for (ox=0; ox < limit; ox++) {
332        for (oy=0; oy < limit; oy++) {
333            int len = read_from_meta(xmlconfig, x + ox, y + oy, z, buf, buf_len);
334
335            if (len <= 0)
336                fprintf(stderr, "Failed to get tile x(%d) y(%d) z(%d)\n", x + ox, y + oy, z);
337            else
338                write_tile(xmlconfig, x + ox, y + oy, z, buf, len);
339        }
340    }
341
342    // Grab timestamp of the meta file and update tile timestamps
343    if (stat(name, &s) == 0) {
344        struct utimbuf b;
345        b.actime = s.st_atime;
346        b.modtime = s.st_mtime;
347        for (ox=0; ox < limit; ox++) {
348            for (oy=0; oy < limit; oy++) {
349                xyz_to_path(meta_path, sizeof(meta_path), HASH_PATH, xmlconfig, x+ox, y+oy, z);
350                utime(meta_path, &b);
351            }
352        }
353    }
354
355    // Remove the .meta file
356    xyz_to_meta(meta_path, sizeof(meta_path), HASH_PATH, xmlconfig, x, y, z);
357    if (unlink(meta_path)<0)
358        perror(meta_path);
359}
360#endif
Note: See TracBrowser for help on using the repository browser.