diff options
Diffstat (limited to '')
-rw-r--r-- | doc/man_knotc.rst | 5 | ||||
-rw-r--r-- | src/knot/zone/backup_dir.c | 16 | ||||
-rw-r--r-- | tests-extra/tests/zone/backup/test.py | 36 | ||||
-rw-r--r-- | tests-extra/tests/zone/backup_lock/test.py | 6 |
4 files changed, 60 insertions, 3 deletions
diff --git a/doc/man_knotc.rst b/doc/man_knotc.rst index 55f4dfaff..4755a6ac3 100644 --- a/doc/man_knotc.rst +++ b/doc/man_knotc.rst @@ -151,8 +151,9 @@ Actions explicitly. If zone flushing is disabled, the original zone file is backed up instead of writing out zone contents to a file. When backing-up a catalog zone, it is recommended to prevent ongoing changes to it by use of - **zone-freeze**. - See :ref:`Notes<notes>` below about the directory permissions. (#) + **zone-freeze**. The force option allows an already existing backupdir to + be overwritten. See :ref:`Notes<notes>` below about the directory permissions. + (#) **zone-restore** [*zone*...] **+backupdir** *directory* [*filter*...] Trigger a zone data and metadata restore from a specified backup directory. diff --git a/src/knot/zone/backup_dir.c b/src/knot/zone/backup_dir.c index 7bf9fd576..da9fd2507 100644 --- a/src/knot/zone/backup_dir.c +++ b/src/knot/zone/backup_dir.c @@ -240,6 +240,22 @@ int backupdir_init(zone_backup_ctx_t *ctx) return KNOT_ENOTDIR; } } else { + if (ctx->forced) { + if (stat(ctx->backup_dir, &sb) == 0) { + int ret2 = remove_path(ctx->backup_dir, S_ISDIR(sb.st_mode)); + if (ret2 != KNOT_EOK) { + return ret2; + } + } else if (errno != ENOENT) { + return knot_map_errno(); + } else if (lstat(ctx->backup_dir, &sb) == 0 && S_ISLNK(sb.st_mode)) { + // Stale symlink. + if (unlink(ctx->backup_dir) != 0) { + return knot_map_errno(); + } + } // Omitting lstat() failure check, make_dir() will do it. + } + ret = make_dir(ctx->backup_dir, S_IRWXU | S_IRWXG, true); if (ret != KNOT_EOK) { return ret; diff --git a/tests-extra/tests/zone/backup/test.py b/tests-extra/tests/zone/backup/test.py index 215b2dcdf..68fcd924e 100644 --- a/tests-extra/tests/zone/backup/test.py +++ b/tests-extra/tests/zone/backup/test.py @@ -37,6 +37,7 @@ for z in zones: slave.zonefile_load = "none" backup_dir = master.dir + "/backup" +backup_dir2 = master.dir + "/backup2" # Backup of a backup, for debugging. slave_bck_dir = slave.dir + "/backup" zone0_expire = 45 # zone zones[0] expiration time in its SOA @@ -57,7 +58,8 @@ for z in zones: master.ctl("zone-sign " + z.name) slave.zone_wait(z, serials_init[z.name]) -master.ctl("zone-backup +backupdir %s" % backup_dir) +master.ctl("zone-backup +backupdir %s" % backup_dir, wait=True) +master.ctl("zone-backup +backupdir %s" % backup_dir2) slave.ctl("zone-backup %s %s +journal +backupdir %s +nozonefile" % \ (zones[0].name, zones[1].name, slave_bck_dir)) @@ -132,4 +134,36 @@ for i in range(start_time + zone0_expire + valgrind_delay - int(t.uptime())): resp = slave.dig(zones[0].name, "SOA") resp.check(rcode="SERVFAIL") +# Test that the older backup is replaced by the newer one. +shutil.rmtree(keydir) +master.start() +master.zones_wait(zones) +(dnskey1_5, dnskey2_5) = get_dnskeys(master, zones) +if dnskey1_5 == dnskey1_4 or dnskey2_5 == dnskey2_4: + set_err("TEST ERROR 2") +master.ctl("-f zone-backup +backupdir %s" % backup_dir, wait=True) +master.stop() +shutil.rmtree(keydir) +master.start() +master.zones_wait(zones) +(dnskey1_6, dnskey2_6) = get_dnskeys(master, zones) +if dnskey1_6 == dnskey1_5 or dnskey2_6 == dnskey2_5: + set_err("TEST ERROR 3") +master.ctl("zone-restore +backupdir %s" % backup_dir, wait=True) +master.zones_wait(zones) +(dnskey1_7, dnskey2_7) = get_dnskeys(master, zones) +if dnskey1_7 != dnskey1_5 or dnskey2_7 != dnskey2_5: + set_err("KEYS FROM FORCED BACKUP NOT RESTORED") + +# Test that even a zonefile not existing in the newer backup gets overwritten. +master.ctl("-f zone-purge %s" % zones[1].name, wait=True) +master.ctl("-f zone-backup +backupdir %s" % backup_dir, wait=True) +master.ctl("-f zone-purge", wait=True) +master.ctl("zone-restore +backupdir %s" % backup_dir, wait=True) +t.sleep(3) +resp = master.dig(zones[0].name, "SOA") +resp.check(rcode="NOERROR") +resp = master.dig(zones[1].name, "SOA") +resp.check(rcode="SERVFAIL") + t.stop() diff --git a/tests-extra/tests/zone/backup_lock/test.py b/tests-extra/tests/zone/backup_lock/test.py index 1aab2b550..0d2ef8b48 100644 --- a/tests-extra/tests/zone/backup_lock/test.py +++ b/tests-extra/tests/zone/backup_lock/test.py @@ -98,6 +98,12 @@ except: pass check_log_err(master, "already exists") +# Attempt to start backup with the "-f" option into already existing backup, expected OK. +try: + master.ctl("-f zone-backup +backupdir %s" % backup_dir, wait=True) +except: + set_err("FORCED BACKUP NOT ALLOWED") + # Attempt to start restore from non-existing backup directory, expected (not exists). try: master.ctl("zone-restore +backupdir %s" % backup_dir_void, wait=True) |