1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3 #ifndef _WIN32_WINNT
4 #define _WIN32_WINNT 0x500
5 #endif
6
7 #include "common.h"
8
9 #include "utils.h"
10
11 #include "log.h"
12
13 #include <sys/stat.h>
14 #include <fcntl.h>
15 #include <dirent.h>
16
17 #include "block-backend.h"
18 #include "obj-store.h"
19
20
21 struct _BHandle {
22 char *store_id;
23 int version;
24 char block_id[41];
25 int fd;
26 int rw_type;
27 char *tmp_file;
28 };
29
30 typedef struct {
31 char *block_dir;
32 int block_dir_len;
33 char *tmp_dir;
34 int tmp_dir_len;
35 } FsPriv;
36
37 static char *
38 get_block_path (BlockBackend *bend,
39 const char *block_sha1,
40 char path[],
41 const char *store_id,
42 int version);
43
44 static int
45 open_tmp_file (BlockBackend *bend,
46 const char *basename,
47 char **path);
48
49 static BHandle *
block_backend_fs_open_block(BlockBackend * bend,const char * store_id,int version,const char * block_id,int rw_type)50 block_backend_fs_open_block (BlockBackend *bend,
51 const char *store_id,
52 int version,
53 const char *block_id,
54 int rw_type)
55 {
56 BHandle *handle;
57 int fd = -1;
58 char *tmp_file;
59
60 g_return_val_if_fail (block_id != NULL, NULL);
61 g_return_val_if_fail (strlen(block_id) == 40, NULL);
62 g_return_val_if_fail (rw_type == BLOCK_READ || rw_type == BLOCK_WRITE, NULL);
63
64 if (rw_type == BLOCK_READ) {
65 char path[SEAF_PATH_MAX];
66 get_block_path (bend, block_id, path, store_id, version);
67 fd = g_open (path, O_RDONLY | O_BINARY, 0);
68 if (fd < 0) {
69 ccnet_warning ("[block bend] failed to open block %s for read: %s\n",
70 block_id, strerror(errno));
71 return NULL;
72 }
73 } else {
74 fd = open_tmp_file (bend, block_id, &tmp_file);
75 if (fd < 0) {
76 ccnet_warning ("[block bend] failed to open block %s for write: %s\n",
77 block_id, strerror(errno));
78 return NULL;
79 }
80 }
81
82 handle = g_new0(BHandle, 1);
83 handle->fd = fd;
84 memcpy (handle->block_id, block_id, 41);
85 handle->rw_type = rw_type;
86 if (rw_type == BLOCK_WRITE)
87 handle->tmp_file = tmp_file;
88 if (store_id)
89 handle->store_id = g_strdup(store_id);
90 handle->version = version;
91
92 return handle;
93 }
94
95 static int
block_backend_fs_read_block(BlockBackend * bend,BHandle * handle,void * buf,int len)96 block_backend_fs_read_block (BlockBackend *bend,
97 BHandle *handle,
98 void *buf, int len)
99 {
100 int ret;
101
102 ret = readn (handle->fd, buf, len);
103 if (ret < 0)
104 seaf_warning ("Failed to read block %s:%s: %s.\n",
105 handle->store_id, handle->block_id, strerror (errno));
106
107 return ret;
108 }
109
110 static int
block_backend_fs_write_block(BlockBackend * bend,BHandle * handle,const void * buf,int len)111 block_backend_fs_write_block (BlockBackend *bend,
112 BHandle *handle,
113 const void *buf, int len)
114 {
115 int ret;
116
117 ret = writen (handle->fd, buf, len);
118 if (ret < 0)
119 seaf_warning ("Failed to write block %s:%s: %s.\n",
120 handle->store_id, handle->block_id, strerror (errno));
121
122 return ret;
123 }
124
125 static int
block_backend_fs_close_block(BlockBackend * bend,BHandle * handle)126 block_backend_fs_close_block (BlockBackend *bend,
127 BHandle *handle)
128 {
129 int ret;
130
131 ret = close (handle->fd);
132
133 return ret;
134 }
135
136 static void
block_backend_fs_block_handle_free(BlockBackend * bend,BHandle * handle)137 block_backend_fs_block_handle_free (BlockBackend *bend,
138 BHandle *handle)
139 {
140 if (handle->rw_type == BLOCK_WRITE) {
141 /* make sure the tmp file is removed even on failure. */
142 g_unlink (handle->tmp_file);
143 g_free (handle->tmp_file);
144 }
145 g_free (handle->store_id);
146 g_free (handle);
147 }
148
149 static int
create_parent_path(const char * path)150 create_parent_path (const char *path)
151 {
152 char *dir = g_path_get_dirname (path);
153 if (!dir)
154 return -1;
155
156 if (g_file_test (dir, G_FILE_TEST_EXISTS)) {
157 g_free (dir);
158 return 0;
159 }
160
161 if (g_mkdir_with_parents (dir, 0777) < 0) {
162 seaf_warning ("Failed to create object parent path: %s.\n", dir);
163 g_free (dir);
164 return -1;
165 }
166
167 g_free (dir);
168 return 0;
169 }
170
171 static int
block_backend_fs_commit_block(BlockBackend * bend,BHandle * handle)172 block_backend_fs_commit_block (BlockBackend *bend,
173 BHandle *handle)
174 {
175 char path[SEAF_PATH_MAX];
176
177 g_return_val_if_fail (handle->rw_type == BLOCK_WRITE, -1);
178
179 get_block_path (bend, handle->block_id, path, handle->store_id, handle->version);
180
181 if (create_parent_path (path) < 0) {
182 seaf_warning ("Failed to create path for block %s:%s.\n",
183 handle->store_id, handle->block_id);
184 return -1;
185 }
186
187 if (g_rename (handle->tmp_file, path) < 0) {
188 seaf_warning ("[block bend] failed to commit block %s:%s: %s\n",
189 handle->store_id, handle->block_id, strerror(errno));
190 return -1;
191 }
192
193 return 0;
194 }
195
196 static gboolean
block_backend_fs_block_exists(BlockBackend * bend,const char * store_id,int version,const char * block_sha1)197 block_backend_fs_block_exists (BlockBackend *bend,
198 const char *store_id,
199 int version,
200 const char *block_sha1)
201 {
202 char block_path[SEAF_PATH_MAX];
203
204 get_block_path (bend, block_sha1, block_path, store_id, version);
205 if (g_access (block_path, F_OK) == 0)
206 return TRUE;
207 else
208 return FALSE;
209 }
210
211 static int
block_backend_fs_remove_block(BlockBackend * bend,const char * store_id,int version,const char * block_id)212 block_backend_fs_remove_block (BlockBackend *bend,
213 const char *store_id,
214 int version,
215 const char *block_id)
216 {
217 char path[SEAF_PATH_MAX];
218
219 get_block_path (bend, block_id, path, store_id, version);
220
221 return g_unlink (path);
222 }
223
224 static BMetadata *
block_backend_fs_stat_block(BlockBackend * bend,const char * store_id,int version,const char * block_id)225 block_backend_fs_stat_block (BlockBackend *bend,
226 const char *store_id,
227 int version,
228 const char *block_id)
229 {
230 char path[SEAF_PATH_MAX];
231 SeafStat st;
232 BMetadata *block_md;
233
234 get_block_path (bend, block_id, path, store_id, version);
235 if (seaf_stat (path, &st) < 0) {
236 seaf_warning ("[block bend] Failed to stat block %s:%s at %s: %s.\n",
237 store_id, block_id, path, strerror(errno));
238 return NULL;
239 }
240 block_md = g_new0(BMetadata, 1);
241 memcpy (block_md->id, block_id, 40);
242 block_md->size = (uint32_t) st.st_size;
243
244 return block_md;
245 }
246
247 static BMetadata *
block_backend_fs_stat_block_by_handle(BlockBackend * bend,BHandle * handle)248 block_backend_fs_stat_block_by_handle (BlockBackend *bend,
249 BHandle *handle)
250 {
251 SeafStat st;
252 BMetadata *block_md;
253
254 if (seaf_fstat (handle->fd, &st) < 0) {
255 seaf_warning ("[block bend] Failed to stat block %s:%s.\n",
256 handle->store_id, handle->block_id);
257 return NULL;
258 }
259 block_md = g_new0(BMetadata, 1);
260 memcpy (block_md->id, handle->block_id, 40);
261 block_md->size = (uint32_t) st.st_size;
262
263 return block_md;
264 }
265
266 static int
block_backend_fs_foreach_block(BlockBackend * bend,const char * store_id,int version,SeafBlockFunc process,void * user_data)267 block_backend_fs_foreach_block (BlockBackend *bend,
268 const char *store_id,
269 int version,
270 SeafBlockFunc process,
271 void *user_data)
272 {
273 FsPriv *priv = bend->be_priv;
274 char *block_dir = NULL;
275 int dir_len;
276 GDir *dir1 = NULL, *dir2;
277 const char *dname1, *dname2;
278 char block_id[128];
279 char path[SEAF_PATH_MAX], *pos;
280 int ret = 0;
281
282 #if defined MIGRATION
283 if (version > 0)
284 block_dir = g_build_filename (priv->block_dir, store_id, NULL);
285 #else
286 block_dir = g_build_filename (priv->block_dir, store_id, NULL);
287 #endif
288 dir_len = strlen (block_dir);
289
290 dir1 = g_dir_open (block_dir, 0, NULL);
291 if (!dir1) {
292 goto out;
293 }
294
295 memcpy (path, block_dir, dir_len);
296 pos = path + dir_len;
297
298 while ((dname1 = g_dir_read_name(dir1)) != NULL) {
299 snprintf (pos, sizeof(path) - dir_len, "/%s", dname1);
300
301 dir2 = g_dir_open (path, 0, NULL);
302 if (!dir2) {
303 seaf_warning ("Failed to open block dir %s.\n", path);
304 continue;
305 }
306
307 while ((dname2 = g_dir_read_name(dir2)) != NULL) {
308 snprintf (block_id, sizeof(block_id), "%s%s", dname1, dname2);
309 if (!process (store_id, version, block_id, user_data)) {
310 g_dir_close (dir2);
311 goto out;
312 }
313 }
314 g_dir_close (dir2);
315 }
316
317 out:
318 if (dir1)
319 g_dir_close (dir1);
320 g_free (block_dir);
321
322 return ret;
323 }
324
325 static int
block_backend_fs_copy(BlockBackend * bend,const char * src_store_id,int src_version,const char * dst_store_id,int dst_version,const char * block_id)326 block_backend_fs_copy (BlockBackend *bend,
327 const char *src_store_id,
328 int src_version,
329 const char *dst_store_id,
330 int dst_version,
331 const char *block_id)
332 {
333 char src_path[SEAF_PATH_MAX];
334 char dst_path[SEAF_PATH_MAX];
335
336 get_block_path (bend, block_id, src_path, src_store_id, src_version);
337 get_block_path (bend, block_id, dst_path, dst_store_id, dst_version);
338
339 if (g_file_test (dst_path, G_FILE_TEST_EXISTS))
340 return 0;
341
342 if (create_parent_path (dst_path) < 0) {
343 seaf_warning ("Failed to create dst path %s for block %s.\n",
344 dst_path, block_id);
345 return -1;
346 }
347
348 #ifdef WIN32
349 if (!CreateHardLink (dst_path, src_path, NULL)) {
350 seaf_warning ("Failed to link %s to %s: %lu.\n",
351 src_path, dst_path, GetLastError());
352 return -1;
353 }
354 return 0;
355 #else
356 int ret = link (src_path, dst_path);
357 if (ret < 0 && errno != EEXIST) {
358 seaf_warning ("Failed to link %s to %s: %s.\n",
359 src_path, dst_path, strerror(errno));
360 return -1;
361 }
362 return ret;
363 #endif
364 }
365
366 static int
block_backend_fs_remove_store(BlockBackend * bend,const char * store_id)367 block_backend_fs_remove_store (BlockBackend *bend, const char *store_id)
368 {
369 FsPriv *priv = bend->be_priv;
370 char *block_dir = NULL;
371 GDir *dir1, *dir2;
372 const char *dname1, *dname2;
373 char *path1, *path2;
374
375 block_dir = g_build_filename (priv->block_dir, store_id, NULL);
376
377 dir1 = g_dir_open (block_dir, 0, NULL);
378 if (!dir1) {
379 g_free (block_dir);
380 return 0;
381 }
382
383 while ((dname1 = g_dir_read_name(dir1)) != NULL) {
384 path1 = g_build_filename (block_dir, dname1, NULL);
385
386 dir2 = g_dir_open (path1, 0, NULL);
387 if (!dir2) {
388 seaf_warning ("Failed to open block dir %s.\n", path1);
389 g_dir_close (dir1);
390 g_free (path1);
391 g_free (block_dir);
392 return -1;
393 }
394
395 while ((dname2 = g_dir_read_name(dir2)) != NULL) {
396 path2 = g_build_filename (path1, dname2, NULL);
397 g_unlink (path2);
398 g_free (path2);
399 }
400 g_dir_close (dir2);
401
402 g_rmdir (path1);
403 g_free (path1);
404 }
405
406 g_dir_close (dir1);
407 g_rmdir (block_dir);
408 g_free (block_dir);
409
410 return 0;
411 }
412
413 static char *
get_block_path(BlockBackend * bend,const char * block_sha1,char path[],const char * store_id,int version)414 get_block_path (BlockBackend *bend,
415 const char *block_sha1,
416 char path[],
417 const char *store_id,
418 int version)
419 {
420 FsPriv *priv = bend->be_priv;
421 char *pos = path;
422 int n;
423
424 #if defined MIGRATION
425 if (version > 0) {
426 n = snprintf (path, SEAF_PATH_MAX, "%s/%s/", priv->block_dir, store_id);
427 pos += n;
428 } else
429 #else
430 n = snprintf (path, SEAF_PATH_MAX, "%s/%s/", priv->block_dir, store_id);
431 pos += n;
432 #endif
433
434 memcpy (pos, block_sha1, 2);
435 pos[2] = '/';
436 pos += 3;
437
438 memcpy (pos, block_sha1 + 2, 41 - 2);
439
440 return path;
441 }
442
443 static int
open_tmp_file(BlockBackend * bend,const char * basename,char ** path)444 open_tmp_file (BlockBackend *bend,
445 const char *basename,
446 char **path)
447 {
448 FsPriv *priv = bend->be_priv;
449 int fd;
450
451 *path = g_strdup_printf ("%s/%s.XXXXXX", priv->tmp_dir, basename);
452 fd = g_mkstemp (*path);
453 if (fd < 0)
454 g_free (*path);
455
456 return fd;
457 }
458
459 BlockBackend *
block_backend_fs_new(const char * seaf_dir,const char * tmp_dir)460 block_backend_fs_new (const char *seaf_dir, const char *tmp_dir)
461 {
462 BlockBackend *bend;
463 FsPriv *priv;
464
465 bend = g_new0(BlockBackend, 1);
466 priv = g_new0(FsPriv, 1);
467 bend->be_priv = priv;
468
469 priv->block_dir = g_build_filename (seaf_dir, "storage", "blocks", NULL);
470 priv->block_dir_len = strlen (priv->block_dir);
471
472 priv->tmp_dir = g_strdup (tmp_dir);
473 priv->tmp_dir_len = strlen (tmp_dir);
474
475 if (g_mkdir_with_parents (priv->block_dir, 0777) < 0) {
476 seaf_warning ("Block dir %s does not exist and"
477 " is unable to create\n", priv->block_dir);
478 goto onerror;
479 }
480
481 if (g_mkdir_with_parents (tmp_dir, 0777) < 0) {
482 seaf_warning ("Blocks tmp dir %s does not exist and"
483 " is unable to create\n", tmp_dir);
484 goto onerror;
485 }
486
487 bend->open_block = block_backend_fs_open_block;
488 bend->read_block = block_backend_fs_read_block;
489 bend->write_block = block_backend_fs_write_block;
490 bend->commit_block = block_backend_fs_commit_block;
491 bend->close_block = block_backend_fs_close_block;
492 bend->exists = block_backend_fs_block_exists;
493 bend->remove_block = block_backend_fs_remove_block;
494 bend->stat_block = block_backend_fs_stat_block;
495 bend->stat_block_by_handle = block_backend_fs_stat_block_by_handle;
496 bend->block_handle_free = block_backend_fs_block_handle_free;
497 bend->foreach_block = block_backend_fs_foreach_block;
498 bend->remove_store = block_backend_fs_remove_store;
499 bend->copy = block_backend_fs_copy;
500
501 return bend;
502
503 onerror:
504 g_free (bend);
505 g_free (bend->be_priv);
506
507 return NULL;
508 }
509