Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c055960b8 | ||
|
|
405e5b3b00 |
No files matched your search
+26
-13
@@ -8,9 +8,9 @@ This document maps rsync's full feature set to FastSync's current implementation
|
|||||||
|--------|-------|-------------|
|
|--------|-------|-------------|
|
||||||
| ✅ Implemented | 34 | Feature works end-to-end |
|
| ✅ Implemented | 34 | Feature works end-to-end |
|
||||||
| 🔀 Alt Arg | 3 | Functionality exists but under different flag/semantics |
|
| 🔀 Alt Arg | 3 | Functionality exists but under different flag/semantics |
|
||||||
| ⚠️ Partial | 1 | Flag parsed/stored but behavior incomplete |
|
| ⚠️ Partial | 3 | Flag parsed/stored but behavior incomplete |
|
||||||
| ❌ Not Implemented | 98 | Flag not recognized or no behavior |
|
| ❌ Not Implemented | 110 | Flag not recognized or no behavior |
|
||||||
| **Total** | **136** | |
|
| **Total** | **150** | |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -30,6 +30,7 @@ This document maps rsync's full feature set to FastSync's current implementation
|
|||||||
| `--exclude=PATTERN` | Exclude files matching pattern | ✅ Implemented | Glob matching in scanner |
|
| `--exclude=PATTERN` | Exclude files matching pattern | ✅ Implemented | Glob matching in scanner |
|
||||||
| `--include=PATTERN` | Include files matching pattern | ✅ Implemented | Glob matching in scanner |
|
| `--include=PATTERN` | Include files matching pattern | ✅ Implemented | Glob matching in scanner |
|
||||||
| `-C`, `--cvs-exclude` | Auto-ignore CVS files | ❌ Not Implemented | Removed because it had no effect |
|
| `-C`, `--cvs-exclude` | Auto-ignore CVS files | ❌ Not Implemented | Removed because it had no effect |
|
||||||
|
| `--no-OPTION` | Turn off an implied option | ❌ Not Implemented | Rsync supports this for options such as `--no-D`; FastSync does not |
|
||||||
|
|
||||||
## 2. Modifying Output
|
## 2. Modifying Output
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@ This document maps rsync's full feature set to FastSync's current implementation
|
|||||||
| `--out-format=FORMAT` | Custom output format | ❌ Not Implemented | Removed because it had no effect |
|
| `--out-format=FORMAT` | Custom output format | ❌ Not Implemented | Removed because it had no effect |
|
||||||
| `--log-file=FILE` | Log to file | ✅ Implemented | `log_file` config field |
|
| `--log-file=FILE` | Log to file | ✅ Implemented | `log_file` config field |
|
||||||
| `--log-file-format=FMT` | Log format | ❌ Not Implemented | |
|
| `--log-file-format=FMT` | Log format | ❌ Not Implemented | |
|
||||||
| `--8-bit-output` | Leave high-bit chars unescaped | ❌ Not Implemented | |
|
| `-8`, `--8-bit-output` | Leave high-bit chars unescaped | ❌ Not Implemented | |
|
||||||
| `--list-only` | List files instead of copying | ❌ Not Implemented | Removed because it had no effect |
|
| `--list-only` | List files instead of copying | ❌ Not Implemented | Removed because it had no effect |
|
||||||
|
|
||||||
## 3. File Selection
|
## 3. File Selection
|
||||||
@@ -52,7 +53,8 @@ This document maps rsync's full feature set to FastSync's current implementation
|
|||||||
|------|-------------------|-----------------|-------|
|
|------|-------------------|-----------------|-------|
|
||||||
| `--exclude-from=FILE` | Read exclude patterns from file | ✅ Implemented | Reads patterns from file |
|
| `--exclude-from=FILE` | Read exclude patterns from file | ✅ Implemented | Reads patterns from file |
|
||||||
| `--include-from=FILE` | Read include patterns from file | ✅ Implemented | Reads patterns from file |
|
| `--include-from=FILE` | Read include patterns from file | ✅ Implemented | Reads patterns from file |
|
||||||
| `--filter=RULE` | Add file-filtering rule | ❌ Not Implemented | Removed because it had no effect |
|
| `-f`, `--filter=RULE` | Add a file-filtering rule | ❌ Not Implemented | `-f` is FastSync's sendfile flag |
|
||||||
|
| `-F` | Add the default `.rsync-filter` rules | ❌ Not Implemented | |
|
||||||
| `--files-from=FILE` | Read source file list from file | ❌ Not Implemented | Removed because it had no effect |
|
| `--files-from=FILE` | Read source file list from file | ❌ Not Implemented | Removed because it had no effect |
|
||||||
| `-0`, `--from0` | Delimit *-from files with NULs | ❌ Not Implemented | |
|
| `-0`, `--from0` | Delimit *-from files with NULs | ❌ Not Implemented | |
|
||||||
| `--max-size=SIZE` | Skip files larger than SIZE | ✅ Implemented | `max_size` in scanner |
|
| `--max-size=SIZE` | Skip files larger than SIZE | ✅ Implemented | `max_size` in scanner |
|
||||||
@@ -63,6 +65,7 @@ This document maps rsync's full feature set to FastSync's current implementation
|
|||||||
| `--existing` | Skip creating new files on receiver | ❌ Not Implemented | |
|
| `--existing` | Skip creating new files on receiver | ❌ Not Implemented | |
|
||||||
| `--ignore-existing` | Skip updating existing files | ❌ Not Implemented | |
|
| `--ignore-existing` | Skip updating existing files | ❌ Not Implemented | |
|
||||||
| `--remove-source-files` | Sender removes synced files | ❌ Not Implemented | |
|
| `--remove-source-files` | Sender removes synced files | ❌ Not Implemented | |
|
||||||
|
| `-x`, `--one-file-system` | Do not cross filesystem boundaries | ❌ Not Implemented | |
|
||||||
|
|
||||||
## 4. Directory Options
|
## 4. Directory Options
|
||||||
|
|
||||||
@@ -72,6 +75,7 @@ This document maps rsync's full feature set to FastSync's current implementation
|
|||||||
| `-R`, `--relative` | Use relative path names | ❌ Not Implemented | Removed because it had no effect |
|
| `-R`, `--relative` | Use relative path names | ❌ Not Implemented | Removed because it had no effect |
|
||||||
| `--no-implied-dirs` | Don't send implied dirs with -R | ❌ Not Implemented | |
|
| `--no-implied-dirs` | Don't send implied dirs with -R | ❌ Not Implemented | |
|
||||||
| `-d`, `--dirs` | Transfer dirs without recursing | ❌ Not Implemented | |
|
| `-d`, `--dirs` | Transfer dirs without recursing | ❌ Not Implemented | |
|
||||||
|
| `--old-dirs`, `--old-d` | Transfer directories without recursing | ❌ Not Implemented | Compatibility aliases for `--dirs` |
|
||||||
| `--mkpath` | Create missing path components | ❌ Not Implemented | |
|
| `--mkpath` | Create missing path components | ❌ Not Implemented | |
|
||||||
|
|
||||||
## 5. Transfer Modifications
|
## 5. Transfer Modifications
|
||||||
@@ -83,7 +87,7 @@ This document maps rsync's full feature set to FastSync's current implementation
|
|||||||
| `--append` | Append data to shorter files | ❌ Not Implemented | Removed because it had no effect |
|
| `--append` | Append data to shorter files | ❌ Not Implemented | Removed because it had no effect |
|
||||||
| `--append-verify` | Append with old-data checksum | ❌ Not Implemented | Removed because it had no effect |
|
| `--append-verify` | Append with old-data checksum | ❌ Not Implemented | Removed because it had no effect |
|
||||||
| `-W`, `--whole-file` | Copy whole file (no delta) | ❌ Not Implemented | |
|
| `-W`, `--whole-file` | Copy whole file (no delta) | ❌ Not Implemented | |
|
||||||
| `--block-size=SIZE` | Force checksum block-size | ⚠️ Partial | Parsed as `--delta-block`; controls delta transfer block size |
|
| `-B`, `--block-size=SIZE` | Force checksum block-size | ⚠️ Partial | Parsed as `--delta-block`; controls delta transfer block size |
|
||||||
|
|
||||||
## 6. Destination Handling
|
## 6. Destination Handling
|
||||||
|
|
||||||
@@ -94,12 +98,14 @@ This document maps rsync's full feature set to FastSync's current implementation
|
|||||||
| `--backup-dir=DIR` | Backup directory hierarchy | ✅ Implemented | `backup_dir` config field |
|
| `--backup-dir=DIR` | Backup directory hierarchy | ✅ Implemented | `backup_dir` config field |
|
||||||
| `--suffix=SUFFIX` | Backup suffix (default ~) | ✅ Implemented | `suffix` config field |
|
| `--suffix=SUFFIX` | Backup suffix (default ~) | ✅ Implemented | `suffix` config field |
|
||||||
| `--delay-updates` | Put updated files in place at end | ❌ Not Implemented | |
|
| `--delay-updates` | Put updated files in place at end | ❌ Not Implemented | |
|
||||||
|
| `-T`, `--temp-dir=DIR` | Create temporary files in DIR | ❌ Not Implemented | `-T` is FastSync's timeout alias |
|
||||||
|
|
||||||
## 7. Deletion
|
## 7. Deletion
|
||||||
|
|
||||||
| Flag | Rsync Description | FastSync Status | Notes |
|
| Flag | Rsync Description | FastSync Status | Notes |
|
||||||
|------|-------------------|-----------------|-------|
|
|------|-------------------|-----------------|-------|
|
||||||
| `--delete` | Delete extraneous files from dest | ✅ Implemented | `use_delete` config field |
|
| `--delete` | Delete extraneous files from dest | ✅ Implemented | `use_delete` config field |
|
||||||
|
| `--del` | Delete during transfer | ❌ Not Implemented | Alias for `--delete-during` |
|
||||||
| `--delete-before` | Delete before transfer | ❌ Not Implemented | Removed because it had no effect |
|
| `--delete-before` | Delete before transfer | ❌ Not Implemented | Removed because it had no effect |
|
||||||
| `--delete-during` | Delete during transfer | ❌ Not Implemented | |
|
| `--delete-during` | Delete during transfer | ❌ Not Implemented | |
|
||||||
| `--delete-delay` | Find deletions during, delete after | ❌ Not Implemented | |
|
| `--delete-delay` | Find deletions during, delete after | ❌ Not Implemented | |
|
||||||
@@ -108,7 +114,7 @@ This document maps rsync's full feature set to FastSync's current implementation
|
|||||||
| `--max-delete=NUM` | Max files to delete | ❌ Not Implemented | Removed because it had no effect |
|
| `--max-delete=NUM` | Max files to delete | ❌ Not Implemented | Removed because it had no effect |
|
||||||
| `--ignore-errors` | Delete even with I/O errors | ❌ Not Implemented | |
|
| `--ignore-errors` | Delete even with I/O errors | ❌ Not Implemented | |
|
||||||
| `--force` | Force deletion of non-empty dirs | ❌ Not Implemented | |
|
| `--force` | Force deletion of non-empty dirs | ❌ Not Implemented | |
|
||||||
| `--prune-empty-dirs` | Prune empty dir chains | ❌ Not Implemented | Removed because it had no effect |
|
| `-m`, `--prune-empty-dirs` | Prune empty dir chains | ❌ Not Implemented | `-m` enables FastSync multithreading instead |
|
||||||
|
|
||||||
## 8. Metadata Preservation
|
## 8. Metadata Preservation
|
||||||
|
|
||||||
@@ -120,7 +126,7 @@ This document maps rsync's full feature set to FastSync's current implementation
|
|||||||
| `-g`, `--group` | Preserve group | ✅ Implemented | Part of -M |
|
| `-g`, `--group` | Preserve group | ✅ Implemented | Part of -M |
|
||||||
| `-t`, `--times` | Preserve modification times | ✅ Implemented | Part of -M |
|
| `-t`, `--times` | Preserve modification times | ✅ Implemented | Part of -M |
|
||||||
| `-E`, `--executability` | Preserve executability | ❌ Not Implemented | |
|
| `-E`, `--executability` | Preserve executability | ❌ Not Implemented | |
|
||||||
| `--chmod=CHMOD` | Affect file permissions | ✅ Implemented | Supports numeric and symbolic `ugo` `rwx` changes; retains receiver safety masking |
|
| `--chmod=CHMOD` | Affect file permissions | ❌ Not Implemented | |
|
||||||
| `-A`, `--acls` | Preserve ACLs | ❌ Not Implemented | Removed because it had no effect |
|
| `-A`, `--acls` | Preserve ACLs | ❌ Not Implemented | Removed because it had no effect |
|
||||||
| `-X`, `--xattrs` | Preserve extended attributes | ❌ Not Implemented | Removed because it had no effect |
|
| `-X`, `--xattrs` | Preserve extended attributes | ❌ Not Implemented | Removed because it had no effect |
|
||||||
| `-H`, `--hard-links` | Preserve hard links | ❌ Not Implemented | Removed because it had no effect |
|
| `-H`, `--hard-links` | Preserve hard links | ❌ Not Implemented | Removed because it had no effect |
|
||||||
@@ -135,6 +141,12 @@ This document maps rsync's full feature set to FastSync's current implementation
|
|||||||
| `-J`, `--omit-link-times` | Omit symlinks from --times | ❌ Not Implemented | |
|
| `-J`, `--omit-link-times` | Omit symlinks from --times | ❌ Not Implemented | |
|
||||||
| `--super` | Receiver attempts super-user activities | ❌ Not Implemented | |
|
| `--super` | Receiver attempts super-user activities | ❌ Not Implemented | |
|
||||||
| `--fake-super` | Store/recover privileged attrs via xattrs | ❌ Not Implemented | |
|
| `--fake-super` | Store/recover privileged attrs via xattrs | ❌ Not Implemented | |
|
||||||
|
| `--open-noatime` | Avoid changing access time when opening files | ❌ Not Implemented | |
|
||||||
|
| `--numeric-ids` | Do not map uid/gid by name | ❌ Not Implemented | |
|
||||||
|
| `--usermap=STRING` | Map usernames | ❌ Not Implemented | |
|
||||||
|
| `--groupmap=STRING` | Map group names | ❌ Not Implemented | |
|
||||||
|
| `--chown=USER:GROUP` | Map owner and group | ❌ Not Implemented | |
|
||||||
|
| `--copy-as=USER[:GROUP]` | Perform the copy as another user/group | ❌ Not Implemented | |
|
||||||
|
|
||||||
## 9. Symlink Handling
|
## 9. Symlink Handling
|
||||||
|
|
||||||
@@ -160,19 +172,19 @@ This document maps rsync's full feature set to FastSync's current implementation
|
|||||||
| Flag | Rsync Description | FastSync Status | Notes |
|
| Flag | Rsync Description | FastSync Status | Notes |
|
||||||
|------|-------------------|-----------------|-------|
|
|------|-------------------|-----------------|-------|
|
||||||
| `--checksum` | Skip based on checksum | ✅ Implemented | With `--incremental`, compares xxHash64 content checksums; `-c` remains compression |
|
| `--checksum` | Skip based on checksum | ✅ Implemented | With `--incremental`, compares xxHash64 content checksums; `-c` remains compression |
|
||||||
| `--checksum-choice=STR` | Choose checksum algorithm | ❌ Not Implemented | xxHash used internally |
|
| `--checksum-choice=STR` (`--cc`) | Choose checksum algorithm | ❌ Not Implemented | xxHash used internally |
|
||||||
| `--compare-dest=DIR` | Compare dest files relative to DIR | ❌ Not Implemented | Removed because it had no effect |
|
| `--compare-dest=DIR` | Compare dest files relative to DIR | ❌ Not Implemented | Removed because it had no effect |
|
||||||
| `--copy-dest=DIR` | Include copies of unchanged files | ❌ Not Implemented | Removed because it had no effect |
|
| `--copy-dest=DIR` | Include copies of unchanged files | ❌ Not Implemented | Removed because it had no effect |
|
||||||
| `--link-dest=DIR` | Hardlink to files when unchanged | ❌ Not Implemented | Removed because it had no effect |
|
| `--link-dest=DIR` | Hardlink to files when unchanged | ❌ Not Implemented | Removed because it had no effect |
|
||||||
| `--fuzzy`, `--no-fuzzy` | Find similar file for basis | ❌ Not Implemented | |
|
| `-y`, `--fuzzy`, `--no-fuzzy` | Find similar file for basis | ❌ Not Implemented | `--no-fuzzy` has no short alias |
|
||||||
|
|
||||||
## 12. Compression
|
## 12. Compression
|
||||||
|
|
||||||
| Flag | Rsync Description | FastSync Status | Notes |
|
| Flag | Rsync Description | FastSync Status | Notes |
|
||||||
|------|-------------------|-----------------|-------|
|
|------|-------------------|-----------------|-------|
|
||||||
| `-z`, `--compress` | Compress file data | 🔀 Alt Arg | Always uses zstd (rsync supports multiple algorithms) |
|
| `-z`, `--compress` | Compress file data | 🔀 Alt Arg | Always uses zstd (rsync supports multiple algorithms) |
|
||||||
| `--compress-choice=STR` | Choose compression algorithm | ❌ Not Implemented | Removed because it had no effect; FastSync always uses zstd |
|
| `--compress-choice=STR` (`--zc`) | Choose compression algorithm | ❌ Not Implemented | Removed because it had no effect; FastSync always uses zstd |
|
||||||
| `--compress-level=NUM` | Set compression level | ✅ Implemented | 1-22, default 5 |
|
| `--compress-level=NUM` (`--zl`) | Set compression level | ✅ Implemented | 1-22, default 5 |
|
||||||
| `--compress-threads=NUM` | Set compression threads | ❌ Not Implemented | |
|
| `--compress-threads=NUM` | Set compression threads | ❌ Not Implemented | |
|
||||||
| `--skip-compress=LIST` | Skip compress for suffixes | ❌ Not Implemented | Internal skip for hardcoded types; not user-configurable |
|
| `--skip-compress=LIST` | Skip compress for suffixes | ❌ Not Implemented | Internal skip for hardcoded types; not user-configurable |
|
||||||
|
|
||||||
@@ -189,6 +201,7 @@ This document maps rsync's full feature set to FastSync's current implementation
|
|||||||
| `--address=ADDRESS` | Bind address for outgoing socket | ❌ Not Implemented | Removed because it had no effect |
|
| `--address=ADDRESS` | Bind address for outgoing socket | ❌ Not Implemented | Removed because it had no effect |
|
||||||
| `-4`, `--ipv4` | Prefer IPv4 | ❌ Not Implemented | Removed because it had no effect |
|
| `-4`, `--ipv4` | Prefer IPv4 | ❌ Not Implemented | Removed because it had no effect |
|
||||||
| `-6`, `--ipv6` | Prefer IPv6 | ❌ Not Implemented | Removed because it had no effect |
|
| `-6`, `--ipv6` | Prefer IPv6 | ❌ Not Implemented | Removed because it had no effect |
|
||||||
|
| `--remote-option=OPT`, `-M` | Send an option only to the remote side | ❌ Not Implemented | `-M` is FastSync's metadata-preservation flag |
|
||||||
|
|
||||||
## 14. Daemon Mode
|
## 14. Daemon Mode
|
||||||
|
|
||||||
@@ -214,6 +227,7 @@ This document maps rsync's full feature set to FastSync's current implementation
|
|||||||
| `--old-args` | Disable modern arg protection | ❌ Not Implemented | |
|
| `--old-args` | Disable modern arg protection | ❌ Not Implemented | |
|
||||||
| `--ignore-missing-args` | Ignore missing source args | ❌ Not Implemented | |
|
| `--ignore-missing-args` | Ignore missing source args | ❌ Not Implemented | |
|
||||||
| `--delete-missing-args` | Delete missing source args | ❌ Not Implemented | |
|
| `--delete-missing-args` | Delete missing source args | ❌ Not Implemented | |
|
||||||
|
| `--max-alloc=SIZE` | Limit a single memory allocation | ❌ Not Implemented | |
|
||||||
|
|
||||||
## 16. Batch Operations
|
## 16. Batch Operations
|
||||||
|
|
||||||
@@ -234,7 +248,6 @@ This document maps rsync's full feature set to FastSync's current implementation
|
|||||||
| `--iconv=CONVERT_SPEC` | Charset conversion | ❌ Not Implemented | |
|
| `--iconv=CONVERT_SPEC` | Charset conversion | ❌ Not Implemented | |
|
||||||
| `--checksum-seed=NUM` | Set checksum seed | ❌ Not Implemented | |
|
| `--checksum-seed=NUM` | Set checksum seed | ❌ Not Implemented | |
|
||||||
| `-s`, `--secluded-args` | Use protocol to send args | ❌ Not Implemented | |
|
| `-s`, `--secluded-args` | Use protocol to send args | ❌ Not Implemented | |
|
||||||
| `--no-OPTION` | Turn off implied option | ❌ Not Implemented | |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "client_send.h"
|
#include "client_send.h"
|
||||||
#include "client_validation.h"
|
#include "client_validation.h"
|
||||||
#include "chmod.h"
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "delta.h"
|
#include "delta.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
@@ -154,7 +153,6 @@ static const OptionEntry OPTION_TABLE[] = {
|
|||||||
{"--sparse", "-S", OPT_FLAG, offsetof(Config, preserve_sparse)},
|
{"--sparse", "-S", OPT_FLAG, offsetof(Config, preserve_sparse)},
|
||||||
{"--inplace", NULL, OPT_FLAG, offsetof(Config, inplace)},
|
{"--inplace", NULL, OPT_FLAG, offsetof(Config, inplace)},
|
||||||
{"--checksum", NULL, OPT_FLAG, offsetof(Config, checksum)},
|
{"--checksum", NULL, OPT_FLAG, offsetof(Config, checksum)},
|
||||||
{"--chmod", NULL, OPT_STRING, offsetof(Config, chmod_spec)},
|
|
||||||
|
|
||||||
{"--source-dir", NULL, OPT_STRING, offsetof(Config, send_directory)},
|
{"--source-dir", NULL, OPT_STRING, offsetof(Config, send_directory)},
|
||||||
{"--dest-dir", NULL, OPT_STRING, offsetof(Config, receive_root_directory)},
|
{"--dest-dir", NULL, OPT_STRING, offsetof(Config, receive_root_directory)},
|
||||||
@@ -222,32 +220,12 @@ int parse_args(Config* config, int argc, char* argv[], int* positional_args,
|
|||||||
}
|
}
|
||||||
if (apply_table_option(config, entry, argv[++i]) != 0)
|
if (apply_table_option(config, entry, argv[++i]) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
if (entry->offset == offsetof(Config, chmod_spec)) {
|
|
||||||
mode_t ignored;
|
|
||||||
if (!chmod_apply(0, config->chmod_spec, &ignored)) {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "--chmod has invalid permission changes");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
config->use_metadata = true;
|
|
||||||
}
|
|
||||||
} else if (apply_table_option(config, entry, NULL) != 0) {
|
} else if (apply_table_option(config, entry, NULL) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strncmp(argv[i], "--chmod=", 8) == 0) {
|
|
||||||
if (set_string_option(&config->chmod_spec, argv[i] + 8, "--chmod") != 0)
|
|
||||||
return -1;
|
|
||||||
mode_t ignored;
|
|
||||||
if (!chmod_apply(0, config->chmod_spec, &ignored)) {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "--chmod has invalid permission changes");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
config->use_metadata = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opt_is(argv[i], "--help", NULL)) {
|
if (opt_is(argv[i], "--help", NULL)) {
|
||||||
print_usage();
|
print_usage();
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ void print_usage(void) {
|
|||||||
printf(" -f Enable sendfile (TCP only, not with -c or -s)\n");
|
printf(" -f Enable sendfile (TCP only, not with -c or -s)\n");
|
||||||
printf(" -v, --verbose Enable debug logging\n");
|
printf(" -v, --verbose Enable debug logging\n");
|
||||||
printf(" -M, --preserve Preserve file metadata\n");
|
printf(" -M, --preserve Preserve file metadata\n");
|
||||||
printf(" --chmod <changes> Modify transferred permissions (rsync syntax)\n");
|
|
||||||
printf(" --chunk-size <n> Chunk size in bytes (default: %d)\n", DEFAULT_CHUNK_SIZE);
|
printf(" --chunk-size <n> Chunk size in bytes (default: %d)\n", DEFAULT_CHUNK_SIZE);
|
||||||
printf(" --source-dir <path> Source directory\n");
|
printf(" --source-dir <path> Source directory\n");
|
||||||
printf(" --dest-dir <path> Destination directory\n");
|
printf(" --dest-dir <path> Destination directory\n");
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
#include "chmod.h"
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
static bool parse_clause(mode_t* mode, const char* begin, const char* end) {
|
|
||||||
const char* p = begin;
|
|
||||||
unsigned who = 0;
|
|
||||||
while (p < end && strchr("ugoa", *p)) {
|
|
||||||
if (*p == 'a')
|
|
||||||
who = 7;
|
|
||||||
else
|
|
||||||
who |= *p == 'u' ? 1U : (*p == 'g' ? 2U : 4U);
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
if (who == 0)
|
|
||||||
who = 7;
|
|
||||||
if (p == end || (*p != '+' && *p != '-' && *p != '='))
|
|
||||||
return false;
|
|
||||||
char operation = *p++;
|
|
||||||
mode_t bits = 0;
|
|
||||||
while (p < end) {
|
|
||||||
mode_t bit;
|
|
||||||
switch (*p++) {
|
|
||||||
case 'r':
|
|
||||||
bit = 4;
|
|
||||||
break;
|
|
||||||
case 'w':
|
|
||||||
bit = 2;
|
|
||||||
break;
|
|
||||||
case 'x':
|
|
||||||
bit = 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bits |= bit;
|
|
||||||
}
|
|
||||||
for (unsigned class_index = 0; class_index < 3; class_index++) {
|
|
||||||
unsigned class_bit = 1U << class_index;
|
|
||||||
if (!(who & class_bit))
|
|
||||||
continue;
|
|
||||||
mode_t shift = (mode_t)((2U - class_index) * 3U);
|
|
||||||
mode_t mask = (mode_t)(7U << shift);
|
|
||||||
mode_t class_bits = (mode_t)(bits << shift);
|
|
||||||
if (operation == '+')
|
|
||||||
*mode |= class_bits;
|
|
||||||
else if (operation == '-')
|
|
||||||
*mode &= ~class_bits;
|
|
||||||
else
|
|
||||||
*mode = (*mode & ~mask) | class_bits;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool chmod_apply(mode_t mode, const char* spec, mode_t* result) {
|
|
||||||
if (!spec || !*spec || !result)
|
|
||||||
return false;
|
|
||||||
bool numeric = true;
|
|
||||||
size_t length = strlen(spec);
|
|
||||||
if (length > 4)
|
|
||||||
numeric = false;
|
|
||||||
for (size_t i = 0; i < length && numeric; i++)
|
|
||||||
numeric = spec[i] >= '0' && spec[i] <= '7';
|
|
||||||
if (numeric) {
|
|
||||||
if (length == 0 || length > 4)
|
|
||||||
return false;
|
|
||||||
mode_t parsed = 0;
|
|
||||||
for (size_t i = 0; i < length; i++)
|
|
||||||
parsed = (mode_t)((parsed << 3) | (spec[i] - '0'));
|
|
||||||
*result = parsed;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
mode_t changed = mode;
|
|
||||||
const char* begin = spec;
|
|
||||||
while (*begin) {
|
|
||||||
const char* end = strchr(begin, ',');
|
|
||||||
if (!end)
|
|
||||||
end = begin + strlen(begin);
|
|
||||||
if (!parse_clause(&changed, begin, end))
|
|
||||||
return false;
|
|
||||||
if (*end == '\0')
|
|
||||||
break;
|
|
||||||
begin = end + 1;
|
|
||||||
if (!*begin)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*result = changed;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#ifndef CHMOD_H
|
|
||||||
#define CHMOD_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
/* Apply the supported rsync --chmod syntax to a permission mode. */
|
|
||||||
bool chmod_apply(mode_t mode, const char* spec, mode_t* result);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
+3
-12
@@ -1,5 +1,4 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "chmod.h"
|
|
||||||
#include "delta.h"
|
#include "delta.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "protocol.h"
|
#include "protocol.h"
|
||||||
@@ -99,7 +98,6 @@ static void config_set_defaults(Config* config) {
|
|||||||
config->server_mode = false;
|
config->server_mode = false;
|
||||||
config->checksum = false;
|
config->checksum = false;
|
||||||
config->compress_choice = NULL;
|
config->compress_choice = NULL;
|
||||||
config->chmod_spec = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool valid_wire_bool(int value) {
|
static bool valid_wire_bool(int value) {
|
||||||
@@ -136,9 +134,7 @@ static bool validate_received_config(const Config* config) {
|
|||||||
config->chunk_size > 0 && config->chunk_size <= MAX_CHUNK_SIZE &&
|
config->chunk_size > 0 && config->chunk_size <= MAX_CHUNK_SIZE &&
|
||||||
config->delta_block_size >= DELTA_BLOCK_SIZE_MIN &&
|
config->delta_block_size >= DELTA_BLOCK_SIZE_MIN &&
|
||||||
config->delta_block_size <= DELTA_BLOCK_SIZE_MAX &&
|
config->delta_block_size <= DELTA_BLOCK_SIZE_MAX &&
|
||||||
config->delta_max_file_size <= DELTA_MAX_FILE_SIZE && config->max_delete >= 0 &&
|
config->delta_max_file_size <= DELTA_MAX_FILE_SIZE && config->max_delete >= 0;
|
||||||
(!config->chmod_spec || !*config->chmod_spec ||
|
|
||||||
chmod_apply(0, config->chmod_spec, &(mode_t){0}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Config* config_create(void) {
|
Config* config_create(void) {
|
||||||
@@ -212,7 +208,6 @@ void config_delete(Config* config) {
|
|||||||
free(config->bind_address);
|
free(config->bind_address);
|
||||||
free(config->daemon_config);
|
free(config->daemon_config);
|
||||||
free(config->compress_choice);
|
free(config->compress_choice);
|
||||||
free(config->chmod_spec);
|
|
||||||
if (config->filters) {
|
if (config->filters) {
|
||||||
array_list_delete(config->filters);
|
array_list_delete(config->filters);
|
||||||
}
|
}
|
||||||
@@ -258,8 +253,7 @@ static bool send_resume_options(int fd, const Config* c) {
|
|||||||
return send_str(fd, c->temp_dir ? c->temp_dir : "") && send_int(fd, c->partial) &&
|
return send_str(fd, c->temp_dir ? c->temp_dir : "") && send_int(fd, c->partial) &&
|
||||||
send_str(fd, c->partial_dir ? c->partial_dir : "") &&
|
send_str(fd, c->partial_dir ? c->partial_dir : "") &&
|
||||||
send_str(fd, c->suffix ? c->suffix : "") && send_int(fd, c->delete_before) &&
|
send_str(fd, c->suffix ? c->suffix : "") && send_int(fd, c->delete_before) &&
|
||||||
send_int(fd, c->checksum) && send_str(fd, c->compress_choice ? c->compress_choice : "") &&
|
send_int(fd, c->checksum) && send_str(fd, c->compress_choice ? c->compress_choice : "");
|
||||||
send_str(fd, c->chmod_spec ? c->chmod_spec : "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool receive_core_fields(int fd, Config* c) {
|
static bool receive_core_fields(int fd, Config* c) {
|
||||||
@@ -336,10 +330,7 @@ static bool receive_resume_options(int fd, Config* c) {
|
|||||||
if (!receive_wire_bool(fd, &c->checksum))
|
if (!receive_wire_bool(fd, &c->checksum))
|
||||||
return false;
|
return false;
|
||||||
c->compress_choice = receive_str(fd);
|
c->compress_choice = receive_str(fd);
|
||||||
if (!c->compress_choice)
|
return c->compress_choice != NULL;
|
||||||
return false;
|
|
||||||
c->chmod_spec = receive_str(fd);
|
|
||||||
return c->chmod_spec != NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool config_send(int file_descriptor, const Config* config) {
|
bool config_send(int file_descriptor, const Config* config) {
|
||||||
|
|||||||
+1
-2
@@ -126,10 +126,9 @@ typedef struct Config {
|
|||||||
|
|
||||||
// PR #184: Compression algorithm negotiation
|
// PR #184: Compression algorithm negotiation
|
||||||
char* compress_choice;
|
char* compress_choice;
|
||||||
char* chmod_spec;
|
|
||||||
} Config;
|
} Config;
|
||||||
|
|
||||||
#define PROTOCOL_VERSION "2.3.0"
|
#define PROTOCOL_VERSION "2.2.0"
|
||||||
#define DEFAULT_CHUNK_SIZE (10 * 1024 * 1024)
|
#define DEFAULT_CHUNK_SIZE (10 * 1024 * 1024)
|
||||||
|
|
||||||
Config* config_create(void);
|
Config* config_create(void);
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "array_list.h"
|
#include "array_list.h"
|
||||||
#include "chmod.h"
|
|
||||||
#include "compression.h"
|
#include "compression.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
@@ -104,16 +103,8 @@ bool file_save_to_disk(const char* root_directory, const File* file, const Confi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FileMetadata adjusted_metadata;
|
bool ok = file_to_disk_secure(disk_path, file->data->data, file->data->size, inplace, sparse,
|
||||||
const FileMetadata* metadata = file->metadata;
|
file->metadata);
|
||||||
if (metadata && config && config->chmod_spec && *config->chmod_spec) {
|
|
||||||
adjusted_metadata = *metadata;
|
|
||||||
if (!chmod_apply(adjusted_metadata.mode, config->chmod_spec, &adjusted_metadata.mode))
|
|
||||||
goto fail;
|
|
||||||
metadata = &adjusted_metadata;
|
|
||||||
}
|
|
||||||
bool ok =
|
|
||||||
file_to_disk_secure(disk_path, file->data->data, file->data->size, inplace, sparse, metadata);
|
|
||||||
free(parent_copy);
|
free(parent_copy);
|
||||||
free(backup_path);
|
free(backup_path);
|
||||||
free(confined_backup);
|
free(confined_backup);
|
||||||
|
|||||||
@@ -52,22 +52,6 @@ class TestArchiveMode:
|
|||||||
assert not mismatches, f"Mismatch: {mismatches}"
|
assert not mismatches, f"Mismatch: {mismatches}"
|
||||||
|
|
||||||
|
|
||||||
class TestChmod:
|
|
||||||
def test_chmod_applies_to_transferred_files(self, shared_server):
|
|
||||||
clean_dir(DEST_DIR)
|
|
||||||
source_file = os.path.join(SOURCE_DIR, "small.txt")
|
|
||||||
os.chmod(source_file, 0o777)
|
|
||||||
result, dur = run_client(
|
|
||||||
SOURCE_DIR, DEST_DIR,
|
|
||||||
flags=["--chmod=u=rw,go=r"],
|
|
||||||
port=shared_server.port,
|
|
||||||
)
|
|
||||||
if result.returncode != 0:
|
|
||||||
pytest.fail(f"Exit {result.returncode}: {(result.stderr or result.stdout)[:200]}")
|
|
||||||
received = get_dest_received_dir(DEST_DIR, SOURCE_DIR)
|
|
||||||
assert (os.stat(os.path.join(received, "small.txt")).st_mode & 0o777) == 0o644
|
|
||||||
|
|
||||||
|
|
||||||
class TestExclude:
|
class TestExclude:
|
||||||
def test_exclude_single(self, shared_server):
|
def test_exclude_single(self, shared_server):
|
||||||
clean_dir(DEST_DIR)
|
clean_dir(DEST_DIR)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "test_client_cli.h"
|
#include "test_client_cli.h"
|
||||||
#include "client_validation.h"
|
#include "client_validation.h"
|
||||||
#include "chmod.h"
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "test_utils.h"
|
#include "test_utils.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
@@ -182,40 +181,6 @@ static void test_parse_args_valid_port() {
|
|||||||
config_delete(cfg);
|
config_delete(cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_parse_args_chmod() {
|
|
||||||
Config* cfg = config_create();
|
|
||||||
char* argv[] = {"fastsync", "--chmod=u=rw,go=r", "/src", "/dst"};
|
|
||||||
int positional_args[2];
|
|
||||||
int positional_count = 0;
|
|
||||||
EXPECT_EQ_INT(parse_args(cfg, 4, argv, positional_args, &positional_count), 0);
|
|
||||||
EXPECT_EQ_STR(cfg->chmod_spec, "u=rw,go=r");
|
|
||||||
EXPECT_TRUE(cfg->use_metadata);
|
|
||||||
mode_t result;
|
|
||||||
EXPECT_TRUE(chmod_apply(0777, cfg->chmod_spec, &result));
|
|
||||||
EXPECT_EQ_INT(result, 0644);
|
|
||||||
config_delete(cfg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_parse_args_numeric_chmod() {
|
|
||||||
Config* cfg = config_create();
|
|
||||||
char* argv[] = {"fastsync", "--chmod", "7777", "/src", "/dst"};
|
|
||||||
int positional_args[2];
|
|
||||||
int positional_count = 0;
|
|
||||||
EXPECT_EQ_INT(parse_args(cfg, 5, argv, positional_args, &positional_count), 0);
|
|
||||||
EXPECT_EQ_STR(cfg->chmod_spec, "7777");
|
|
||||||
EXPECT_TRUE(cfg->use_metadata);
|
|
||||||
config_delete(cfg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_parse_args_rejects_invalid_chmod() {
|
|
||||||
Config* cfg = config_create();
|
|
||||||
char* argv[] = {"fastsync", "--chmod=a+X", "/src", "/dst"};
|
|
||||||
int positional_args[2];
|
|
||||||
int positional_count = 0;
|
|
||||||
EXPECT_EQ_INT(parse_args(cfg, 4, argv, positional_args, &positional_count), -1);
|
|
||||||
config_delete(cfg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Test parse_args rejects port > 65535 */
|
/* Test parse_args rejects port > 65535 */
|
||||||
static void test_parse_args_invalid_port() {
|
static void test_parse_args_invalid_port() {
|
||||||
Config* cfg = config_create();
|
Config* cfg = config_create();
|
||||||
@@ -387,9 +352,6 @@ void test_client_cli() {
|
|||||||
test_parse_args_help();
|
test_parse_args_help();
|
||||||
test_parse_args_version();
|
test_parse_args_version();
|
||||||
test_parse_args_valid_port();
|
test_parse_args_valid_port();
|
||||||
test_parse_args_chmod();
|
|
||||||
test_parse_args_numeric_chmod();
|
|
||||||
test_parse_args_rejects_invalid_chmod();
|
|
||||||
test_parse_args_invalid_port();
|
test_parse_args_invalid_port();
|
||||||
test_parse_args_non_numeric_port();
|
test_parse_args_non_numeric_port();
|
||||||
test_parse_args_invalid_server_port();
|
test_parse_args_invalid_server_port();
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#include "test_metadata.h"
|
#include "test_metadata.h"
|
||||||
#include "chmod.h"
|
|
||||||
#include "metadata.h"
|
#include "metadata.h"
|
||||||
#include "protocol.h"
|
#include "protocol.h"
|
||||||
#include "test_utils.h"
|
#include "test_utils.h"
|
||||||
@@ -145,27 +144,6 @@ static void test_file_restore_metadata() {
|
|||||||
unlink(path);
|
unlink(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_chmod_changes() {
|
|
||||||
mode_t result;
|
|
||||||
EXPECT_TRUE(chmod_apply(0777, "u=rw,go=r", &result));
|
|
||||||
EXPECT_EQ_INT(result, 0644);
|
|
||||||
EXPECT_TRUE(chmod_apply(0644, "a+x", &result));
|
|
||||||
EXPECT_EQ_INT(result, 0755);
|
|
||||||
result = 0777;
|
|
||||||
EXPECT_TRUE(chmod_apply(0777, "0000", &result));
|
|
||||||
EXPECT_EQ_INT(result, 0000);
|
|
||||||
result = 0777;
|
|
||||||
EXPECT_TRUE(chmod_apply(0777, "7777", &result));
|
|
||||||
EXPECT_EQ_INT(result, 07777);
|
|
||||||
result = 0777;
|
|
||||||
EXPECT_TRUE(chmod_apply(0777, "755", &result));
|
|
||||||
EXPECT_EQ_INT(result, 0755);
|
|
||||||
EXPECT_FALSE(chmod_apply(0777, "888", &result));
|
|
||||||
EXPECT_FALSE(chmod_apply(0777, "10000", &result));
|
|
||||||
EXPECT_FALSE(chmod_apply(0777, "a+X", &result));
|
|
||||||
EXPECT_FALSE(chmod_apply(0777, "a+r,", &result));
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_metadata() {
|
void test_metadata() {
|
||||||
test_metadata_to_from_buf_roundtrip();
|
test_metadata_to_from_buf_roundtrip();
|
||||||
test_metadata_to_buf_null();
|
test_metadata_to_buf_null();
|
||||||
@@ -174,5 +152,4 @@ void test_metadata() {
|
|||||||
test_metadata_send_null();
|
test_metadata_send_null();
|
||||||
test_metadata_rejects_invalid_values();
|
test_metadata_rejects_invalid_values();
|
||||||
test_file_restore_metadata();
|
test_file_restore_metadata();
|
||||||
test_chmod_changes();
|
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user