Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : Tar backup command extension
4 : Copyright (C) Aurélien Aptel 2013
5 :
6 : This program is free software; you can redistribute it and/or modify
7 : it under the terms of the GNU General Public License as published by
8 : the Free Software Foundation; either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : This program is distributed in the hope that it will be useful,
12 : but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : GNU General Public License for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : /**
21 : * # General overview of the tar extension
22 : *
23 : * All tar_xxx() functions work on a `struct tar` which store most of
24 : * the context of the backup process.
25 : *
26 : * The current tar context can be accessed via the global variable
27 : * `tar_ctx`. It's publicly exported as an opaque handle via
28 : * tar_get_ctx().
29 : *
30 : * A tar context is first configured through tar_parse_args() which
31 : * can be called from either the CLI (in client.c) or the interactive
32 : * session (via the cmd_tar() callback).
33 : *
34 : * Once the configuration is done (successfully), the context is ready
35 : * for processing and tar_to_process() returns true.
36 : *
37 : * The next step is to call tar_process() which dispatch the
38 : * processing to either tar_create() or tar_extract(), depending on
39 : * the context.
40 : *
41 : * ## Archive creation
42 : *
43 : * tar_create() creates an archive using the libarchive API then
44 : *
45 : * - iterates on the requested paths if the context is in inclusion
46 : * mode with tar_create_from_list()
47 : *
48 : * - or iterates on the whole share (starting from the current dir) if
49 : * in exclusion mode or if no specific path were requested
50 : *
51 : * The do_list() function from client.c is used to list recursively
52 : * the share. In particular it takes a DOS path mask (eg. \mydir\*)
53 : * and a callback function which will be called with each file name
54 : * and attributes. The tar callback function is get_file_callback().
55 : *
56 : * The callback function checks whether the file should be skipped
57 : * according the the configuration via tar_create_skip_path(). If it's
58 : * not skipped it's downloaded and written to the archive in
59 : * tar_get_file().
60 : *
61 : * ## Archive extraction
62 : *
63 : * tar_extract() opens the archive and iterates on each file in
64 : * it. For each file tar_extract_skip_path() checks whether it should
65 : * be skipped according to the config. If it's not skipped it's
66 : * uploaded on the server in tar_send_file().
67 : */
68 :
69 : #include "includes.h"
70 : #include "system/filesys.h"
71 : #include "client/client_proto.h"
72 : #include "client/clitar_proto.h"
73 : #include "libsmb/libsmb.h"
74 : #include "lib/util/util_file.h"
75 :
76 : #ifdef HAVE_LIBARCHIVE
77 :
78 : #include <archive.h>
79 : #include <archive_entry.h>
80 :
81 : /* prepend module name and line number to debug messages */
82 : #define DBG(a, b) (DEBUG(a, ("tar:%-4d ", __LINE__)), DEBUG(a, b))
83 :
84 : /* preprocessor magic to stringify __LINE__ (int) */
85 : #define STR1(x) #x
86 : #define STR2(x) STR1(x)
87 :
88 : /**
89 : * Number of byte in a block unit.
90 : */
91 : #define TAR_BLOCK_UNIT 512
92 :
93 : /**
94 : * Default tar block size in TAR_BLOCK_UNIT.
95 : */
96 : #define TAR_DEFAULT_BLOCK_SIZE 20
97 :
98 : /**
99 : * Maximum value for the blocksize field
100 : */
101 : #define TAR_MAX_BLOCK_SIZE 0xffff
102 :
103 : /**
104 : * Size of the buffer used when downloading a file
105 : */
106 : #define TAR_CLI_READ_SIZE 0xff00
107 :
108 : #define TAR_DO_LIST_ATTR (FILE_ATTRIBUTE_DIRECTORY \
109 : | FILE_ATTRIBUTE_SYSTEM \
110 : | FILE_ATTRIBUTE_HIDDEN)
111 :
112 :
113 : enum tar_operation {
114 : TAR_NO_OPERATION,
115 : TAR_CREATE, /* c flag */
116 : TAR_EXTRACT, /* x flag */
117 : };
118 :
119 : enum tar_selection {
120 : TAR_NO_SELECTION,
121 : TAR_INCLUDE, /* I and F flag, default */
122 : TAR_EXCLUDE, /* X flag */
123 : };
124 :
125 : struct tar {
126 : TALLOC_CTX *talloc_ctx;
127 :
128 : /* in state that needs/can be processed? */
129 : bool to_process;
130 :
131 : /* flags */
132 : struct tar_mode {
133 : enum tar_operation operation; /* create, extract */
134 : enum tar_selection selection; /* include, exclude */
135 : int blocksize; /* size in TAR_BLOCK_UNIT of a tar file block */
136 : bool hidden; /* backup hidden file? */
137 : bool system; /* backup system file? */
138 : bool incremental; /* backup _only_ archived file? */
139 : bool reset; /* unset archive bit? */
140 : bool dry; /* don't write tar file? */
141 : bool regex; /* XXX: never actually using regex... */
142 : bool verbose; /* XXX: ignored */
143 : } mode;
144 :
145 : /* nb of bytes received */
146 : uint64_t total_size;
147 :
148 : /* path to tar archive name */
149 : char *tar_path;
150 :
151 : /* list of path to include or exclude */
152 : char **path_list;
153 : int path_list_size;
154 :
155 : /* archive handle */
156 : struct archive *archive;
157 :
158 : /* counters */
159 : uint64_t numdir;
160 : uint64_t numfile;
161 : };
162 :
163 : /**
164 : * Global context imported in client.c when needed.
165 : *
166 : * Default options.
167 : */
168 : struct tar tar_ctx = {
169 : .mode.selection = TAR_INCLUDE,
170 : .mode.blocksize = TAR_DEFAULT_BLOCK_SIZE,
171 : .mode.hidden = true,
172 : .mode.system = true,
173 : .mode.incremental = false,
174 : .mode.reset = false,
175 : .mode.dry = false,
176 : .mode.regex = false,
177 : .mode.verbose = false,
178 : };
179 :
180 : /* tar, local function */
181 : static int tar_create(struct tar* t);
182 : static int tar_create_from_list(struct tar *t);
183 : static int tar_extract(struct tar *t);
184 : static int tar_read_inclusion_file(struct tar *t, const char* filename);
185 : static int tar_send_file(struct tar *t, struct archive_entry *entry);
186 : static int tar_set_blocksize(struct tar *t, int size);
187 : static int tar_set_newer_than(struct tar *t, const char *filename);
188 : static NTSTATUS tar_add_selection_path(struct tar *t, const char *path);
189 : static void tar_dump(struct tar *t);
190 : static NTSTATUS tar_extract_skip_path(struct tar *t,
191 : struct archive_entry *entry,
192 : bool *_skip);
193 : static TALLOC_CTX *tar_reset_mem_context(struct tar *t);
194 : static void tar_free_mem_context(struct tar *t);
195 : static NTSTATUS tar_create_skip_path(struct tar *t,
196 : const char *fullpath,
197 : const struct file_info *finfo,
198 : bool *_skip);
199 :
200 : static NTSTATUS tar_path_in_list(struct tar *t, const char *path,
201 : bool reverse, bool *_is_in_list);
202 :
203 : static int tar_get_file(struct tar *t,
204 : const char *full_dos_path,
205 : struct file_info *finfo);
206 :
207 : static NTSTATUS get_file_callback(struct cli_state *cli,
208 : struct file_info *finfo,
209 : const char *dir);
210 :
211 : /* utilities */
212 : static char *fix_unix_path(char *path, bool removeprefix);
213 : static NTSTATUS path_base_name(TALLOC_CTX *ctx, const char *path, char **_base);
214 : static const char* skip_useless_char_in_path(const char *p);
215 : static int make_remote_path(const char *full_path);
216 : static int max_token (const char *str);
217 : static NTSTATUS is_subpath(const char *sub, const char *full,
218 : bool *_subpath_match);
219 : /*
220 : * tar_get_ctx - retrieve global tar context handle
221 : */
222 11175 : struct tar *tar_get_ctx(void)
223 : {
224 11175 : return &tar_ctx;
225 : }
226 :
227 : /**
228 : * cmd_block - interactive command to change tar blocksize
229 : *
230 : * Read a size from the client command line and update the current
231 : * blocksize.
232 : */
233 0 : int cmd_block(void)
234 : {
235 : /* XXX: from client.c */
236 : const extern char *cmd_ptr;
237 : char *buf;
238 0 : int err = 0;
239 : bool ok;
240 0 : TALLOC_CTX *ctx = talloc_new(NULL);
241 0 : if (ctx == NULL) {
242 0 : return 1;
243 : }
244 :
245 0 : ok = next_token_talloc(ctx, &cmd_ptr, &buf, NULL);
246 0 : if (!ok) {
247 0 : DBG(0, ("blocksize <n>\n"));
248 0 : err = 1;
249 0 : goto out;
250 : }
251 :
252 0 : ok = tar_set_blocksize(&tar_ctx, atoi(buf));
253 0 : if (ok) {
254 0 : DBG(0, ("invalid blocksize\n"));
255 0 : err = 1;
256 0 : goto out;
257 : }
258 :
259 0 : DBG(2, ("blocksize is now %d\n", tar_ctx.mode.blocksize));
260 :
261 0 : out:
262 0 : talloc_free(ctx);
263 0 : return err;
264 : }
265 :
266 : /**
267 : * cmd_tarmode - interactive command to change tar behaviour
268 : *
269 : * Read one or more modes from the client command line and update the
270 : * current tar mode.
271 : */
272 40 : int cmd_tarmode(void)
273 : {
274 : const extern char *cmd_ptr;
275 : char *buf;
276 : TALLOC_CTX *ctx;
277 :
278 : struct {
279 : const char *cmd;
280 : bool *p;
281 : bool value;
282 40 : } table[] = {
283 : {"full", &tar_ctx.mode.incremental, false},
284 : {"inc", &tar_ctx.mode.incremental, true },
285 : {"reset", &tar_ctx.mode.reset, true },
286 : {"noreset", &tar_ctx.mode.reset, false},
287 : {"system", &tar_ctx.mode.system, true },
288 : {"nosystem", &tar_ctx.mode.system, false},
289 : {"hidden", &tar_ctx.mode.hidden, true },
290 : {"nohidden", &tar_ctx.mode.hidden, false},
291 : {"verbose", &tar_ctx.mode.verbose, false},
292 : {"noverbose", &tar_ctx.mode.verbose, true },
293 : };
294 :
295 40 : ctx = talloc_new(NULL);
296 40 : if (ctx == NULL) {
297 0 : return 1;
298 : }
299 :
300 96 : while (next_token_talloc(ctx, &cmd_ptr, &buf, NULL)) {
301 : size_t i;
302 216 : for (i = 0; i < ARRAY_SIZE(table); i++) {
303 216 : if (strequal(table[i].cmd, buf)) {
304 56 : *table[i].p = table[i].value;
305 56 : break;
306 : }
307 : }
308 :
309 56 : if (i == ARRAY_SIZE(table))
310 0 : d_printf("tarmode: unrecognised option %s\n", buf);
311 : }
312 :
313 200 : d_printf("tarmode is now %s, %s, %s, %s, %s\n",
314 40 : tar_ctx.mode.incremental ? "incremental" : "full",
315 40 : tar_ctx.mode.system ? "system" : "nosystem",
316 40 : tar_ctx.mode.hidden ? "hidden" : "nohidden",
317 40 : tar_ctx.mode.reset ? "reset" : "noreset",
318 40 : tar_ctx.mode.verbose ? "verbose" : "noverbose");
319 :
320 40 : talloc_free(ctx);
321 40 : return 0;
322 : }
323 :
324 : /**
325 : * cmd_tar - interactive command to start a tar backup/restoration
326 : *
327 : * Check presence of argument, parse them and handle the request.
328 : */
329 12 : int cmd_tar(void)
330 : {
331 : const extern char *cmd_ptr;
332 : const char *flag;
333 : const char **val;
334 : char *buf;
335 12 : int maxtok = max_token(cmd_ptr);
336 12 : int i = 0;
337 12 : int err = 0;
338 : bool ok;
339 : int rc;
340 12 : TALLOC_CTX *ctx = talloc_new(NULL);
341 12 : if (ctx == NULL) {
342 0 : return 1;
343 : }
344 :
345 12 : ok = next_token_talloc(ctx, &cmd_ptr, &buf, NULL);
346 12 : if (!ok) {
347 0 : d_printf("tar <c|x>[IXFbgvanN] [options] <tar file> [path list]\n");
348 0 : err = 1;
349 0 : goto out;
350 : }
351 :
352 12 : flag = buf;
353 12 : val = talloc_array(ctx, const char *, maxtok);
354 12 : if (val == NULL) {
355 0 : err = 1;
356 0 : goto out;
357 : }
358 :
359 36 : while (next_token_talloc(ctx, &cmd_ptr, &buf, NULL)) {
360 24 : val[i++] = buf;
361 : }
362 :
363 12 : rc = tar_parse_args(&tar_ctx, flag, val, i);
364 12 : if (rc != 0) {
365 0 : d_printf("parse_args failed\n");
366 0 : err = 1;
367 0 : goto out;
368 : }
369 :
370 12 : rc = tar_process(&tar_ctx);
371 12 : if (rc != 0) {
372 0 : d_printf("tar_process failed\n");
373 0 : err = 1;
374 0 : goto out;
375 : }
376 :
377 12 : out:
378 12 : talloc_free(ctx);
379 12 : return err;
380 : }
381 :
382 :
383 : /**
384 : * tar_parse_args - parse and set tar command line arguments
385 : * @flag: string pointing to tar options
386 : * @val: number of tar arguments
387 : * @valsize: table of arguments after the flags (number of element in val)
388 : *
389 : * tar arguments work in a weird way. For each flag f that takes a
390 : * value v, the user is supposed to type:
391 : *
392 : * on the CLI:
393 : * -Tf1f2f3 v1 v2 v3 TARFILE PATHS...
394 : *
395 : * in the interactive session:
396 : * tar f1f2f3 v1 v2 v3 TARFILE PATHS...
397 : *
398 : * @flag has only flags (eg. "f1f2f3") and @val has the arguments
399 : * (values) following them (eg. ["v1", "v2", "v3", "TARFILE", "PATH1",
400 : * "PATH2"]).
401 : *
402 : * There are only 2 flags that take an arg: b and N. The other flags
403 : * just change the semantic of PATH or TARFILE.
404 : *
405 : * PATH can be a list of included/excluded paths, the path to a file
406 : * containing a list of included/excluded paths to use (F flag). If no
407 : * PATH is provided, the whole share is used (/).
408 : */
409 184 : int tar_parse_args(struct tar* t,
410 : const char *flag,
411 : const char **val,
412 : int valsize)
413 : {
414 : TALLOC_CTX *ctx;
415 184 : bool do_read_list = false;
416 : /* index of next value to use */
417 184 : int ival = 0;
418 : int rc;
419 :
420 184 : if (t == NULL) {
421 0 : DBG_WARNING("Invalid tar context\n");
422 0 : return 1;
423 : }
424 :
425 184 : ctx = tar_reset_mem_context(t);
426 184 : if (ctx == NULL) {
427 0 : return 1;
428 : }
429 : /*
430 : * Reset back some options - could be from interactive version
431 : * all other modes are left as they are
432 : */
433 184 : t->mode.operation = TAR_NO_OPERATION;
434 184 : t->mode.selection = TAR_NO_SELECTION;
435 184 : t->mode.dry = false;
436 184 : t->to_process = false;
437 184 : t->total_size = 0;
438 :
439 504 : while (flag[0] != '\0') {
440 320 : switch(flag[0]) {
441 : /* operation */
442 148 : case 'c':
443 148 : if (t->mode.operation != TAR_NO_OPERATION) {
444 0 : d_printf("Tar must be followed by only one of c or x.\n");
445 0 : return 1;
446 : }
447 148 : t->mode.operation = TAR_CREATE;
448 148 : break;
449 36 : case 'x':
450 36 : if (t->mode.operation != TAR_NO_OPERATION) {
451 0 : d_printf("Tar must be followed by only one of c or x.\n");
452 0 : return 1;
453 : }
454 36 : t->mode.operation = TAR_EXTRACT;
455 36 : break;
456 :
457 : /* selection */
458 8 : case 'I':
459 8 : if (t->mode.selection != TAR_NO_SELECTION) {
460 0 : d_printf("Only one of I,X,F must be specified\n");
461 0 : return 1;
462 : }
463 8 : t->mode.selection = TAR_INCLUDE;
464 8 : break;
465 44 : case 'X':
466 44 : if (t->mode.selection != TAR_NO_SELECTION) {
467 0 : d_printf("Only one of I,X,F must be specified\n");
468 0 : return 1;
469 : }
470 44 : t->mode.selection = TAR_EXCLUDE;
471 44 : break;
472 16 : case 'F':
473 16 : if (t->mode.selection != TAR_NO_SELECTION) {
474 0 : d_printf("Only one of I,X,F must be specified\n");
475 0 : return 1;
476 : }
477 16 : t->mode.selection = TAR_INCLUDE;
478 16 : do_read_list = true;
479 16 : break;
480 :
481 : /* blocksize */
482 0 : case 'b':
483 0 : if (ival >= valsize) {
484 0 : d_printf("Option b must be followed by a blocksize\n");
485 0 : return 1;
486 : }
487 :
488 0 : if (tar_set_blocksize(t, atoi(val[ival]))) {
489 0 : d_printf("Option b must be followed by a valid blocksize\n");
490 0 : return 1;
491 : }
492 :
493 0 : ival++;
494 0 : break;
495 :
496 : /* incremental mode */
497 4 : case 'g':
498 4 : t->mode.incremental = true;
499 4 : break;
500 :
501 : /* newer than */
502 4 : case 'N':
503 4 : if (ival >= valsize) {
504 0 : d_printf("Option N must be followed by valid file name\n");
505 0 : return 1;
506 : }
507 :
508 4 : if (tar_set_newer_than(t, val[ival])) {
509 0 : d_printf("Error setting newer-than time\n");
510 0 : return 1;
511 : }
512 :
513 4 : ival++;
514 4 : break;
515 :
516 : /* reset mode */
517 4 : case 'a':
518 4 : t->mode.reset = true;
519 4 : break;
520 :
521 : /* verbose */
522 0 : case 'v':
523 0 : t->mode.verbose = true;
524 0 : break;
525 :
526 : /* regex match */
527 56 : case 'r':
528 56 : t->mode.regex = true;
529 56 : break;
530 :
531 : /* dry run mode */
532 0 : case 'n':
533 0 : if (t->mode.operation != TAR_CREATE) {
534 0 : d_printf("n is only meaningful when creating a tar-file\n");
535 0 : return 1;
536 : }
537 :
538 0 : t->mode.dry = true;
539 0 : d_printf("dry_run set\n");
540 0 : break;
541 :
542 0 : default:
543 0 : d_printf("Unknown tar option\n");
544 0 : return 1;
545 : }
546 :
547 320 : flag++;
548 : }
549 :
550 : /* no selection given? default selection is include */
551 184 : if (t->mode.selection == TAR_NO_SELECTION) {
552 116 : t->mode.selection = TAR_INCLUDE;
553 : }
554 :
555 184 : if (valsize - ival < 1) {
556 0 : d_printf("No tar file given.\n");
557 0 : return 1;
558 : }
559 :
560 : /* handle TARFILE */
561 184 : t->tar_path = talloc_strdup(ctx, val[ival]);
562 184 : if (t->tar_path == NULL) {
563 0 : return 1;
564 : }
565 184 : ival++;
566 :
567 : /*
568 : * Make sure that dbf points to stderr if we are using stdout for
569 : * tar output
570 : */
571 184 : if (t->mode.operation == TAR_CREATE && strequal(t->tar_path, "-")) {
572 0 : setup_logging("smbclient", DEBUG_STDERR);
573 : }
574 :
575 : /* handle PATHs... */
576 :
577 : /* flag F -> read file list */
578 184 : if (do_read_list) {
579 16 : if (valsize - ival != 1) {
580 0 : d_printf("Option F must be followed by exactly one filename.\n");
581 0 : return 1;
582 : }
583 :
584 16 : rc = tar_read_inclusion_file(t, val[ival]);
585 16 : if (rc != 0) {
586 0 : return 1;
587 : }
588 16 : ival++;
589 : /* otherwise store all the PATHs on the command line */
590 : } else {
591 : int i;
592 352 : for (i = ival; i < valsize; i++) {
593 : NTSTATUS status;
594 184 : status = tar_add_selection_path(t, val[i]);
595 184 : if (!NT_STATUS_IS_OK(status)) {
596 0 : return 1;
597 : }
598 : }
599 : }
600 :
601 184 : t->to_process = true;
602 184 : tar_dump(t);
603 184 : return 0;
604 : }
605 :
606 : /**
607 : * tar_process - start processing archive
608 : *
609 : * The talloc context of the fields is freed at the end of the call.
610 : */
611 184 : int tar_process(struct tar *t)
612 : {
613 184 : int rc = 0;
614 :
615 184 : if (t == NULL) {
616 0 : DBG_WARNING("Invalid tar context\n");
617 0 : return 1;
618 : }
619 :
620 184 : switch(t->mode.operation) {
621 36 : case TAR_EXTRACT:
622 36 : rc = tar_extract(t);
623 36 : break;
624 148 : case TAR_CREATE:
625 148 : rc = tar_create(t);
626 148 : break;
627 0 : default:
628 0 : DBG_WARNING("Invalid tar state\n");
629 0 : rc = 1;
630 : }
631 :
632 184 : t->to_process = false;
633 184 : tar_free_mem_context(t);
634 184 : DBG(5, ("tar_process done, err = %d\n", rc));
635 184 : return rc;
636 : }
637 :
638 : /**
639 : * tar_create - create archive and fetch files
640 : */
641 148 : static int tar_create(struct tar* t)
642 : {
643 : int r;
644 148 : int err = 0;
645 : NTSTATUS status;
646 : const char *mask;
647 : struct timespec tp_start, tp_end;
648 148 : TALLOC_CTX *ctx = talloc_new(NULL);
649 148 : if (ctx == NULL) {
650 0 : return 1;
651 : }
652 :
653 148 : clock_gettime_mono(&tp_start);
654 :
655 148 : t->numfile = 0;
656 148 : t->numdir = 0;
657 148 : t->archive = archive_write_new();
658 :
659 148 : if (!t->mode.dry) {
660 148 : const int bsize = t->mode.blocksize * TAR_BLOCK_UNIT;
661 148 : r = archive_write_set_bytes_per_block(t->archive, bsize);
662 148 : if (r != ARCHIVE_OK) {
663 0 : d_printf("Can't use a block size of %d bytes", bsize);
664 0 : err = 1;
665 0 : goto out;
666 : }
667 :
668 : /*
669 : * Use PAX restricted format which is not the most
670 : * conservative choice but has useful extensions and is widely
671 : * supported
672 : */
673 148 : r = archive_write_set_format_pax_restricted(t->archive);
674 148 : if (r != ARCHIVE_OK) {
675 0 : d_printf("Can't use pax restricted format: %s\n",
676 : archive_error_string(t->archive));
677 0 : err = 1;
678 0 : goto out;
679 : }
680 :
681 148 : if (strequal(t->tar_path, "-")) {
682 0 : r = archive_write_open_fd(t->archive, STDOUT_FILENO);
683 : } else {
684 148 : r = archive_write_open_filename(t->archive, t->tar_path);
685 : }
686 :
687 148 : if (r != ARCHIVE_OK) {
688 0 : d_printf("Can't open %s: %s\n", t->tar_path,
689 : archive_error_string(t->archive));
690 0 : err = 1;
691 0 : goto out_close;
692 : }
693 : }
694 :
695 : /*
696 : * In inclusion mode, iterate on the inclusion list
697 : */
698 148 : if (t->mode.selection == TAR_INCLUDE && t->path_list_size > 0) {
699 108 : if (tar_create_from_list(t)) {
700 0 : err = 1;
701 0 : goto out_close;
702 : }
703 : } else {
704 40 : mask = talloc_asprintf(ctx, "%s\\*", client_get_cur_dir());
705 40 : if (mask == NULL) {
706 0 : err = 1;
707 0 : goto out_close;
708 : }
709 40 : mask = client_clean_name(ctx, mask);
710 40 : if (mask == NULL) {
711 0 : err = 1;
712 0 : goto out_close;
713 : }
714 40 : DBG(5, ("tar_process do_list with mask: %s\n", mask));
715 40 : status = do_list(mask, TAR_DO_LIST_ATTR, get_file_callback, true, true);
716 40 : if (!NT_STATUS_IS_OK(status)) {
717 0 : DBG(0, ("do_list fail %s\n", nt_errstr(status)));
718 0 : err = 1;
719 0 : goto out_close;
720 : }
721 : }
722 :
723 148 : clock_gettime_mono(&tp_end);
724 148 : d_printf("tar: dumped %"PRIu64" files and %"PRIu64" directories\n",
725 : t->numfile, t->numdir);
726 148 : d_printf("Total bytes written: %"PRIu64" (%.1f MiB/s)\n",
727 : t->total_size,
728 148 : t->total_size/timespec_elapsed2(&tp_start, &tp_end)/1024/1024);
729 :
730 148 : out_close:
731 :
732 148 : if (!t->mode.dry) {
733 148 : r = archive_write_close(t->archive);
734 148 : if (r != ARCHIVE_OK) {
735 0 : d_printf("Fatal: %s\n", archive_error_string(t->archive));
736 0 : err = 1;
737 0 : goto out;
738 : }
739 : }
740 148 : out:
741 : #ifdef HAVE_ARCHIVE_READ_FREE
742 148 : archive_write_free(t->archive);
743 : #else
744 : archive_write_finish(t->archive);
745 : #endif
746 148 : talloc_free(ctx);
747 148 : return err;
748 : }
749 :
750 : /**
751 : * tar_create_from_list - fetch from path list in include mode
752 : */
753 108 : static int tar_create_from_list(struct tar *t)
754 : {
755 108 : int err = 0;
756 : NTSTATUS status;
757 : char *base;
758 : const char *path, *mask, *start_dir;
759 : int i;
760 108 : TALLOC_CTX *ctx = talloc_new(NULL);
761 108 : if (ctx == NULL) {
762 0 : return 1;
763 : }
764 :
765 108 : start_dir = talloc_strdup(ctx, client_get_cur_dir());
766 108 : if (start_dir == NULL) {
767 0 : err = 1;
768 0 : goto out;
769 : }
770 :
771 232 : for (i = 0; i < t->path_list_size; i++) {
772 124 : path = t->path_list[i];
773 124 : base = NULL;
774 124 : status = path_base_name(ctx, path, &base);
775 124 : if (!NT_STATUS_IS_OK(status)) {
776 0 : err = 1;
777 0 : goto out;
778 : }
779 124 : mask = talloc_asprintf(ctx, "%s\\%s",
780 : client_get_cur_dir(), path);
781 124 : if (mask == NULL) {
782 0 : err = 1;
783 0 : goto out;
784 : }
785 124 : mask = client_clean_name(ctx, mask);
786 124 : if (mask == NULL) {
787 0 : err = 1;
788 0 : goto out;
789 : }
790 :
791 124 : DBG(5, ("incl. path='%s', base='%s', mask='%s'\n",
792 : path, base ? base : "NULL", mask));
793 :
794 124 : if (base != NULL) {
795 28 : base = talloc_asprintf(ctx, "%s%s\\",
796 : client_get_cur_dir(), base);
797 28 : if (base == NULL) {
798 0 : err = 1;
799 0 : goto out;
800 : }
801 28 : base = client_clean_name(ctx, base);
802 28 : if (base == NULL) {
803 0 : err = 1;
804 0 : goto out;
805 : }
806 :
807 28 : DBG(5, ("cd '%s' before do_list\n", base));
808 28 : client_set_cur_dir(base);
809 : }
810 124 : status = do_list(mask, TAR_DO_LIST_ATTR, get_file_callback, true, true);
811 124 : if (base != NULL) {
812 28 : client_set_cur_dir(start_dir);
813 : }
814 124 : if (!NT_STATUS_IS_OK(status)) {
815 0 : DBG(0, ("do_list failed on %s (%s)\n", path, nt_errstr(status)));
816 0 : err = 1;
817 0 : goto out;
818 : }
819 : }
820 :
821 108 : out:
822 108 : talloc_free(ctx);
823 108 : return err;
824 : }
825 :
826 : /**
827 : * get_file_callback - do_list callback
828 : *
829 : * Callback for client.c do_list(). Called for each file found on the
830 : * share matching do_list mask. Recursively call do_list() with itself
831 : * as callback when the current file is a directory.
832 : */
833 1528 : static NTSTATUS get_file_callback(struct cli_state *cli,
834 : struct file_info *finfo,
835 : const char *dir)
836 : {
837 1528 : NTSTATUS status = NT_STATUS_OK;
838 : char *remote_name;
839 1528 : char *old_dir = NULL;
840 1528 : char *new_dir = NULL;
841 1528 : const char *initial_dir = dir;
842 1528 : bool skip = false;
843 : bool isdir;
844 : int rc;
845 1528 : TALLOC_CTX *ctx = talloc_new(NULL);
846 1528 : if (ctx == NULL) {
847 0 : return NT_STATUS_NO_MEMORY;
848 : }
849 :
850 1528 : remote_name = talloc_asprintf(ctx, "%s\\%s", initial_dir, finfo->name);
851 1528 : if (remote_name == NULL) {
852 0 : status = NT_STATUS_NO_MEMORY;
853 0 : goto out;
854 : }
855 1528 : remote_name = client_clean_name(ctx, remote_name);
856 1528 : if (remote_name == NULL) {
857 0 : status = NT_STATUS_NO_MEMORY;
858 0 : goto out;
859 : }
860 :
861 1528 : if (strequal(finfo->name, "..") || strequal(finfo->name, ".")) {
862 496 : goto out;
863 : }
864 :
865 1032 : isdir = finfo->attr & FILE_ATTRIBUTE_DIRECTORY;
866 1032 : if (isdir) {
867 204 : old_dir = talloc_strdup(ctx, initial_dir);
868 204 : new_dir = talloc_asprintf(ctx, "%s\\", remote_name);
869 204 : if ((old_dir == NULL) || (new_dir == NULL)) {
870 0 : status = NT_STATUS_NO_MEMORY;
871 0 : goto out;
872 : }
873 : }
874 :
875 1032 : status = tar_create_skip_path(&tar_ctx,
876 : isdir ? new_dir : remote_name,
877 : finfo, &skip);
878 1032 : if (!NT_STATUS_IS_OK(status)) {
879 0 : goto out;
880 : }
881 :
882 1032 : if (skip) {
883 288 : DBG(5, ("--- %s\n", remote_name));
884 288 : status = NT_STATUS_OK;
885 288 : goto out;
886 : }
887 :
888 744 : rc = tar_get_file(&tar_ctx, remote_name, finfo);
889 744 : if (rc != 0) {
890 0 : status = NT_STATUS_UNSUCCESSFUL;
891 0 : goto out;
892 : }
893 :
894 744 : out:
895 1528 : talloc_free(ctx);
896 1528 : return status;
897 : }
898 :
899 : /**
900 : * tar_get_file - fetch a remote file to the local archive
901 : * @full_dos_path: path to the file to fetch
902 : * @finfo: attributes of the file to fetch
903 : */
904 744 : static int tar_get_file(struct tar *t,
905 : const char *full_dos_path,
906 : struct file_info *finfo)
907 : {
908 : extern struct cli_state *cli;
909 : NTSTATUS status;
910 : struct archive_entry *entry;
911 : char *full_unix_path;
912 : char buf[TAR_CLI_READ_SIZE];
913 : size_t len;
914 744 : uint64_t off = 0;
915 744 : uint16_t remote_fd = (uint16_t)-1;
916 744 : int err = 0, r;
917 744 : const bool isdir = finfo->attr & FILE_ATTRIBUTE_DIRECTORY;
918 744 : TALLOC_CTX *ctx = talloc_new(NULL);
919 :
920 744 : if (ctx == NULL) {
921 0 : return 1;
922 : }
923 :
924 744 : DBG(5, ("+++ %s\n", full_dos_path));
925 :
926 744 : t->total_size += finfo->size;
927 :
928 744 : if (t->mode.dry) {
929 0 : goto out;
930 : }
931 :
932 744 : if (t->mode.reset) {
933 : /* ignore return value: server might not store DOS attributes */
934 32 : set_remote_attr(full_dos_path, FILE_ATTRIBUTE_ARCHIVE, ATTR_UNSET);
935 : }
936 :
937 744 : full_unix_path = talloc_asprintf(ctx, ".%s", full_dos_path);
938 744 : if (full_unix_path == NULL) {
939 0 : err = 1;
940 0 : goto out;
941 : }
942 744 : string_replace(full_unix_path, '\\', '/');
943 744 : entry = archive_entry_new();
944 744 : archive_entry_copy_pathname(entry, full_unix_path);
945 744 : archive_entry_set_filetype(entry, isdir ? AE_IFDIR : AE_IFREG);
946 744 : archive_entry_set_atime(entry,
947 : finfo->atime_ts.tv_sec,
948 : finfo->atime_ts.tv_nsec);
949 744 : archive_entry_set_mtime(entry,
950 : finfo->mtime_ts.tv_sec,
951 : finfo->mtime_ts.tv_nsec);
952 744 : archive_entry_set_ctime(entry,
953 : finfo->ctime_ts.tv_sec,
954 : finfo->ctime_ts.tv_nsec);
955 744 : archive_entry_set_perm(entry, isdir ? 0755 : 0644);
956 : /*
957 : * check if we can safely cast unsigned file size to libarchive
958 : * signed size. Very unlikely problem (>9 exabyte file)
959 : */
960 744 : if (finfo->size > INT64_MAX) {
961 0 : d_printf("Remote file %s too big\n", full_dos_path);
962 0 : goto out_entry;
963 : }
964 :
965 744 : archive_entry_set_size(entry, (int64_t)finfo->size);
966 :
967 744 : if (isdir) {
968 : /* It's a directory just write a header */
969 188 : r = archive_write_header(t->archive, entry);
970 188 : if (r != ARCHIVE_OK) {
971 0 : d_printf("Fatal: %s\n", archive_error_string(t->archive));
972 0 : err = 1;
973 : }
974 188 : if (t->mode.verbose) {
975 0 : d_printf("a %s\\\n", full_dos_path);
976 : }
977 188 : DBG(5, ("get_file skip dir %s\n", full_dos_path));
978 188 : goto out_entry;
979 : }
980 :
981 556 : status = cli_open(cli, full_dos_path, O_RDONLY, DENY_NONE, &remote_fd);
982 556 : if (!NT_STATUS_IS_OK(status)) {
983 0 : d_printf("%s opening remote file %s\n",
984 : nt_errstr(status), full_dos_path);
985 0 : goto out_entry;
986 : }
987 :
988 : /* don't make tar file entry until after the file is open */
989 556 : r = archive_write_header(t->archive, entry);
990 556 : if (r != ARCHIVE_OK) {
991 0 : d_printf("Fatal: %s\n", archive_error_string(t->archive));
992 0 : err = 1;
993 0 : goto out_entry;
994 : }
995 :
996 556 : if (t->mode.verbose) {
997 0 : d_printf("a %s\n", full_dos_path);
998 : }
999 :
1000 : do {
1001 1541 : status = cli_read(cli, remote_fd, buf, off, sizeof(buf), &len);
1002 1541 : if (!NT_STATUS_IS_OK(status)) {
1003 0 : d_printf("Error reading file %s : %s\n",
1004 : full_dos_path, nt_errstr(status));
1005 0 : err = 1;
1006 0 : goto out_close;
1007 : }
1008 :
1009 1541 : off += len;
1010 :
1011 1541 : r = archive_write_data(t->archive, buf, len);
1012 1541 : if (r < 0) {
1013 0 : d_printf("Fatal: %s\n", archive_error_string(t->archive));
1014 0 : err = 1;
1015 0 : goto out_close;
1016 : }
1017 :
1018 1541 : } while (off < finfo->size);
1019 556 : t->numfile++;
1020 :
1021 556 : out_close:
1022 556 : cli_close(cli, remote_fd);
1023 :
1024 744 : out_entry:
1025 744 : archive_entry_free(entry);
1026 :
1027 744 : out:
1028 744 : talloc_free(ctx);
1029 744 : return err;
1030 : }
1031 :
1032 : /**
1033 : * tar_extract - open archive and send files.
1034 : */
1035 36 : static int tar_extract(struct tar *t)
1036 : {
1037 36 : int err = 0;
1038 : int r;
1039 : struct archive_entry *entry;
1040 36 : const size_t bsize = t->mode.blocksize * TAR_BLOCK_UNIT;
1041 : int rc;
1042 :
1043 36 : t->archive = archive_read_new();
1044 36 : archive_read_support_format_all(t->archive);
1045 : #ifdef HAVE_ARCHIVE_READ_SUPPORT_FILTER_ALL
1046 36 : archive_read_support_filter_all(t->archive);
1047 : #endif
1048 :
1049 36 : if (strequal(t->tar_path, "-")) {
1050 0 : r = archive_read_open_fd(t->archive, STDIN_FILENO, bsize);
1051 : } else {
1052 36 : r = archive_read_open_filename(t->archive, t->tar_path, bsize);
1053 : }
1054 :
1055 36 : if (r != ARCHIVE_OK) {
1056 0 : d_printf("Can't open %s : %s\n", t->tar_path,
1057 : archive_error_string(t->archive));
1058 0 : err = 1;
1059 0 : goto out;
1060 : }
1061 :
1062 364 : for (;;) {
1063 : NTSTATUS status;
1064 400 : bool skip = false;
1065 400 : r = archive_read_next_header(t->archive, &entry);
1066 400 : if (r == ARCHIVE_EOF) {
1067 36 : break;
1068 : }
1069 364 : if (r == ARCHIVE_WARN) {
1070 0 : d_printf("Warning: %s\n", archive_error_string(t->archive));
1071 : }
1072 364 : if (r == ARCHIVE_FATAL) {
1073 0 : d_printf("Fatal: %s\n", archive_error_string(t->archive));
1074 0 : err = 1;
1075 0 : goto out;
1076 : }
1077 :
1078 364 : status = tar_extract_skip_path(t, entry, &skip);
1079 364 : if (!NT_STATUS_IS_OK(status)) {
1080 0 : err = 1;
1081 0 : goto out;
1082 : }
1083 364 : if (skip) {
1084 140 : DBG(5, ("--- %s\n", archive_entry_pathname(entry)));
1085 140 : continue;
1086 : }
1087 224 : DBG(5, ("+++ %s\n", archive_entry_pathname(entry)));
1088 :
1089 224 : if (t->mode.verbose) {
1090 0 : d_printf("x %s\n", archive_entry_pathname(entry));
1091 : }
1092 :
1093 224 : rc = tar_send_file(t, entry);
1094 224 : if (rc != 0) {
1095 0 : err = 1;
1096 0 : goto out;
1097 : }
1098 : }
1099 :
1100 36 : out:
1101 : #ifdef HAVE_ARCHIVE_READ_FREE
1102 36 : r = archive_read_free(t->archive);
1103 : #else
1104 : r = archive_read_finish(t->archive);
1105 : #endif
1106 36 : if (r != ARCHIVE_OK) {
1107 0 : d_printf("Can't close %s : %s\n", t->tar_path,
1108 : archive_error_string(t->archive));
1109 0 : err = 1;
1110 : }
1111 36 : return err;
1112 : }
1113 :
1114 : /**
1115 : * tar_send_file - send @entry to the remote server
1116 : * @entry: current archive entry
1117 : *
1118 : * Handle the creation of the parent directories and transfer the
1119 : * entry to a new remote file.
1120 : */
1121 224 : static int tar_send_file(struct tar *t, struct archive_entry *entry)
1122 : {
1123 : extern struct cli_state *cli;
1124 : char *dos_path;
1125 : char *full_path;
1126 : NTSTATUS status;
1127 224 : uint16_t remote_fd = (uint16_t) -1;
1128 224 : int err = 0;
1129 224 : int flags = O_RDWR | O_CREAT | O_TRUNC;
1130 224 : mode_t filetype = archive_entry_filetype(entry);
1131 224 : mode_t mode = archive_entry_mode(entry);
1132 224 : time_t mtime = archive_entry_mtime(entry);
1133 : int rc;
1134 224 : TALLOC_CTX *ctx = talloc_new(NULL);
1135 224 : if (ctx == NULL) {
1136 0 : return 1;
1137 : }
1138 :
1139 224 : dos_path = talloc_strdup(ctx, archive_entry_pathname(entry));
1140 224 : if (dos_path == NULL) {
1141 0 : err = 1;
1142 0 : goto out;
1143 : }
1144 224 : fix_unix_path(dos_path, true);
1145 :
1146 224 : full_path = talloc_strdup(ctx, client_get_cur_dir());
1147 224 : if (full_path == NULL) {
1148 0 : err = 1;
1149 0 : goto out;
1150 : }
1151 224 : full_path = talloc_strdup_append(full_path, dos_path);
1152 224 : if (full_path == NULL) {
1153 0 : err = 1;
1154 0 : goto out;
1155 : }
1156 224 : full_path = client_clean_name(ctx, full_path);
1157 224 : if (full_path == NULL) {
1158 0 : err = 1;
1159 0 : goto out;
1160 : }
1161 :
1162 224 : if (filetype != AE_IFREG && filetype != AE_IFDIR) {
1163 0 : d_printf("Skipping non-dir & non-regular file %s\n", full_path);
1164 0 : goto out;
1165 : }
1166 :
1167 224 : rc = make_remote_path(full_path);
1168 224 : if (rc != 0) {
1169 0 : err = 1;
1170 0 : goto out;
1171 : }
1172 :
1173 224 : if (filetype == AE_IFDIR) {
1174 40 : goto out;
1175 : }
1176 :
1177 184 : status = cli_open(cli, full_path, flags, DENY_NONE, &remote_fd);
1178 184 : if (!NT_STATUS_IS_OK(status)) {
1179 0 : d_printf("Error opening remote file %s: %s\n",
1180 : full_path, nt_errstr(status));
1181 0 : err = 1;
1182 0 : goto out;
1183 : }
1184 :
1185 268 : for (;;) {
1186 : const void *buf;
1187 : size_t len;
1188 : off_t off;
1189 : int r;
1190 :
1191 452 : r = archive_read_data_block(t->archive, &buf, &len, &off);
1192 452 : if (r == ARCHIVE_EOF) {
1193 184 : break;
1194 : }
1195 268 : if (r == ARCHIVE_WARN) {
1196 0 : d_printf("Warning: %s\n", archive_error_string(t->archive));
1197 : }
1198 268 : if (r == ARCHIVE_FATAL) {
1199 0 : d_printf("Fatal: %s\n", archive_error_string(t->archive));
1200 0 : err = 1;
1201 0 : goto close_out;
1202 : }
1203 :
1204 268 : status = cli_writeall(cli, remote_fd, 0, buf, off, len, NULL);
1205 268 : if (!NT_STATUS_IS_OK(status)) {
1206 0 : d_printf("Error writing remote file %s: %s\n",
1207 : full_path, nt_errstr(status));
1208 0 : err = 1;
1209 0 : goto close_out;
1210 : }
1211 : }
1212 :
1213 184 : close_out:
1214 184 : status = cli_close(cli, remote_fd);
1215 184 : if (!NT_STATUS_IS_OK(status)) {
1216 0 : d_printf("Error closing remote file %s: %s\n",
1217 : full_path, nt_errstr(status));
1218 0 : err = 1;
1219 : }
1220 :
1221 184 : status = cli_setatr(cli, full_path, mode, mtime);
1222 184 : if (!NT_STATUS_IS_OK(status)) {
1223 0 : d_printf("Error setting attributes on remote file %s: %s\n",
1224 : full_path, nt_errstr(status));
1225 0 : err = 1;
1226 : }
1227 :
1228 184 : out:
1229 224 : talloc_free(ctx);
1230 224 : return err;
1231 : }
1232 :
1233 : /**
1234 : * tar_add_selection_path - add a path to the path list
1235 : * @path: path to add
1236 : */
1237 224 : static NTSTATUS tar_add_selection_path(struct tar *t, const char *path)
1238 : {
1239 : const char **list;
1240 224 : TALLOC_CTX *ctx = t->talloc_ctx;
1241 224 : if (!t->path_list) {
1242 164 : t->path_list = str_list_make_empty(ctx);
1243 164 : if (t->path_list == NULL) {
1244 0 : return NT_STATUS_NO_MEMORY;
1245 : }
1246 164 : t->path_list_size = 0;
1247 : }
1248 :
1249 : /* cast to silence gcc const-qual warning */
1250 224 : list = str_list_add((void *)t->path_list, path);
1251 224 : if (list == NULL) {
1252 0 : return NT_STATUS_NO_MEMORY;
1253 : }
1254 224 : t->path_list = discard_const_p(char *, list);
1255 224 : t->path_list_size++;
1256 224 : fix_unix_path(t->path_list[t->path_list_size - 1], true);
1257 :
1258 224 : return NT_STATUS_OK;
1259 : }
1260 :
1261 : /**
1262 : * tar_set_blocksize - set block size in TAR_BLOCK_UNIT
1263 : */
1264 0 : static int tar_set_blocksize(struct tar *t, int size)
1265 : {
1266 0 : if (size <= 0 || size > TAR_MAX_BLOCK_SIZE) {
1267 0 : return 1;
1268 : }
1269 :
1270 0 : t->mode.blocksize = size;
1271 :
1272 0 : return 0;
1273 : }
1274 :
1275 : /**
1276 : * tar_set_newer_than - set date threshold of saved files
1277 : * @filename: local path to a file
1278 : *
1279 : * Only files newer than the modification time of @filename will be
1280 : * saved.
1281 : *
1282 : * Note: this function set the global variable newer_than from
1283 : * client.c. Thus the time is not a field of the tar structure. See
1284 : * cmd_newer() to change its value from an interactive session.
1285 : */
1286 4 : static int tar_set_newer_than(struct tar *t, const char *filename)
1287 : {
1288 : extern time_t newer_than;
1289 : SMB_STRUCT_STAT stbuf;
1290 : int rc;
1291 :
1292 4 : rc = sys_stat(filename, &stbuf, false);
1293 4 : if (rc != 0) {
1294 0 : d_printf("Error setting newer-than time\n");
1295 0 : return 1;
1296 : }
1297 :
1298 4 : newer_than = convert_timespec_to_time_t(stbuf.st_ex_mtime);
1299 4 : DBG(1, ("Getting files newer than %s", time_to_asc(newer_than)));
1300 4 : return 0;
1301 : }
1302 :
1303 : /**
1304 : * tar_read_inclusion_file - set path list from file
1305 : * @filename: path to the list file
1306 : *
1307 : * Read and add each line of @filename to the path list.
1308 : */
1309 16 : static int tar_read_inclusion_file(struct tar *t, const char* filename)
1310 : {
1311 : char *line;
1312 16 : int err = 0;
1313 16 : int fd = -1;
1314 16 : TALLOC_CTX *ctx = talloc_new(NULL);
1315 16 : if (ctx == NULL) {
1316 0 : return 1;
1317 : }
1318 :
1319 16 : fd = open(filename, O_RDONLY);
1320 16 : if (fd < 0) {
1321 0 : d_printf("Can't open inclusion file '%s': %s\n", filename, strerror(errno));
1322 0 : err = 1;
1323 0 : goto out;
1324 : }
1325 :
1326 16 : for (line = afdgets(fd, ctx, 0);
1327 56 : line != NULL;
1328 40 : line = afdgets(fd, ctx, 0)) {
1329 : NTSTATUS status;
1330 40 : status = tar_add_selection_path(t, line);
1331 40 : if (!NT_STATUS_IS_OK(status)) {
1332 0 : err = 1;
1333 0 : goto out;
1334 : }
1335 : }
1336 :
1337 16 : out:
1338 16 : if (fd != -1) {
1339 16 : close(fd);
1340 : }
1341 16 : talloc_free(ctx);
1342 16 : return err;
1343 : }
1344 :
1345 : /**
1346 : * tar_path_in_list - check whether @path is in the path list
1347 : * @path: path to find
1348 : * @reverse: when true also try to find path list element in @path
1349 : * @_is_in_list: set if @path is in the path list
1350 : *
1351 : * Look at each path of the path list and set @_is_in_list if @path is a
1352 : * subpath of one of them.
1353 : *
1354 : * If you want /path to be in the path list (path/a/, path/b/) set
1355 : * @reverse to true to try to match the other way around.
1356 : */
1357 160 : static NTSTATUS tar_path_in_list(struct tar *t, const char *path,
1358 : bool reverse, bool *_is_in_list)
1359 : {
1360 : int i;
1361 : const char *p;
1362 : const char *pattern;
1363 :
1364 160 : if (path == NULL || path[0] == '\0') {
1365 0 : *_is_in_list = false;
1366 0 : return NT_STATUS_OK;
1367 : }
1368 :
1369 160 : p = skip_useless_char_in_path(path);
1370 :
1371 436 : for (i = 0; i < t->path_list_size; i++) {
1372 : bool is_in_list;
1373 : NTSTATUS status;
1374 :
1375 364 : pattern = skip_useless_char_in_path(t->path_list[i]);
1376 364 : status = is_subpath(p, pattern, &is_in_list);
1377 364 : if (!NT_STATUS_IS_OK(status)) {
1378 88 : return status;
1379 : }
1380 364 : if (reverse && !is_in_list) {
1381 0 : status = is_subpath(pattern, p, &is_in_list);
1382 0 : if (!NT_STATUS_IS_OK(status)) {
1383 0 : return status;
1384 : }
1385 : }
1386 364 : if (is_in_list) {
1387 88 : *_is_in_list = true;
1388 88 : return NT_STATUS_OK;
1389 : }
1390 : }
1391 :
1392 72 : *_is_in_list = false;
1393 72 : return NT_STATUS_OK;
1394 : }
1395 :
1396 : /**
1397 : * tar_extract_skip_path - check if @entry should be skipped
1398 : * @entry: current tar entry
1399 : * @_skip: set true if path should be skipped, otherwise false
1400 : *
1401 : * Skip predicate for tar extraction (archive to server) only.
1402 : */
1403 364 : static NTSTATUS tar_extract_skip_path(struct tar *t,
1404 : struct archive_entry *entry,
1405 : bool *_skip)
1406 : {
1407 364 : const char *fullpath = archive_entry_pathname(entry);
1408 364 : bool in = true;
1409 :
1410 364 : if (t->path_list_size <= 0) {
1411 68 : *_skip = false;
1412 68 : return NT_STATUS_OK;
1413 : }
1414 :
1415 296 : if (t->mode.regex) {
1416 176 : in = mask_match_list(fullpath, t->path_list, t->path_list_size, true);
1417 : } else {
1418 120 : NTSTATUS status = tar_path_in_list(t, fullpath, false, &in);
1419 120 : if (!NT_STATUS_IS_OK(status)) {
1420 0 : return status;
1421 : }
1422 : }
1423 :
1424 296 : if (t->mode.selection == TAR_EXCLUDE) {
1425 128 : *_skip = in;
1426 : } else {
1427 168 : *_skip = !in;
1428 : }
1429 :
1430 296 : return NT_STATUS_OK;
1431 : }
1432 :
1433 : /**
1434 : * tar_create_skip_path - check if @fullpath should be skipped
1435 : * @fullpath: full remote path of the current file
1436 : * @finfo: remote file attributes
1437 : * @_skip: returned skip not
1438 : *
1439 : * Skip predicate for tar creation (server to archive) only.
1440 : */
1441 1032 : static NTSTATUS tar_create_skip_path(struct tar *t,
1442 : const char *fullpath,
1443 : const struct file_info *finfo,
1444 : bool *_skip)
1445 : {
1446 : /* syntaxic sugar */
1447 1032 : const mode_t mode = finfo->attr;
1448 1032 : const bool isdir = mode & FILE_ATTRIBUTE_DIRECTORY;
1449 1032 : const bool exclude = t->mode.selection == TAR_EXCLUDE;
1450 1032 : bool in = true;
1451 :
1452 1032 : if (!isdir) {
1453 :
1454 : /* 1. if we don't want X and we have X, skip */
1455 828 : if (!t->mode.system && (mode & FILE_ATTRIBUTE_SYSTEM)) {
1456 96 : *_skip = true;
1457 96 : return NT_STATUS_OK;
1458 : }
1459 :
1460 732 : if (!t->mode.hidden && (mode & FILE_ATTRIBUTE_HIDDEN)) {
1461 64 : *_skip = true;
1462 64 : return NT_STATUS_OK;
1463 : }
1464 :
1465 : /* 2. if we only want archive and it's not, skip */
1466 :
1467 668 : if (t->mode.incremental && !(mode & FILE_ATTRIBUTE_ARCHIVE)) {
1468 56 : *_skip = true;
1469 56 : return NT_STATUS_OK;
1470 : }
1471 : }
1472 :
1473 : /* 3. is it in the selection list? */
1474 :
1475 : /*
1476 : * tar_create_from_list() use the include list as a starting
1477 : * point, no need to check
1478 : */
1479 816 : if (!exclude) {
1480 636 : *_skip = false;
1481 636 : return NT_STATUS_OK;
1482 : }
1483 :
1484 : /* we are now in exclude mode */
1485 :
1486 : /* no matter the selection, no list => include everything */
1487 180 : if (t->path_list_size <= 0) {
1488 20 : *_skip = false;
1489 20 : return NT_STATUS_OK;
1490 : }
1491 :
1492 160 : if (t->mode.regex) {
1493 120 : in = mask_match_list(fullpath, t->path_list, t->path_list_size, true);
1494 : } else {
1495 40 : bool reverse = isdir && !exclude;
1496 40 : NTSTATUS status = tar_path_in_list(t, fullpath, reverse, &in);
1497 40 : if (!NT_STATUS_IS_OK(status)) {
1498 0 : return status;
1499 : }
1500 : }
1501 160 : *_skip = in;
1502 :
1503 160 : return NT_STATUS_OK;
1504 : }
1505 :
1506 : /**
1507 : * tar_to_process - return true if @t is ready to be processed
1508 : *
1509 : * @t is ready if it properly parsed command line arguments.
1510 : */
1511 21994 : bool tar_to_process(struct tar *t)
1512 : {
1513 21994 : if (t == NULL) {
1514 0 : DBG_WARNING("Invalid tar context\n");
1515 0 : return false;
1516 : }
1517 21994 : return t->to_process;
1518 : }
1519 :
1520 : /**
1521 : * skip_useless_char_in_path - skip leading slashes/dots
1522 : *
1523 : * Skip leading slashes, backslashes and dot-slashes.
1524 : */
1525 524 : static const char* skip_useless_char_in_path(const char *p)
1526 : {
1527 684 : while (p) {
1528 684 : if (*p == '/' || *p == '\\') {
1529 40 : p++;
1530 : }
1531 644 : else if (p[0] == '.' && (p[1] == '/' || p[1] == '\\')) {
1532 120 : p += 2;
1533 : }
1534 : else
1535 524 : return p;
1536 : }
1537 0 : return p;
1538 : }
1539 :
1540 : /**
1541 : * is_subpath - check if the path @sub is a subpath of @full.
1542 : * @sub: path to test
1543 : * @full: container path
1544 : * @_subpath_match: set true if @sub is a subpath of @full, otherwise false
1545 : *
1546 : * String comparison is case-insensitive.
1547 : */
1548 364 : static NTSTATUS is_subpath(const char *sub, const char *full,
1549 : bool *_subpath_match)
1550 : {
1551 364 : NTSTATUS status = NT_STATUS_OK;
1552 364 : int len = 0;
1553 : char *f, *s;
1554 364 : TALLOC_CTX *tmp_ctx = talloc_new(NULL);
1555 364 : if (tmp_ctx == NULL) {
1556 0 : status = NT_STATUS_NO_MEMORY;
1557 0 : goto out;
1558 : }
1559 :
1560 364 : f = strlower_talloc(tmp_ctx, full);
1561 364 : if (f == NULL) {
1562 0 : status = NT_STATUS_NO_MEMORY;
1563 0 : goto out_ctx_free;
1564 : }
1565 364 : string_replace(f, '\\', '/');
1566 364 : s = strlower_talloc(tmp_ctx, sub);
1567 364 : if (s == NULL) {
1568 0 : status = NT_STATUS_NO_MEMORY;
1569 0 : goto out_ctx_free;
1570 : }
1571 364 : string_replace(s, '\\', '/');
1572 :
1573 : /* find the point where sub and full diverge */
1574 7642 : while ((*f != '\0') && (*s != '\0') && (*f == *s)) {
1575 7278 : f++;
1576 7278 : s++;
1577 7278 : len++;
1578 : }
1579 :
1580 364 : if ((*f == '\0') && (*s == '\0')) {
1581 28 : *_subpath_match = true; /* sub and full match */
1582 28 : goto out_ctx_free;
1583 : }
1584 :
1585 336 : if ((*f == '\0') && (len > 0) && (*(f - 1) == '/')) {
1586 : /* sub diverges from full at path separator */
1587 0 : *_subpath_match = true;
1588 0 : goto out_ctx_free;
1589 : }
1590 :
1591 336 : if ((*s == '\0') && (strcmp(f, "/") == 0)) {
1592 : /* full diverges from sub with trailing slash only */
1593 0 : *_subpath_match = true;
1594 0 : goto out_ctx_free;
1595 : }
1596 :
1597 336 : if ((*s == '/') && (*f == '\0')) {
1598 : /* sub diverges from full with extra path component */
1599 60 : *_subpath_match = true;
1600 60 : goto out_ctx_free;
1601 : }
1602 276 : *_subpath_match = false;
1603 :
1604 364 : out_ctx_free:
1605 364 : talloc_free(tmp_ctx);
1606 364 : out:
1607 364 : return status;
1608 : }
1609 :
1610 :
1611 : /**
1612 : * make_remote_path - recursively make remote dirs
1613 : * @full_path: full hierarchy to create
1614 : *
1615 : * Create @full_path and each parent directories as needed.
1616 : */
1617 224 : static int make_remote_path(const char *full_path)
1618 : {
1619 : extern struct cli_state *cli;
1620 : char *path;
1621 : char *subpath;
1622 : char *state;
1623 : char *last_backslash;
1624 : char *p;
1625 : int len;
1626 : NTSTATUS status;
1627 224 : int err = 0;
1628 224 : TALLOC_CTX *ctx = talloc_new(NULL);
1629 224 : if (ctx == NULL) {
1630 0 : return 1;
1631 : }
1632 :
1633 224 : subpath = talloc_strdup(ctx, full_path);
1634 224 : if (subpath == NULL) {
1635 0 : err = 1;
1636 0 : goto out;
1637 : }
1638 224 : path = talloc_strdup(ctx, full_path);
1639 224 : if (path == NULL) {
1640 0 : err = 1;
1641 0 : goto out;
1642 : }
1643 224 : len = talloc_get_size(path) - 1;
1644 :
1645 224 : last_backslash = strrchr_m(path, '\\');
1646 224 : if (last_backslash == NULL) {
1647 0 : goto out;
1648 : }
1649 :
1650 224 : *last_backslash = 0;
1651 :
1652 224 : subpath[0] = 0;
1653 224 : p = strtok_r(path, "\\", &state);
1654 :
1655 508 : while (p != NULL) {
1656 284 : strlcat(subpath, p, len);
1657 284 : status = cli_chkpath(cli, subpath);
1658 284 : if (!NT_STATUS_IS_OK(status)) {
1659 64 : status = cli_mkdir(cli, subpath);
1660 64 : if (!NT_STATUS_IS_OK(status)) {
1661 0 : d_printf("Can't mkdir %s: %s\n", subpath, nt_errstr(status));
1662 0 : err = 1;
1663 0 : goto out;
1664 : }
1665 64 : DBG(3, ("mkdir %s\n", subpath));
1666 : }
1667 :
1668 284 : strlcat(subpath, "\\", len);
1669 284 : p = strtok_r(NULL, "/\\", &state);
1670 :
1671 : }
1672 :
1673 224 : out:
1674 224 : talloc_free(ctx);
1675 224 : return err;
1676 : }
1677 :
1678 : /**
1679 : * tar_reset_mem_context - reset talloc context associated with @t
1680 : *
1681 : * At the start of the program the context is NULL so a new one is
1682 : * allocated. On the following runs (interactive session only), simply
1683 : * free the children.
1684 : */
1685 184 : static TALLOC_CTX *tar_reset_mem_context(struct tar *t)
1686 : {
1687 184 : tar_free_mem_context(t);
1688 184 : t->talloc_ctx = talloc_new(NULL);
1689 184 : return t->talloc_ctx;
1690 : }
1691 :
1692 : /**
1693 : * tar_free_mem_context - free talloc context associated with @t
1694 : */
1695 368 : static void tar_free_mem_context(struct tar *t)
1696 : {
1697 368 : if (t->talloc_ctx) {
1698 184 : talloc_free(t->talloc_ctx);
1699 184 : t->talloc_ctx = NULL;
1700 184 : t->path_list_size = 0;
1701 184 : t->path_list = NULL;
1702 184 : t->tar_path = NULL;
1703 : }
1704 368 : }
1705 :
1706 : #define XSET(v) [v] = #v
1707 : #define XTABLE(v, t) DBG(2, ("DUMP:%-20.20s = %s\n", #v, t[v]))
1708 : #define XBOOL(v) DBG(2, ("DUMP:%-20.20s = %d\n", #v, v ? 1 : 0))
1709 : #define XSTR(v) DBG(2, ("DUMP:%-20.20s = %s\n", #v, v ? v : "NULL"))
1710 : #define XINT(v) DBG(2, ("DUMP:%-20.20s = %d\n", #v, v))
1711 : #define XUINT64(v) DBG(2, ("DUMP:%-20.20s = %" PRIu64 "\n", #v, v))
1712 :
1713 : /**
1714 : * tar_dump - dump tar structure on stdout
1715 : */
1716 184 : static void tar_dump(struct tar *t)
1717 : {
1718 : int i;
1719 184 : const char* op[] = {
1720 : XSET(TAR_NO_OPERATION),
1721 : XSET(TAR_CREATE),
1722 : XSET(TAR_EXTRACT),
1723 : };
1724 :
1725 184 : const char* sel[] = {
1726 : XSET(TAR_NO_SELECTION),
1727 : XSET(TAR_INCLUDE),
1728 : XSET(TAR_EXCLUDE),
1729 : };
1730 :
1731 184 : XBOOL(t->to_process);
1732 184 : XTABLE(t->mode.operation, op);
1733 184 : XTABLE(t->mode.selection, sel);
1734 184 : XINT(t->mode.blocksize);
1735 184 : XBOOL(t->mode.hidden);
1736 184 : XBOOL(t->mode.system);
1737 184 : XBOOL(t->mode.incremental);
1738 184 : XBOOL(t->mode.reset);
1739 184 : XBOOL(t->mode.dry);
1740 184 : XBOOL(t->mode.verbose);
1741 184 : XUINT64(t->total_size);
1742 184 : XSTR(t->tar_path);
1743 184 : XINT(t->path_list_size);
1744 :
1745 408 : for (i = 0; t->path_list && t->path_list[i]; i++) {
1746 224 : DBG(2, ("DUMP: t->path_list[%2d] = %s\n", i, t->path_list[i]));
1747 : }
1748 :
1749 184 : DBG(2, ("DUMP:t->path_list @ %p (%d elem)\n", t->path_list, i));
1750 184 : }
1751 : #undef XSET
1752 : #undef XTABLE
1753 : #undef XBOOL
1754 : #undef XSTR
1755 : #undef XINT
1756 :
1757 : /**
1758 : * max_token - return upper limit for the number of token in @str
1759 : *
1760 : * The result is not exact, the actual number of token might be less
1761 : * than what is returned.
1762 : */
1763 12 : static int max_token(const char *str)
1764 : {
1765 : const char *s;
1766 12 : int nb = 0;
1767 :
1768 12 : if (str == NULL) {
1769 0 : return 0;
1770 : }
1771 :
1772 12 : s = str;
1773 1488 : while (s[0] != '\0') {
1774 1476 : if (isspace((int)s[0])) {
1775 32 : nb++;
1776 : }
1777 1476 : s++;
1778 : }
1779 :
1780 12 : nb++;
1781 :
1782 12 : return nb;
1783 : }
1784 :
1785 : /**
1786 : * fix_unix_path - convert @path to a DOS path
1787 : * @path: path to convert
1788 : * @removeprefix: if true, remove leading ./ or /.
1789 : */
1790 448 : static char *fix_unix_path(char *path, bool do_remove_prefix)
1791 : {
1792 448 : char *from = path, *to = path;
1793 :
1794 448 : if (path == NULL || path[0] == '\0') {
1795 0 : return path;
1796 : }
1797 :
1798 : /* remove prefix:
1799 : * ./path => path
1800 : * /path => path
1801 : */
1802 448 : if (do_remove_prefix) {
1803 : /* /path */
1804 448 : if (path[0] == '/' || path[0] == '\\') {
1805 12 : from += 1;
1806 : }
1807 :
1808 : /* ./path */
1809 448 : if (path[1] != '\0' && path[0] == '.' && (path[1] == '/' || path[1] == '\\')) {
1810 180 : from += 2;
1811 : }
1812 : }
1813 :
1814 : /* replace / with \ */
1815 9824 : while (from[0] != '\0') {
1816 9376 : if (from[0] == '/') {
1817 444 : to[0] = '\\';
1818 : } else {
1819 8932 : to[0] = from[0];
1820 : }
1821 :
1822 9376 : from++;
1823 9376 : to++;
1824 : }
1825 448 : to[0] = '\0';
1826 :
1827 448 : return path;
1828 : }
1829 :
1830 : /**
1831 : * path_base_name - return @path basename
1832 : *
1833 : * If @path doesn't contain any directory separator return NULL.
1834 : */
1835 124 : static NTSTATUS path_base_name(TALLOC_CTX *ctx, const char *path, char **_base)
1836 : {
1837 124 : char *base = NULL;
1838 124 : int last = -1;
1839 : int i;
1840 :
1841 2250 : for (i = 0; path[i]; i++) {
1842 2126 : if (path[i] == '\\' || path[i] == '/') {
1843 44 : last = i;
1844 : }
1845 : }
1846 :
1847 124 : if (last >= 0) {
1848 28 : base = talloc_strdup(ctx, path);
1849 28 : if (base == NULL) {
1850 0 : return NT_STATUS_NO_MEMORY;
1851 : }
1852 :
1853 28 : base[last] = 0;
1854 : }
1855 :
1856 124 : *_base = base;
1857 124 : return NT_STATUS_OK;
1858 : }
1859 :
1860 : #else
1861 :
1862 : #define NOT_IMPLEMENTED DEBUG(0, ("tar mode not compiled. build used --without-libarchive\n"))
1863 :
1864 : int cmd_block(void)
1865 : {
1866 : NOT_IMPLEMENTED;
1867 : return 1;
1868 : }
1869 :
1870 : int cmd_tarmode(void)
1871 : {
1872 : NOT_IMPLEMENTED;
1873 : return 1;
1874 : }
1875 :
1876 : int cmd_tar(void)
1877 : {
1878 : NOT_IMPLEMENTED;
1879 : return 1;
1880 : }
1881 :
1882 : int tar_process(struct tar* tar)
1883 : {
1884 : NOT_IMPLEMENTED;
1885 : return 1;
1886 : }
1887 :
1888 : int tar_parse_args(struct tar *tar, const char *flag, const char **val, int valsize)
1889 : {
1890 : NOT_IMPLEMENTED;
1891 : return 1;
1892 : }
1893 :
1894 : bool tar_to_process(struct tar *tar)
1895 : {
1896 : return false;
1897 : }
1898 :
1899 : struct tar *tar_get_ctx()
1900 : {
1901 : return NULL;
1902 : }
1903 :
1904 : #endif
|