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