NCP 87/17 Recover Salvageable File
All checks were successful
Source release / source-package (push) Successful in 58s
All checks were successful
Source release / source-package (push) Successful in 58s
This commit is contained in:
@@ -108,6 +108,8 @@ typedef struct {
|
||||
extern int handle_func_0x57(uint8 *p, uint8 *responsedata, int task);
|
||||
extern int handle_func_0x57_salvage_scan(uint8 *p, int request_len,
|
||||
uint8 *responsedata, int task);
|
||||
extern int handle_func_0x57_salvage_recover(uint8 *p, int request_len,
|
||||
uint8 *responsedata, int task);
|
||||
extern int handle_func_0x56(uint8 *p, uint8 *responsedata, int task);
|
||||
|
||||
extern int fill_namespace_buffer(int volume, uint8 *rdata);
|
||||
|
||||
@@ -200,6 +200,8 @@ int nwsalvage_read_metadata(const char *metadata_path,
|
||||
int nwsalvage_scan_directory(int volume, const char *unix_directory,
|
||||
unsigned long scan_sequence,
|
||||
struct nwsalvage_scan_result *result);
|
||||
int nwsalvage_recover_scan_sequence(int volume, unsigned long scan_sequence,
|
||||
unsigned long directory_base, int task);
|
||||
/*
|
||||
* Capture a server-side delete before nw_unlink_node() would remove it.
|
||||
* Returns 0 when the file was moved to the recycle repository and metadata
|
||||
|
||||
@@ -2839,6 +2839,59 @@ int handle_func_0x57_salvage_scan(uint8 *q, int request_len,
|
||||
return(result);
|
||||
}
|
||||
|
||||
|
||||
int handle_func_0x57_salvage_recover(uint8 *q, int request_len,
|
||||
uint8 *responsedata, int task)
|
||||
{
|
||||
int namespace;
|
||||
uint32 scan_sequence;
|
||||
uint32 volume;
|
||||
uint32 directory_base;
|
||||
int result;
|
||||
|
||||
(void)namespace;
|
||||
(void)responsedata;
|
||||
|
||||
/*
|
||||
* ncpfs ncp_ns_salvage_file() packs 87/17 as:
|
||||
* byte subfunction 0x11
|
||||
* byte namespace
|
||||
* byte reserved
|
||||
* dword scan sequence / deleted file id
|
||||
* dword volume id
|
||||
* dword directory/base id
|
||||
* pstr new filename
|
||||
* mars_nwe recovers to the original_path from the trusted JSON sidecar; the
|
||||
* client supplied new filename is intentionally not used for the first pass.
|
||||
*/
|
||||
if (!q || request_len < 16 || q[0] != 0x11)
|
||||
return(-0xfb);
|
||||
|
||||
namespace = (int)q[1];
|
||||
scan_sequence = GET_32(q + 3);
|
||||
volume = GET_32(q + 7);
|
||||
directory_base = GET_32(q + 11);
|
||||
|
||||
if (volume >= (uint32)used_nw_volumes)
|
||||
return(-0x98);
|
||||
|
||||
result = nwsalvage_recover_scan_sequence((int)volume, scan_sequence,
|
||||
directory_base, task);
|
||||
if (result < 0) {
|
||||
if (errno == EEXIST)
|
||||
return(-0x92);
|
||||
return(-0x98);
|
||||
}
|
||||
if (result == 0)
|
||||
return(-0xff);
|
||||
|
||||
XDPRINTF((3, 0,
|
||||
"NCP 87/17 salvage recover ns=%d seq=0x%lx vol=%lu base=0x%lx result=%d",
|
||||
namespace, (unsigned long)scan_sequence, (unsigned long)volume,
|
||||
(unsigned long)directory_base, result));
|
||||
return(0);
|
||||
}
|
||||
|
||||
int handle_func_0x57(uint8 *p, uint8 *responsedata, int task)
|
||||
{
|
||||
int result = -0xfb; /* unknown request */
|
||||
|
||||
@@ -5088,6 +5088,11 @@ static int handle_ncp_serv(void)
|
||||
break;
|
||||
|
||||
case 0x11: /* 87/17 Recover Salvageable File */
|
||||
result = handle_func_0x57_salvage_recover(requestdata, requestlen,
|
||||
responsedata,
|
||||
ncprequest->task);
|
||||
break;
|
||||
|
||||
case 0x12: /* 87/18 Purge Salvageable File */
|
||||
result = -0xfb;
|
||||
break;
|
||||
|
||||
422
src/nwsalvage.c
422
src/nwsalvage.c
@@ -6,6 +6,7 @@
|
||||
#include "nwarchive.h"
|
||||
#include "nwatalk.h"
|
||||
#include "nwattrib.h"
|
||||
#include "nwfile.h"
|
||||
#include "trustee.h"
|
||||
#include "tools.h"
|
||||
#include "connect.h"
|
||||
@@ -1994,6 +1995,427 @@ int nwsalvage_scan_directory(int volume, const char *unix_directory,
|
||||
return(found);
|
||||
}
|
||||
|
||||
|
||||
static int nwsalvage_original_path_to_unix(int volume,
|
||||
const char *original_path,
|
||||
char *unixname,
|
||||
size_t unixname_len)
|
||||
{
|
||||
char volume_root[NWSALVAGE_PATH_MAX];
|
||||
char volume_name[NWSALVAGE_REPOSITORY_NAME_MAX];
|
||||
const char *colon;
|
||||
const char *rel;
|
||||
size_t volume_name_len;
|
||||
|
||||
if (!original_path || !*original_path || !unixname || !unixname_len) {
|
||||
errno = EINVAL;
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if (nw_get_volume_name(volume, (uint8 *)volume_name,
|
||||
sizeof(volume_name)) < 1 ||
|
||||
nwsalvage_copy_volume_root(volume, volume_root, sizeof(volume_root)) < 0)
|
||||
return(-1);
|
||||
|
||||
colon = strchr(original_path, ':');
|
||||
if (!colon) {
|
||||
errno = EINVAL;
|
||||
return(-1);
|
||||
}
|
||||
|
||||
volume_name_len = strlen(volume_name);
|
||||
if ((size_t)(colon - original_path) != volume_name_len ||
|
||||
strncasecmp(original_path, volume_name, volume_name_len) != 0) {
|
||||
errno = EINVAL;
|
||||
return(-1);
|
||||
}
|
||||
|
||||
rel = colon + 1;
|
||||
while (*rel == '/' || *rel == '\\')
|
||||
rel++;
|
||||
|
||||
if (!*rel || !nwsalvage_relative_path_valid(rel)) {
|
||||
errno = EINVAL;
|
||||
return(-1);
|
||||
}
|
||||
|
||||
return(nwsalvage_path_join(unixname, unixname_len, volume_root, rel));
|
||||
}
|
||||
|
||||
static int nwsalvage_hex_nibble(int c)
|
||||
{
|
||||
if (c >= '0' && c <= '9') return(c - '0');
|
||||
if (c >= 'a' && c <= 'f') return(c - 'a' + 10);
|
||||
if (c >= 'A' && c <= 'F') return(c - 'A' + 10);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
static int nwsalvage_hex_to_bytes(const char *hex, uint8 *out, size_t out_len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (!hex || !out || strlen(hex) != out_len * 2) {
|
||||
errno = EINVAL;
|
||||
return(-1);
|
||||
}
|
||||
|
||||
for (i = 0; i < out_len; i++) {
|
||||
int hi = nwsalvage_hex_nibble((unsigned char)hex[i * 2]);
|
||||
int lo = nwsalvage_hex_nibble((unsigned char)hex[i * 2 + 1]);
|
||||
if (hi < 0 || lo < 0) {
|
||||
errno = EINVAL;
|
||||
return(-1);
|
||||
}
|
||||
out[i] = (uint8)((hi << 4) | lo);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
static int nwsalvage_finder_info_is_zero(const uint8 *finder_info, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < len; i++)
|
||||
if (finder_info[i])
|
||||
return(0);
|
||||
return(1);
|
||||
}
|
||||
|
||||
static int nwsalvage_restore_metadata(int volume, const char *unixname,
|
||||
const struct nwsalvage_metadata_entry *entry)
|
||||
{
|
||||
struct stat stb;
|
||||
struct utimbuf times;
|
||||
int i;
|
||||
|
||||
if (!unixname || !entry) {
|
||||
errno = EINVAL;
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if (stat(unixname, &stb) < 0)
|
||||
return(-1);
|
||||
|
||||
(void)set_nw_attrib_dword(volume, (char *)unixname, &stb,
|
||||
(uint32)entry->attributes);
|
||||
|
||||
(void)mars_nwe_set_archive_info((char *)unixname,
|
||||
!!(entry->netware_archive_flags & MARS_NWE_ARCHIVE_HAS_DATE),
|
||||
(uint16)entry->netware_archive_date,
|
||||
!!(entry->netware_archive_flags & MARS_NWE_ARCHIVE_HAS_TIME),
|
||||
(uint16)entry->netware_archive_time,
|
||||
!!(entry->netware_archive_flags & MARS_NWE_ARCHIVE_HAS_ARCHIVER),
|
||||
(uint32)entry->netware_archiver_id);
|
||||
|
||||
(void)mars_nwe_set_file_info((char *)unixname,
|
||||
!!(entry->netware_fileinfo_flags & MARS_NWE_FILEINFO_HAS_CREATE_DATE),
|
||||
(uint16)entry->netware_create_date,
|
||||
!!(entry->netware_fileinfo_flags & MARS_NWE_FILEINFO_HAS_CREATE_TIME),
|
||||
(uint16)entry->netware_create_time,
|
||||
!!(entry->netware_fileinfo_flags & MARS_NWE_FILEINFO_HAS_CREATOR),
|
||||
(uint32)entry->netware_creator_id);
|
||||
|
||||
(void)mars_nwe_set_file_modifier_info((char *)unixname,
|
||||
!!(entry->netware_fileinfo_flags & MARS_NWE_FILEINFO_HAS_MODIFIER),
|
||||
(uint32)entry->netware_modifier_id);
|
||||
|
||||
if (entry->finder_info_hex[0]) {
|
||||
uint8 finder_info[NWATALK_FINDER_INFO_LEN];
|
||||
if (nwsalvage_hex_to_bytes(entry->finder_info_hex, finder_info,
|
||||
sizeof(finder_info)) == 0 &&
|
||||
!nwsalvage_finder_info_is_zero(finder_info, sizeof(finder_info)))
|
||||
(void)nwatalk_set_finder_info(unixname, finder_info, sizeof(finder_info));
|
||||
}
|
||||
|
||||
if (entry->afp_attributes)
|
||||
(void)nwatalk_set_afp_attributes(unixname, (uint16)entry->afp_attributes);
|
||||
|
||||
if (entry->afp_entry_id[0]) {
|
||||
char *end = NULL;
|
||||
unsigned long entry_id = strtoul(entry->afp_entry_id, &end, 0);
|
||||
if (end && *end == '\0')
|
||||
(void)nwatalk_set_entry_id(unixname, (uint32)entry_id);
|
||||
}
|
||||
|
||||
(void)tru_set_inherited_mask(volume, (uint8 *)unixname, &stb,
|
||||
(int)entry->inherited_rights_mask);
|
||||
|
||||
if (entry->trustee_count) {
|
||||
NW_OIC nwoic[NWSALVAGE_TRUSTEE_MAX];
|
||||
int count = (int)entry->trustee_count;
|
||||
if (count > NWSALVAGE_TRUSTEE_MAX)
|
||||
count = NWSALVAGE_TRUSTEE_MAX;
|
||||
memset(nwoic, 0, sizeof(nwoic));
|
||||
for (i = 0; i < count; i++) {
|
||||
nwoic[i].id = (uint32)entry->trustees[i].object_id;
|
||||
nwoic[i].trustee = (int)entry->trustees[i].rights;
|
||||
}
|
||||
(void)tru_add_trustee_set(volume, (uint8 *)unixname, &stb, count, nwoic);
|
||||
}
|
||||
|
||||
if (entry->mode) {
|
||||
if (seteuid(0)) {}
|
||||
(void)chmod(unixname, (mode_t)(entry->mode & 07777));
|
||||
(void)reseteuid();
|
||||
}
|
||||
|
||||
times.actime = (time_t)entry->atime;
|
||||
times.modtime = (time_t)entry->mtime;
|
||||
if (times.actime || times.modtime)
|
||||
(void)utime(unixname, ×);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
static int nwsalvage_copy_payload_with_novell_io(int volume,
|
||||
const char *source_unixname,
|
||||
const char *dest_unixname,
|
||||
const struct stat *source_stb,
|
||||
int task)
|
||||
{
|
||||
struct stat src_open_stb;
|
||||
struct stat dst_open_stb;
|
||||
int source_handle = -1;
|
||||
int dest_handle = -1;
|
||||
unsigned long long remaining;
|
||||
uint32 offset = 0;
|
||||
int result = 0;
|
||||
|
||||
if (!source_unixname || !dest_unixname || !source_stb) {
|
||||
errno = EINVAL;
|
||||
return(-1);
|
||||
}
|
||||
|
||||
source_handle = file_creat_open(volume, (uint8 *)source_unixname,
|
||||
&src_open_stb, 0, 1, 8, task);
|
||||
if (source_handle < 0)
|
||||
return(source_handle);
|
||||
|
||||
dest_handle = file_creat_open(volume, (uint8 *)dest_unixname,
|
||||
&dst_open_stb, FILE_ATTR_NORMAL, 3, 2 | 8,
|
||||
task);
|
||||
if (dest_handle < 0) {
|
||||
(void)nw_close_file(source_handle, 0, task);
|
||||
return(dest_handle);
|
||||
}
|
||||
|
||||
remaining = (unsigned long long)source_stb->st_size;
|
||||
while (remaining) {
|
||||
uint32 chunk;
|
||||
int copied;
|
||||
|
||||
if (offset == 0xffffffffUL) {
|
||||
result = -0xfe;
|
||||
break;
|
||||
}
|
||||
|
||||
chunk = (remaining > 0x7fffffffUL) ? 0x7fffffffUL : (uint32)remaining;
|
||||
copied = nw_server_copy(source_handle, offset, dest_handle, offset, chunk);
|
||||
if (copied < 0) {
|
||||
result = copied;
|
||||
break;
|
||||
}
|
||||
if ((uint32)copied != chunk) {
|
||||
result = -0xff;
|
||||
break;
|
||||
}
|
||||
remaining -= chunk;
|
||||
offset += chunk;
|
||||
}
|
||||
|
||||
if (!result)
|
||||
result = nw_commit_file(dest_handle);
|
||||
|
||||
(void)nw_close_file(dest_handle, 0, task);
|
||||
(void)nw_close_file(source_handle, 0, task);
|
||||
return(result);
|
||||
}
|
||||
|
||||
static int nwsalvage_metadata_volume_matches(const struct nwsalvage_metadata_entry *entry,
|
||||
const char *volume_name)
|
||||
{
|
||||
const char *colon;
|
||||
|
||||
if (!entry || !volume_name)
|
||||
return(0);
|
||||
|
||||
colon = strchr(entry->original_path, ':');
|
||||
if (!colon)
|
||||
return(0);
|
||||
|
||||
return((size_t)(colon - entry->original_path) == strlen(volume_name) &&
|
||||
strncasecmp(entry->original_path, volume_name,
|
||||
(size_t)(colon - entry->original_path)) == 0);
|
||||
}
|
||||
|
||||
static int nwsalvage_find_by_base_in_tree(const char *metadata_dir,
|
||||
const char *volume_root,
|
||||
const char *volume_name,
|
||||
unsigned long directory_base,
|
||||
unsigned long scan_sequence,
|
||||
unsigned long *match_index,
|
||||
struct nwsalvage_scan_result *result)
|
||||
{
|
||||
DIR *dir;
|
||||
struct dirent *de;
|
||||
int found = 0;
|
||||
|
||||
dir = opendir(metadata_dir);
|
||||
if (!dir)
|
||||
return(errno == ENOENT ? 0 : -1);
|
||||
|
||||
while (!found && (de = readdir(dir)) != NULL) {
|
||||
char child[NWSALVAGE_PATH_MAX];
|
||||
struct stat st;
|
||||
size_t len;
|
||||
|
||||
if (de->d_name[0] == '.')
|
||||
continue;
|
||||
if (nwsalvage_path_join(child, sizeof(child), metadata_dir, de->d_name) < 0)
|
||||
continue;
|
||||
if (stat(child, &st) < 0)
|
||||
continue;
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
found = nwsalvage_find_by_base_in_tree(child, volume_root, volume_name,
|
||||
directory_base, scan_sequence,
|
||||
match_index, result);
|
||||
if (found < 0)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!S_ISREG(st.st_mode))
|
||||
continue;
|
||||
|
||||
len = strlen(de->d_name);
|
||||
if (len < 6 || strcmp(de->d_name + len - 5, ".json") != 0)
|
||||
continue;
|
||||
|
||||
memset(&result->metadata, 0, sizeof(result->metadata));
|
||||
if (nwsalvage_read_metadata(child, &result->metadata) < 0)
|
||||
continue;
|
||||
if (!nwsalvage_metadata_volume_matches(&result->metadata, volume_name))
|
||||
continue;
|
||||
if (result->metadata.original_parent_entry_id != directory_base)
|
||||
continue;
|
||||
|
||||
if (*match_index < scan_sequence) {
|
||||
(*match_index)++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*match_index != scan_sequence)
|
||||
continue;
|
||||
|
||||
if (nwsalvage_path_join(result->recycle_path, sizeof(result->recycle_path),
|
||||
volume_root, result->metadata.recycle_relative_path) < 0)
|
||||
continue;
|
||||
|
||||
result->scan_sequence = *match_index;
|
||||
result->scan_volume = (unsigned long)atoi(volume_name); /* overwritten below */
|
||||
result->scan_directory_base = directory_base;
|
||||
strmaxcpy(result->metadata_path, child, sizeof(result->metadata_path) - 1);
|
||||
found = 1;
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
return(found);
|
||||
}
|
||||
|
||||
static int nwsalvage_find_by_base(int volume, unsigned long scan_sequence,
|
||||
unsigned long directory_base,
|
||||
struct nwsalvage_scan_result *result)
|
||||
{
|
||||
struct nwsalvage_config config;
|
||||
char volume_root[NWSALVAGE_PATH_MAX];
|
||||
char metadata_root[NWSALVAGE_PATH_MAX];
|
||||
char volume_name[NWSALVAGE_REPOSITORY_NAME_MAX];
|
||||
unsigned long match_index = 0;
|
||||
int found;
|
||||
|
||||
if (!result) {
|
||||
errno = EINVAL;
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if (nwsalvage_config_load_from_ini(&config, nwsalvage_ini_get, NULL) < 0)
|
||||
return(-1);
|
||||
if (!config.enabled)
|
||||
return(0);
|
||||
|
||||
if (nwsalvage_copy_volume_root(volume, volume_root, sizeof(volume_root)) < 0)
|
||||
return(-1);
|
||||
if (nw_get_volume_name(volume, (uint8 *)volume_name,
|
||||
sizeof(volume_name)) < 1)
|
||||
return(-1);
|
||||
if (nwsalvage_path_join(metadata_root, sizeof(metadata_root), volume_root,
|
||||
config.metadata_repository) < 0)
|
||||
return(-1);
|
||||
|
||||
memset(result, 0, sizeof(*result));
|
||||
found = nwsalvage_find_by_base_in_tree(metadata_root, volume_root, volume_name,
|
||||
directory_base, scan_sequence,
|
||||
&match_index, result);
|
||||
if (found > 0)
|
||||
result->scan_volume = (unsigned long)volume;
|
||||
return(found);
|
||||
}
|
||||
|
||||
int nwsalvage_recover_scan_sequence(int volume, unsigned long scan_sequence,
|
||||
unsigned long directory_base, int task)
|
||||
{
|
||||
struct nwsalvage_scan_result scan;
|
||||
char dest_unixname[NWSALVAGE_PATH_MAX];
|
||||
struct stat source_stb;
|
||||
int result;
|
||||
|
||||
memset(&scan, 0, sizeof(scan));
|
||||
result = nwsalvage_find_by_base(volume, scan_sequence, directory_base, &scan);
|
||||
if (result <= 0)
|
||||
return(result);
|
||||
|
||||
if (nwsalvage_original_path_to_unix(volume, scan.metadata.original_path,
|
||||
dest_unixname, sizeof(dest_unixname)) < 0)
|
||||
return(-1);
|
||||
|
||||
if (access(dest_unixname, F_OK) == 0) {
|
||||
errno = EEXIST;
|
||||
return(-1);
|
||||
}
|
||||
if (errno != ENOENT)
|
||||
return(-1);
|
||||
|
||||
if (stat(scan.recycle_path, &source_stb) < 0)
|
||||
return(-1);
|
||||
if (!S_ISREG(source_stb.st_mode)) {
|
||||
errno = EINVAL;
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if (make_parent_dirs(dest_unixname) < 0)
|
||||
return(-1);
|
||||
|
||||
result = nwsalvage_copy_payload_with_novell_io(volume, scan.recycle_path,
|
||||
dest_unixname, &source_stb,
|
||||
task);
|
||||
if (result < 0) {
|
||||
unlink(dest_unixname);
|
||||
errno = EIO;
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if (nwsalvage_restore_metadata(volume, dest_unixname, &scan.metadata) < 0) {
|
||||
/* The payload is already safely restored through mars_nwe I/O. Keep it. */
|
||||
}
|
||||
|
||||
if (unlink(scan.recycle_path) < 0)
|
||||
return(-1);
|
||||
if (unlink(scan.metadata_path) < 0)
|
||||
return(-1);
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
static void nwsalvage_fill_trustees(int volume, const char *unixname,
|
||||
const struct stat *stb,
|
||||
struct nwsalvage_deleted_entry *entry)
|
||||
|
||||
@@ -66,6 +66,10 @@ if(SALVAGE_NCPFS_INCLUDE_DIR AND SALVAGE_NCPFS_LIBRARY)
|
||||
add_executable(ncp_salvage_scan_smoke ncp_salvage_scan_smoke.c)
|
||||
target_include_directories(ncp_salvage_scan_smoke PRIVATE ${SALVAGE_NCPFS_INCLUDE_DIR})
|
||||
target_link_libraries(ncp_salvage_scan_smoke ${SALVAGE_NCPFS_LIBRARY})
|
||||
|
||||
add_executable(ncp_salvage_recover_smoke ncp_salvage_recover_smoke.c)
|
||||
target_include_directories(ncp_salvage_recover_smoke PRIVATE ${SALVAGE_NCPFS_INCLUDE_DIR})
|
||||
target_link_libraries(ncp_salvage_recover_smoke ${SALVAGE_NCPFS_LIBRARY})
|
||||
else()
|
||||
message(STATUS
|
||||
"Skipping salvage NCP smoke helpers: ncpfs/libncp headers or library not found"
|
||||
|
||||
132
tests/salvage/ncp_salvage_recover_smoke.c
Normal file
132
tests/salvage/ncp_salvage_recover_smoke.c
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Linux smoke helper for NCP 87/17 Recover Salvageable File.
|
||||
*
|
||||
* The helper scans a directory with the official ncplib salvage scan API, then
|
||||
* recovers the Nth matching deleted original name using ncp_ns_salvage_file().
|
||||
* It intentionally selects by the returned scan tuple (seq/vol/base), not by
|
||||
* backend .recycle version names such as "Copy #1 of ...".
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <ncp/nwcalls.h>
|
||||
#include <ncp/ncplib.h>
|
||||
|
||||
#ifndef NCP_PATH_STD
|
||||
#define NCP_PATH_STD 0
|
||||
#endif
|
||||
|
||||
/* Older distro headers may miss the prototype although patched libncp has it. */
|
||||
long ncp_ns_salvage_file(NWCONN_HANDLE conn, u_int8_t src_ns,
|
||||
const struct ncp_deleted_file *finfo, const char *newfname);
|
||||
|
||||
static void usage(const char *prog)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: %s [ncpfs options] DIRECTORY ORIGINAL_NAME [MATCH_INDEX]\n"
|
||||
"\n"
|
||||
"ncpfs options are parsed by ncp_initialize(), for example:\n"
|
||||
" -S SERVER -U USER -P PASSWORD -n\n"
|
||||
"\n"
|
||||
"Example:\n"
|
||||
" %s -S MARS -U SUPERVISOR -P secret SYS:PUBLIC SLVGCHK.TXT 0\n",
|
||||
prog, prog);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
NWCONN_HANDLE conn;
|
||||
long init_err = 0;
|
||||
const char *path = NULL;
|
||||
const char *wanted_name = NULL;
|
||||
int wanted_index = 0;
|
||||
int matched_index = 0;
|
||||
struct ncp_deleted_file info;
|
||||
struct ncp_deleted_file selected;
|
||||
char name[512];
|
||||
char selected_name[512];
|
||||
long err;
|
||||
int selected_valid = 0;
|
||||
int i;
|
||||
|
||||
if (NWCallsInit(NULL, NULL)) {
|
||||
fprintf(stderr, "NWCallsInit failed\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
conn = ncp_initialize(&argc, argv, 1, &init_err);
|
||||
if (!conn) {
|
||||
fprintf(stderr, "ncp_initialize/login failed: %ld\n", init_err);
|
||||
usage(argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
|
||||
usage(argv[0]);
|
||||
ncp_close(conn);
|
||||
return 0;
|
||||
} else if (!path) {
|
||||
path = argv[i];
|
||||
} else if (!wanted_name) {
|
||||
wanted_name = argv[i];
|
||||
} else {
|
||||
wanted_index = atoi(argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!path || !wanted_name) {
|
||||
usage(argv[0]);
|
||||
ncp_close(conn);
|
||||
return 2;
|
||||
}
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
memset(&selected, 0, sizeof(selected));
|
||||
selected_name[0] = '\0';
|
||||
info.seq = -1;
|
||||
|
||||
while ((err = ncp_ns_scan_salvageable_file(conn, NW_NS_DOS,
|
||||
NCP_DIRSTYLE_NOHANDLE, 0, 0, (const unsigned char *)path,
|
||||
NCP_PATH_STD, &info, name, sizeof(name))) == 0) {
|
||||
printf("NCP salvage recover candidate seq=%d vol=%u base=%u name=%s\n",
|
||||
(int)info.seq, (unsigned int)info.vol,
|
||||
(unsigned int)info.base, name);
|
||||
if (!strcmp(name, wanted_name)) {
|
||||
if (matched_index == wanted_index) {
|
||||
selected = info;
|
||||
snprintf(selected_name, sizeof(selected_name), "%s", name);
|
||||
selected_valid = 1;
|
||||
break;
|
||||
}
|
||||
matched_index++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!selected_valid) {
|
||||
fprintf(stderr,
|
||||
"no matching salvage entry: path=%s name=%s index=%d last_error=0x%04x\n",
|
||||
path, wanted_name, wanted_index, (unsigned int)err);
|
||||
ncp_close(conn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
err = ncp_ns_salvage_file(conn, NW_NS_DOS, &selected, selected_name);
|
||||
if (err) {
|
||||
fprintf(stderr,
|
||||
"NCP salvage recover failed: seq=%d vol=%u base=%u name=%s error=0x%04x\n",
|
||||
(int)selected.seq, (unsigned int)selected.vol,
|
||||
(unsigned int)selected.base, selected_name, (unsigned int)err);
|
||||
ncp_close(conn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("NCP salvage recover ok seq=%d vol=%u base=%u name=%s\n",
|
||||
(int)selected.seq, (unsigned int)selected.vol,
|
||||
(unsigned int)selected.base, selected_name);
|
||||
ncp_close(conn);
|
||||
return 0;
|
||||
}
|
||||
@@ -265,6 +265,65 @@ cat_metadata() {
|
||||
grep -q '"trustees"' "$path" || fail_check "metadata missing trustees"
|
||||
}
|
||||
|
||||
|
||||
run_ncp_salvage_recover() {
|
||||
local status before_scan_tmp after_scan_tmp
|
||||
before_scan_tmp=$(mktemp "${TMPDIR:-/tmp}/mars-salvage-recover-before.XXXXXX") || {
|
||||
fail_check "could not allocate recover pre-scan temp file"
|
||||
return 1
|
||||
}
|
||||
after_scan_tmp=$(mktemp "${TMPDIR:-/tmp}/mars-salvage-recover-after.XXXXXX") || {
|
||||
rm -f "$before_scan_tmp"
|
||||
fail_check "could not allocate recover post-scan temp file"
|
||||
return 1
|
||||
}
|
||||
|
||||
section "NCP salvage recover 87/17"
|
||||
emit "recover_path=$SCAN_PATH"
|
||||
emit "recover_name=$BASENAME"
|
||||
emit "\$ ./ncp_salvage_recover_smoke -S '$SERVER' -U '$USER_NAME' -P ****** '$SCAN_PATH' '$BASENAME' 0"
|
||||
"$SCRIPT_DIR/ncp_salvage_recover_smoke" -S "$SERVER" -U "$USER_NAME" \
|
||||
-P "$PASSWORD" "$SCAN_PATH" "$BASENAME" 0 2>&1 | tee "$before_scan_tmp" | tee -a "$REPORT_TMP"
|
||||
status=${PIPESTATUS[0]}
|
||||
emit "[exit=$status]"
|
||||
if [ "$status" -ne 0 ]; then
|
||||
fail_check "NCP salvage recover failed for $SCAN_PATH/$BASENAME"
|
||||
rm -f "$before_scan_tmp" "$after_scan_tmp"
|
||||
return 1
|
||||
fi
|
||||
|
||||
check_file "recover smoke: restored live payload" "$UNIX_PATH"
|
||||
if [ -e "$FIRST_RECYCLE" ]; then
|
||||
fail_check "recover smoke: first recycle payload still exists: $FIRST_RECYCLE"
|
||||
else
|
||||
emit "recover smoke: first recycle payload consumed: $FIRST_RECYCLE"
|
||||
fi
|
||||
if [ -e "$FIRST_META" ]; then
|
||||
fail_check "recover smoke: first metadata sidecar still exists: $FIRST_META"
|
||||
else
|
||||
emit "recover smoke: first metadata sidecar consumed: $FIRST_META"
|
||||
fi
|
||||
|
||||
section "NCP salvage scan after recover"
|
||||
"$SCRIPT_DIR/ncp_salvage_scan_smoke" -S "$SERVER" -U "$USER_NAME" \
|
||||
-P "$PASSWORD" "$SCAN_PATH" 2>&1 | tee "$after_scan_tmp" | tee -a "$REPORT_TMP"
|
||||
status=${PIPESTATUS[0]}
|
||||
emit "[exit=$status]"
|
||||
if [ "$status" -ne 0 ]; then
|
||||
fail_check "NCP salvage scan after recover failed for $SCAN_PATH"
|
||||
else
|
||||
local basename_count
|
||||
basename_count=$(grep -F "name=$BASENAME" "$after_scan_tmp" | wc -l)
|
||||
if [ "$basename_count" -lt 1 ]; then
|
||||
fail_check "NCP salvage scan after recover expected remaining history entry named $BASENAME"
|
||||
else
|
||||
emit "NCP salvage scan after recover remaining $BASENAME entries=$basename_count"
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -f "$before_scan_tmp" "$after_scan_tmp"
|
||||
}
|
||||
|
||||
run_ncp_salvage_scan() {
|
||||
local status scan_tmp
|
||||
scan_tmp=$(mktemp "${TMPDIR:-/tmp}/mars-salvage-scan.XXXXXX") || {
|
||||
@@ -337,6 +396,7 @@ if [ -f "$SECOND_META" ]; then
|
||||
fi
|
||||
|
||||
run_ncp_salvage_scan
|
||||
run_ncp_salvage_recover
|
||||
|
||||
finish_report
|
||||
[ "$FAILURES" -eq 0 ]
|
||||
|
||||
Reference in New Issue
Block a user