Implement tar pool. Do not use dup for single files, as it uses a shared file position.
[calfbox.git] / tarfile.c
bloba0d01c0bf20f8c01a8446d4c492a1b14fbfb8dd4
1 /*
2 Calf Box, an open source musical instrument.
3 Copyright (C) 2010-2013 Krzysztof Foltman
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "config-api.h"
20 #include "errors.h"
21 #include "tarfile.h"
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <malloc.h>
25 #include <string.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <stdlib.h>
29 #include <unistd.h>
31 struct tar_record
33 char name[100];
34 char mode[8];
35 char uid[8];
36 char gid[8];
37 char size[12];
38 char mtime[12];
39 char checksum[8];
40 char typeflag;
41 char linkname[100];
42 char ustar[6];
43 char ustarver[2];
44 char uname[32];
45 char gname[32];
46 char devmajor[8];
47 char devminor[8];
48 char prefix[155];
49 char padding[12];
52 static void remove_item_if(gpointer p);
54 struct cbox_tarfile *cbox_tarfile_open(const char *pathname, GError **error)
56 gboolean debug = cbox_config_get_int("debug", "tarfile", 0);
57 gchar *canonical = realpath(pathname, NULL);
58 if (!canonical)
60 if (error)
61 g_set_error(error, G_FILE_ERROR, g_file_error_from_errno (errno), "cannot determine canonical name of '%s'", pathname);
62 return NULL;
64 int fd = open(canonical, O_RDONLY | O_LARGEFILE);
65 if (fd < 0)
67 free(canonical);
68 if (error)
69 g_set_error(error, G_FILE_ERROR, g_file_error_from_errno (errno), "cannot open '%s'", pathname);
70 return NULL;
72 GHashTable *byname = NULL, *byname_nc = NULL;
74 byname = g_hash_table_new(g_str_hash, g_str_equal);
75 byname_nc = g_hash_table_new(g_str_hash, g_str_equal);
76 if (!byname || !byname_nc)
77 goto error;
79 struct cbox_tarfile *tf = calloc(1, sizeof(struct cbox_tarfile));
80 if (!tf)
81 goto error;
82 tf->fd = fd;
83 tf->items_byname = byname;
84 tf->items_byname_nc = byname_nc;
85 tf->refs = 1;
86 tf->file_pathname = canonical;
87 while(1)
89 struct tar_record rec;
90 int nbytes = read(fd, &rec, sizeof(rec));
91 if (nbytes != sizeof(rec))
92 break;
94 int len = sizeof(rec.name);
95 while(len > 0 && (rec.name[len - 1] == ' ' || rec.name[len - 1] == '\0'))
96 len--;
98 char sizetext[13];
99 memcpy(sizetext, rec.size, 12);
100 sizetext[12] = '\0';
101 unsigned long long size = strtoll(sizetext, NULL, 8);
103 // skip block if name is empty
104 if (!len)
105 goto skipitem;
106 struct cbox_taritem *ti = calloc(1, sizeof(struct cbox_taritem));
107 if (ti)
109 ti->filename = g_strndup(rec.name, len);
110 ti->filename_nc = g_utf8_casefold(rec.name, len);
111 if (!ti->filename || !ti->filename_nc)
112 goto itemerror;
113 ti->offset = lseek64(fd, 0, SEEK_CUR);
114 ti->size = size;
115 ti->refs = 2;
117 // Overwrite old items by the same name and/or same case-folded name
118 remove_item_if(g_hash_table_lookup(tf->items_byname, ti->filename));
119 remove_item_if(g_hash_table_lookup(tf->items_byname_nc, ti->filename_nc));
121 g_hash_table_insert(tf->items_byname, ti->filename, ti);
122 g_hash_table_insert(tf->items_byname_nc, ti->filename_nc, ti);
123 if (debug)
124 printf("name = %s len = %d offset = %d readsize = %d\n", ti->filename, len, (int)ti->offset, (int)size);
126 goto skipitem;
128 itemerror:
129 rec.name[99] = '\0';
130 g_warning("Could not allocate memory for tar item %s", rec.name);
131 if (ti)
133 if (ti->filename_nc)
134 g_free(ti->filename_nc);
135 if (ti->filename)
136 g_free(ti->filename);
137 free(ti);
139 skipitem:
140 lseek64(fd, (size + 511) &~ 511, SEEK_CUR);
142 return tf;
144 error:
145 if (byname)
146 g_hash_table_destroy(byname);
147 if (byname_nc)
148 g_hash_table_destroy(byname_nc);
149 free(canonical);
150 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot allocate memory for tarfile data");
151 return NULL;
154 void remove_item_if(gpointer p)
156 if (!p)
157 return;
159 struct cbox_taritem *ti = p;
160 // If all references (by name and by case-folded name) gone, remove the item
161 if (!--ti->refs)
163 g_free(ti->filename);
164 g_free(ti->filename_nc);
165 free(ti);
169 struct cbox_taritem *cbox_tarfile_get_item_by_name(struct cbox_tarfile *tarfile, const char *item_filename, gboolean ignore_case)
171 if (ignore_case)
173 gchar *folded = g_utf8_casefold(item_filename, -1);
174 struct cbox_taritem *item = g_hash_table_lookup(tarfile->items_byname_nc, folded);
175 g_free(folded);
176 return item;
178 else
179 return g_hash_table_lookup(tarfile->items_byname, item_filename);
182 int cbox_tarfile_openitem(struct cbox_tarfile *tarfile, struct cbox_taritem *item)
184 int fd = open(tarfile->file_pathname, O_RDONLY | O_LARGEFILE);
185 if (fd >= 0)
186 lseek64(fd, item->offset, SEEK_SET);
187 return fd;
190 void cbox_tarfile_closeitem(struct cbox_tarfile *tarfile, struct cbox_taritem *item, int fd)
192 if (fd >= 0)
193 close(fd);
196 static void delete_foreach_func(gpointer key, gpointer value, gpointer user_data)
198 struct cbox_taritem *ti = value;
199 if (!--ti->refs)
201 g_free(ti->filename);
202 g_free(ti->filename_nc);
203 free(ti);
207 void cbox_tarfile_destroy(struct cbox_tarfile *tf)
209 g_hash_table_foreach(tf->items_byname, delete_foreach_func, NULL);
210 g_hash_table_foreach(tf->items_byname_nc, delete_foreach_func, NULL);
211 close(tf->fd);
212 g_hash_table_destroy(tf->items_byname);
213 g_hash_table_destroy(tf->items_byname_nc);
214 free(tf->file_pathname);
215 free(tf);
218 ////////////////////////////////////////////////////////////////////////////////
220 struct cbox_tarpool *cbox_tarpool_new()
222 struct cbox_tarpool *pool = calloc(1, sizeof(struct cbox_tarpool));
223 pool->files = g_hash_table_new(g_str_hash, g_str_equal);
224 return pool;
227 struct cbox_tarfile *cbox_tarpool_get_tarfile(struct cbox_tarpool *pool, const char *name, GError **error)
229 //gchar *c = realpath(name, NULL);
230 gchar *c = g_strdup(name);
231 struct cbox_tarfile *tf = g_hash_table_lookup(pool->files, c);
232 if (tf)
233 tf->refs++;
234 else
236 tf = cbox_tarfile_open(c, error);
237 if (!tf)
239 g_free(c);
240 return NULL;
242 g_hash_table_insert(pool->files, c, tf);
244 g_free(c);
245 return tf;
248 void cbox_tarpool_release_tarfile(struct cbox_tarpool *pool, struct cbox_tarfile *file)
250 if (!--file->refs)
251 cbox_tarfile_destroy(file);
254 void cbox_tarpool_destroy(struct cbox_tarpool *pool)
256 g_hash_table_destroy(pool->files);
257 free(pool);