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