btrfs-progs: send dump: introduce helper for printing escaped path
[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 <ctype.h>
32 #include <asm/types.h>
33 #include <uuid/uuid.h>
34 #include "utils.h"
35 #include "commands.h"
36 #include "send-utils.h"
37 #include "send-stream.h"
38 #include "send-dump.h"
39
40 #define PATH_CAT_OR_RET(function_name, outpath, path1, path2, ret)      \
41 ({                                                                      \
42         ret = path_cat_out(outpath, path1, path2);                      \
43         if (ret < 0) {                                                  \
44                 error("%s: path invalid: %s\n", function_name, path2);  \
45                 return ret;                                             \
46         }                                                               \
47 })
48
49 /*
50  * Print path and escape chaacters (in a C way) that could break the line.
51  * Returns the length of the escaped characters. Unprintable characters are
52  * escaped as octals.
53  */
54 static int print_path_escaped(const char *path)
55 {
56         size_t i;
57         size_t path_len = strlen(path);
58         int len = 0;
59
60         for (i = 0; i < path_len; i++) {
61                 char c = path[i];
62
63                 len++;
64                 switch (c) {
65                 case '\a': putchar('\\'); putchar('a'); len++; break;
66                 case '\b': putchar('\\'); putchar('b'); len++; break;
67                 case '\e': putchar('\\'); putchar('e'); len++; break;
68                 case '\f': putchar('\\'); putchar('f'); len++; break;
69                 case '\n': putchar('\\'); putchar('n'); len++; break;
70                 case '\r': putchar('\\'); putchar('r'); len++; break;
71                 case '\t': putchar('\\'); putchar('t'); len++; break;
72                 case '\v': putchar('\\'); putchar('v'); len++; break;
73                 case ' ':  putchar('\\'); putchar(' '); len++; break;
74                 case '\\': putchar('\\'); putchar('\\'); len++; break;
75                 default:
76                           if (!isprint(c)) {
77                                   printf("\\%c%c%c",
78                                                   '0' + ((c & 0300) >> 6),
79                                                   '0' + ((c & 070) >> 3),
80                                                   '0' + (c & 07));
81                                   len += 3;
82                           } else {
83                                   putchar(c);
84                           }
85                 }
86         }
87         return len;
88 }
89
90 /*
91  * Underlying PRINT_DUMP, the only difference is how we handle
92  * the full path.
93  */
94 __attribute__ ((format (printf, 5, 6)))
95 static int __print_dump(int subvol, void *user, const char *path,
96                         const char *title, const char *fmt, ...)
97 {
98         struct btrfs_dump_send_args *r = user;
99         char full_path[PATH_MAX] = {0};
100         char *out_path;
101         va_list args;
102         int ret;
103
104         if (subvol) {
105                 PATH_CAT_OR_RET(title, r->full_subvol_path, r->root_path, path, ret);
106                 out_path = r->full_subvol_path;
107         } else {
108                 PATH_CAT_OR_RET(title, full_path, r->full_subvol_path, path, ret);
109                 out_path = full_path;
110         }
111
112         /* Unified header */
113         printf("%-16s%-32s", title, out_path);
114         va_start(args, fmt);
115         /* Operation specified ones */
116         vprintf(fmt, args);
117         va_end(args);
118         putchar('\n');
119         return 0;
120 }
121
122 /* For subvolume/snapshot operation only */
123 #define PRINT_DUMP_SUBVOL(user, path, title, fmt, ...) \
124         __print_dump(1, user, path, title, fmt, ##__VA_ARGS__)
125
126 /* For other operations */
127 #define PRINT_DUMP(user, path, title, fmt, ...) \
128         __print_dump(0, user, path, title, fmt, ##__VA_ARGS__)
129
130 static int print_subvol(const char *path, const u8 *uuid, u64 ctransid,
131                         void *user)
132 {
133         char uuid_str[BTRFS_UUID_UNPARSED_SIZE];
134
135         uuid_unparse(uuid, uuid_str);
136
137         return PRINT_DUMP_SUBVOL(user, path, "subvol", "uuid=%s transid=%llu",
138                                  uuid_str, ctransid);
139 }
140
141 static int print_snapshot(const char *path, const u8 *uuid, u64 ctransid,
142                           const u8 *parent_uuid, u64 parent_ctransid,
143                           void *user)
144 {
145         char uuid_str[BTRFS_UUID_UNPARSED_SIZE];
146         char parent_uuid_str[BTRFS_UUID_UNPARSED_SIZE];
147         int ret;
148
149         uuid_unparse(uuid, uuid_str);
150         uuid_unparse(parent_uuid, parent_uuid_str);
151
152         ret = PRINT_DUMP_SUBVOL(user, path, "snapshot",
153                 "uuid=%s transid=%llu parent_uuid=%s parent_transid=%llu",
154                                 uuid_str, ctransid, parent_uuid_str,
155                                 parent_ctransid);
156         return ret;
157 }
158
159 static int print_mkfile(const char *path, void *user)
160 {
161         return PRINT_DUMP(user, path, "mkfile", NULL);
162 }
163
164 static int print_mkdir(const char *path, void *user)
165 {
166         return PRINT_DUMP(user, path, "mkdir", NULL);
167 }
168
169 static int print_mknod(const char *path, u64 mode, u64 dev, void *user)
170 {
171         return PRINT_DUMP(user, path, "mknod", "mode=%llo dev=0x%llx", mode,
172                           dev);
173 }
174
175 static int print_mkfifo(const char *path, void *user)
176 {
177         return PRINT_DUMP(user, path, "mkfifo", NULL);
178 }
179
180 static int print_mksock(const char *path, void *user)
181 {
182         return PRINT_DUMP(user, path, "mksock", NULL);
183 }
184
185 static int print_symlink(const char *path, const char *lnk, void *user)
186 {
187         return PRINT_DUMP(user, path, "symlink", "dest=%s", lnk);
188 }
189
190 static int print_rename(const char *from, const char *to, void *user)
191 {
192         struct btrfs_dump_send_args *r = user;
193         char full_to[PATH_MAX];
194         int ret;
195
196         PATH_CAT_OR_RET("rename", full_to, r->full_subvol_path, to, ret);
197         return PRINT_DUMP(user, from, "rename", "dest=%s", full_to);
198 }
199
200 static int print_link(const char *path, const char *lnk, void *user)
201 {
202         return PRINT_DUMP(user, path, "link", "dest=%s", lnk);
203 }
204
205 static int print_unlink(const char *path, void *user)
206 {
207         return PRINT_DUMP(user, path, "unlink", NULL);
208 }
209
210 static int print_rmdir(const char *path, void *user)
211 {
212         return PRINT_DUMP(user, path, "rmdir", NULL);
213 }
214
215 static int print_write(const char *path, const void *data, u64 offset,
216                        u64 len, void *user)
217 {
218         return PRINT_DUMP(user, path, "write", "offset=%llu len=%llu",
219                           offset, len);
220 }
221
222 static int print_clone(const char *path, u64 offset, u64 len,
223                        const u8 *clone_uuid, u64 clone_ctransid,
224                        const char *clone_path, u64 clone_offset,
225                        void *user)
226 {
227         struct btrfs_dump_send_args *r = user;
228         char full_path[PATH_MAX];
229         int ret;
230
231         PATH_CAT_OR_RET("clone", full_path, r->full_subvol_path, clone_path,
232                         ret);
233         return PRINT_DUMP(user, path, "clone",
234                           "offset=%llu len=%llu from=%s clone_offset=%llu",
235                           offset, len, full_path, clone_offset);
236 }
237
238 static int print_set_xattr(const char *path, const char *name,
239                            const void *data, int len, void *user)
240 {
241         return PRINT_DUMP(user, path, "set_xattr", "name=%s data=%.*s len=%d",
242                           name, len, (char *)data, len);
243 }
244
245 static int print_remove_xattr(const char *path, const char *name, void *user)
246 {
247
248         return PRINT_DUMP(user, path, "remove_xattr", "name=%s", name);
249 }
250
251 static int print_truncate(const char *path, u64 size, void *user)
252 {
253         return PRINT_DUMP(user, path, "truncate", "size=%llu", size);
254 }
255
256 static int print_chmod(const char *path, u64 mode, void *user)
257 {
258         return PRINT_DUMP(user, path, "chmod", "mode=%llo", mode);
259 }
260
261 static int print_chown(const char *path, u64 uid, u64 gid, void *user)
262 {
263         return PRINT_DUMP(user, path, "chown", "gid=%llu uid=%llu", gid, uid);
264 }
265
266 static int sprintf_timespec(struct timespec *ts, char *dest, int max_size)
267 {
268         struct tm *tm;
269         int ret;
270
271         tm = localtime(&ts->tv_sec);
272         if (!tm) {
273                 error("failed to convert time %lld.%.9ld to local time",
274                       (long long)ts->tv_sec, ts->tv_nsec);
275                 return -EINVAL;
276         }
277         ret = strftime(dest, max_size, "%FT%T%z", tm);
278         if (ret == 0) {
279                 error(
280                 "time %lld.%ld is too long to convert into readable string",
281                       (long long)ts->tv_sec, ts->tv_nsec);
282                 return -EINVAL;
283         }
284         return 0;
285 }
286
287 #define TIME_STRING_MAX 64
288 static int print_utimes(const char *path, struct timespec *at,
289                         struct timespec *mt, struct timespec *ct,
290                         void *user)
291 {
292         char at_str[TIME_STRING_MAX];
293         char mt_str[TIME_STRING_MAX];
294         char ct_str[TIME_STRING_MAX];
295
296         if (sprintf_timespec(at, at_str, TIME_STRING_MAX - 1) < 0 ||
297             sprintf_timespec(mt, mt_str, TIME_STRING_MAX - 1) < 0 ||
298             sprintf_timespec(ct, ct_str, TIME_STRING_MAX - 1) < 0)
299                 return -EINVAL;
300         return PRINT_DUMP(user, path, "utimes", "atime=%s mtime=%s ctime=%s",
301                           at_str, mt_str, ct_str);
302 }
303
304 static int print_update_extent(const char *path, u64 offset, u64 len,
305                                void *user)
306 {
307         return PRINT_DUMP(user, path, "update_extent", "offset=%llu len=%llu",
308                           offset, len);
309 }
310
311 struct btrfs_send_ops btrfs_print_send_ops = {
312         .subvol = print_subvol,
313         .snapshot = print_snapshot,
314         .mkfile = print_mkfile,
315         .mkdir = print_mkdir,
316         .mknod = print_mknod,
317         .mkfifo = print_mkfifo,
318         .mksock = print_mksock,
319         .symlink = print_symlink,
320         .rename = print_rename,
321         .link = print_link,
322         .unlink = print_unlink,
323         .rmdir = print_rmdir,
324         .write = print_write,
325         .clone = print_clone,
326         .set_xattr = print_set_xattr,
327         .remove_xattr = print_remove_xattr,
328         .truncate = print_truncate,
329         .chmod = print_chmod,
330         .chown = print_chown,
331         .utimes = print_utimes,
332         .update_extent = print_update_extent
333 };