btrfs-progs: introduce new send-dump object
[platform/upstream/btrfs-progs.git] / send-dump.c
1 /*
2  * Copyright (C) 2016 Fujitsu.  All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public
6  * License v2 as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public
14  * License along with this program.
15  */
16
17 #include <unistd.h>
18 #include <stdint.h>
19 #include <dirent.h>
20 #include <pthread.h>
21 #include <math.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <sys/ioctl.h>
26 #include <libgen.h>
27 #include <mntent.h>
28 #include <limits.h>
29 #include <stdlib.h>
30 #include <time.h>
31 #include <asm/types.h>
32 #include <uuid/uuid.h>
33 #include "utils.h"
34 #include "commands.h"
35 #include "send-utils.h"
36 #include "send-stream.h"
37 #include "send-dump.h"
38
39 #define PATH_CAT_OR_RET(function_name, outpath, path1, path2, ret)      \
40 ({                                                                      \
41         ret = path_cat_out(outpath, path1, path2);                      \
42         if (ret < 0) {                                                  \
43                 error("%s: path invalid: %s\n", function_name, path2);  \
44                 return ret;                                             \
45         }                                                               \
46 })
47
48 /*
49  * Underlying PRINT_DUMP, the only difference is how we handle
50  * the full path.
51  */
52 __attribute__ ((format (printf, 5, 6)))
53 static int __print_dump(int subvol, void *user, const char *path,
54                         const char *title, const char *fmt, ...)
55 {
56         struct btrfs_dump_send_args *r = user;
57         char full_path[PATH_MAX] = {0};
58         char *out_path;
59         va_list args;
60         int ret;
61
62         if (subvol) {
63                 PATH_CAT_OR_RET(title, r->full_subvol_path, r->root_path, path, ret);
64                 out_path = r->full_subvol_path;
65         } else {
66                 PATH_CAT_OR_RET(title, full_path, r->full_subvol_path, path, ret);
67                 out_path = full_path;
68         }
69
70         /* Unified header */
71         printf("%-16s%-32s", title, out_path);
72         va_start(args, fmt);
73         /* Operation specified ones */
74         vprintf(fmt, args);
75         va_end(args);
76         putchar('\n');
77         return 0;
78 }
79
80 /* For subvolume/snapshot operation only */
81 #define PRINT_DUMP_SUBVOL(user, path, title, fmt, ...) \
82         __print_dump(1, user, path, title, fmt, ##__VA_ARGS__)
83
84 /* For other operations */
85 #define PRINT_DUMP(user, path, title, fmt, ...) \
86         __print_dump(0, user, path, title, fmt, ##__VA_ARGS__)
87
88 static int print_subvol(const char *path, const u8 *uuid, u64 ctransid,
89                         void *user)
90 {
91         char uuid_str[BTRFS_UUID_UNPARSED_SIZE];
92
93         uuid_unparse(uuid, uuid_str);
94
95         return PRINT_DUMP_SUBVOL(user, path, "subvol", "uuid=%s transid=%llu",
96                                  uuid_str, ctransid);
97 }
98
99 static int print_snapshot(const char *path, const u8 *uuid, u64 ctransid,
100                           const u8 *parent_uuid, u64 parent_ctransid,
101                           void *user)
102 {
103         char uuid_str[BTRFS_UUID_UNPARSED_SIZE];
104         char parent_uuid_str[BTRFS_UUID_UNPARSED_SIZE];
105         int ret;
106
107         uuid_unparse(uuid, uuid_str);
108         uuid_unparse(parent_uuid, parent_uuid_str);
109
110         ret = PRINT_DUMP_SUBVOL(user, path, "snapshot",
111                 "uuid=%s transid=%llu parent_uuid=%s parent_transid=%llu",
112                                 uuid_str, ctransid, parent_uuid_str,
113                                 parent_ctransid);
114         return ret;
115 }
116
117 static int print_mkfile(const char *path, void *user)
118 {
119         return PRINT_DUMP(user, path, "mkfile", NULL);
120 }
121
122 static int print_mkdir(const char *path, void *user)
123 {
124         return PRINT_DUMP(user, path, "mkdir", NULL);
125 }
126
127 static int print_mknod(const char *path, u64 mode, u64 dev, void *user)
128 {
129         return PRINT_DUMP(user, path, "mknod", "mode=%llo dev=0x%llx", mode,
130                           dev);
131 }
132
133 static int print_mkfifo(const char *path, void *user)
134 {
135         return PRINT_DUMP(user, path, "mkfifo", NULL);
136 }
137
138 static int print_mksock(const char *path, void *user)
139 {
140         return PRINT_DUMP(user, path, "mksock", NULL);
141 }
142
143 static int print_symlink(const char *path, const char *lnk, void *user)
144 {
145         return PRINT_DUMP(user, path, "symlink", "dest=%s", lnk);
146 }
147
148 static int print_rename(const char *from, const char *to, void *user)
149 {
150         struct btrfs_dump_send_args *r = user;
151         char full_to[PATH_MAX];
152         int ret;
153
154         PATH_CAT_OR_RET("rename", full_to, r->full_subvol_path, to, ret);
155         return PRINT_DUMP(user, from, "rename", "dest=%s", full_to);
156 }
157
158 static int print_link(const char *path, const char *lnk, void *user)
159 {
160         return PRINT_DUMP(user, path, "link", "dest=%s", lnk);
161 }
162
163 static int print_unlink(const char *path, void *user)
164 {
165         return PRINT_DUMP(user, path, "unlink", NULL);
166 }
167
168 static int print_rmdir(const char *path, void *user)
169 {
170         return PRINT_DUMP(user, path, "rmdir", NULL);
171 }
172
173 static int print_write(const char *path, const void *data, u64 offset,
174                        u64 len, void *user)
175 {
176         return PRINT_DUMP(user, path, "write", "offset=%llu len=%llu",
177                           offset, len);
178 }
179
180 static int print_clone(const char *path, u64 offset, u64 len,
181                        const u8 *clone_uuid, u64 clone_ctransid,
182                        const char *clone_path, u64 clone_offset,
183                        void *user)
184 {
185         struct btrfs_dump_send_args *r = user;
186         char full_path[PATH_MAX];
187         int ret;
188
189         PATH_CAT_OR_RET("clone", full_path, r->full_subvol_path, clone_path,
190                         ret);
191         return PRINT_DUMP(user, path, "clone",
192                           "offset=%llu len=%llu from=%s clone_offset=%llu",
193                           offset, len, full_path, clone_offset);
194 }
195
196 static int print_set_xattr(const char *path, const char *name,
197                            const void *data, int len, void *user)
198 {
199         return PRINT_DUMP(user, path, "set_xattr", "name=%s data=%.*s len=%d",
200                           name, len, (char *)data, len);
201 }
202
203 static int print_remove_xattr(const char *path, const char *name, void *user)
204 {
205
206         return PRINT_DUMP(user, path, "remove_xattr", "name=%s", name);
207 }
208
209 static int print_truncate(const char *path, u64 size, void *user)
210 {
211         return PRINT_DUMP(user, path, "truncate", "size=%llu", size);
212 }
213
214 static int print_chmod(const char *path, u64 mode, void *user)
215 {
216         return PRINT_DUMP(user, path, "chmod", "mode=%llo", mode);
217 }
218
219 static int print_chown(const char *path, u64 uid, u64 gid, void *user)
220 {
221         return PRINT_DUMP(user, path, "chown", "gid=%llu uid=%llu", gid, uid);
222 }
223
224 static int sprintf_timespec(struct timespec *ts, char *dest, int max_size)
225 {
226         struct tm *tm;
227         int ret;
228
229         tm = localtime(&ts->tv_sec);
230         if (!tm) {
231                 error("failed to convert time %lld.%.9ld to local time",
232                       (long long)ts->tv_sec, ts->tv_nsec);
233                 return -EINVAL;
234         }
235         ret = strftime(dest, max_size, "%FT%T%z", tm);
236         if (ret == 0) {
237                 error(
238                 "time %lld.%ld is too long to convert into readable string",
239                       (long long)ts->tv_sec, ts->tv_nsec);
240                 return -EINVAL;
241         }
242         return 0;
243 }
244
245 #define TIME_STRING_MAX 64
246 static int print_utimes(const char *path, struct timespec *at,
247                         struct timespec *mt, struct timespec *ct,
248                         void *user)
249 {
250         char at_str[TIME_STRING_MAX];
251         char mt_str[TIME_STRING_MAX];
252         char ct_str[TIME_STRING_MAX];
253
254         if (sprintf_timespec(at, at_str, TIME_STRING_MAX - 1) < 0 ||
255             sprintf_timespec(mt, mt_str, TIME_STRING_MAX - 1) < 0 ||
256             sprintf_timespec(ct, ct_str, TIME_STRING_MAX - 1) < 0)
257                 return -EINVAL;
258         return PRINT_DUMP(user, path, "utimes", "atime=%s mtime=%s ctime=%s",
259                           at_str, mt_str, ct_str);
260 }
261
262 static int print_update_extent(const char *path, u64 offset, u64 len,
263                                void *user)
264 {
265         return PRINT_DUMP(user, path, "update_extent", "offset=%llu len=%llu",
266                           offset, len);
267 }
268
269 struct btrfs_send_ops btrfs_print_send_ops = {
270         .subvol = print_subvol,
271         .snapshot = print_snapshot,
272         .mkfile = print_mkfile,
273         .mkdir = print_mkdir,
274         .mknod = print_mknod,
275         .mkfifo = print_mkfifo,
276         .mksock = print_mksock,
277         .symlink = print_symlink,
278         .rename = print_rename,
279         .link = print_link,
280         .unlink = print_unlink,
281         .rmdir = print_rmdir,
282         .write = print_write,
283         .clone = print_clone,
284         .set_xattr = print_set_xattr,
285         .remove_xattr = print_remove_xattr,
286         .truncate = print_truncate,
287         .chmod = print_chmod,
288         .chown = print_chown,
289         .utimes = print_utimes,
290         .update_extent = print_update_extent
291 };