Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 232 additions & 0 deletions cf-net/cf-net.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include <cleanup.h>
#include <protocol.h>
#include <sequence.h>
#include <files_lib.h>

#define ARG_UNUSED __attribute__((unused))

Expand Down Expand Up @@ -100,6 +101,8 @@
"\t\t\t(%d can be used in both the remote and output file paths when '-j' is used)"},
{"opendir", "List files and folders in a directory",
"cf-net opendir masterfiles"},
{"getdir", "Recursively downloads files and folders in a directory",
"cf-net getdir masterfiles"},
{NULL, NULL, NULL}
};

Expand Down Expand Up @@ -144,6 +147,7 @@
generator_macro(STAT) \
generator_macro(GET) \
generator_macro(OPENDIR) \
generator_macro(GETDIR) \
generator_macro(MULTI) \
generator_macro(MULTITLS) \
generator_macro(HELP) \
Expand Down Expand Up @@ -197,6 +201,7 @@
static int CFNetOpenDir(CFNetOptions *opts, const char *hostname, char **args);
static int CFNetMulti(const char *server);
static int CFNetMultiTLS(const char *server, const char *use_protocol_version);
static int CFNetGetDir(CFNetOptions *opts, const char *hostname, char **args);


//*******************************************************************
Expand Down Expand Up @@ -411,6 +416,8 @@
return CFNetGet(opts, hostname, args);
case CFNET_CMD_OPENDIR:
return CFNetOpenDir(opts, hostname, args);
case CFNET_CMD_GETDIR:
return CFNetGetDir(opts, hostname, args);
case CFNET_CMD_MULTI:
return CFNetMulti(hostname);
case CFNET_CMD_MULTITLS:
Expand Down Expand Up @@ -875,7 +882,7 @@
{
snprintf(threads[i]->data->local_file, PATH_MAX, local_file, i);
}
else

Check failure

Code scanning / CodeQL

Non-constant format string Critical

The format string argument to
snprintf
has a source which cannot be verified to originate from a string literal.
The format string argument to
snprintf
has a source which cannot be verified to originate from a string literal.
The format string argument to
snprintf
has a source which cannot be verified to originate from a string literal.
The format string argument to
snprintf
has a source which cannot be verified to originate from a string literal.
{
snprintf(threads[i]->data->local_file, PATH_MAX, "%s.%d", local_file, i);
}
Expand Down Expand Up @@ -976,6 +983,231 @@
return 0;
}

static int CFNetGetDir( CFNetOptions *opts, const char *hostname, char **args)
{
assert(opts);
assert(hostname);
assert(args);
char *local_dir = NULL;

int argc = 0;
while (args[argc] != NULL)

Check warning

Code scanning / CodeQL

Poorly documented large function Warning

Poorly documented function: fewer than 2% comments for a function of 220 lines.
{
++argc;
}

static struct option longopts[] = {
{ "output", required_argument, NULL, 'o' },
{ NULL, 0, NULL, 0 }
};
assert(opts != NULL);
if (argc <= 1)
{
return invalid_command("getdir");
}
extern int optind;
optind = 0;
extern char *optarg;
int c = 0;
const char *optstr = "o:";
bool specified_path = false;
while ((c = getopt_long(argc, args, optstr, longopts, NULL))
!= -1)
{
switch (c)
{
case 'o':
{
if (local_dir != NULL)
{
Log(LOG_LEVEL_INFO,
"Warning: multiple occurences of -o in command, "\
"only last one will be used.");
free(local_dir);
}
local_dir = xstrdup(optarg);
specified_path = true;
break;
}
case ':':
{
return invalid_command("getdir");
break;
}
case '?':
{
return invalid_command("getdir");
break;
}
default:
{
printf("Default optarg = '%s', c = '%c' = %i\n",
optarg, c, (int)c);
break;
}
}
}
args = &(args[optind]);
argc -= optind;
char *remote_dir = args[0];
if (specified_path)
{
size_t len = strlen(local_dir) + strlen(basename(remote_dir)) + 2; // / and '\0'
char *temp = malloc(len);
if (temp == NULL)
{
free(local_dir);
return -1;
}
snprintf(temp, len, "%s/%s", local_dir, basename(remote_dir));
free(local_dir);
local_dir = temp;
}

if (local_dir == NULL)
{
char *base = basename(remote_dir);
local_dir = xstrdup(base);
}

AgentConnection *conn = CFNetOpenConnection(hostname, opts->use_protocol_version);
if (conn == NULL)
{
free(local_dir);
return -1;
}
struct stat sb;
bool ret = ProtocolStat(conn, remote_dir, &sb);
if (!ret)
{
printf("Could not stat: '%s'\n", remote_dir);
free(local_dir);
CFNetDisconnect(conn);
return -1;
}
if (!S_ISDIR(sb.st_mode))
{
printf("'%s' is not a directory, use 'get' for single file download\n", remote_dir);
free(local_dir);
CFNetDisconnect(conn);
return -1;
}

Seq *seq = ProtocolOpenDir(conn, remote_dir);
if (seq == NULL)
{
free(local_dir);
CFNetDisconnect(conn);
return -1;
}

for (size_t i = 0; i < SeqLength(seq); i++)
{
char dir_entry[PATH_MAX];
char *curr = SeqAt(seq, i);

if (strcmp(".", curr) == 0 || strcmp("..", curr) == 0)
{
continue;
}

snprintf(dir_entry, sizeof(dir_entry), "%s/%s", remote_dir, curr);

if (!ProtocolStat(conn, dir_entry, &sb))
{
continue;
}

if (S_ISDIR(sb.st_mode))
{
bool force = false;
char path[PATH_MAX];
int written;

if (specified_path)
{
written = snprintf(path, sizeof(path), "%s/%s/", local_dir, curr);
}
else
{
char cwd[PATH_MAX];
if (getcwd(cwd, sizeof(cwd)) != NULL)
{
written = snprintf(path, sizeof(path), "%s/%s/%s/", cwd, local_dir, curr);
}
else
{
Log(LOG_LEVEL_ERR, "Failed to get current working directory");
continue;
}
}
if (written < 0 || written >= sizeof(path))
{
Log(LOG_LEVEL_ERR, "Path too long for directory: %s", curr);
continue;
}
MakeParentDirectory(path, force, NULL);

Seq *items = ProtocolOpenDir(conn, dir_entry);
if (items == NULL)
{
Log(LOG_LEVEL_ERR, "Failed to open subdir: %s", dir_entry);
continue;
}

for (size_t j = 0; j < SeqLength(items); j++)
{
char *item = SeqAt(items, j);
if (strcmp(".", item) == 0 || strcmp("..", item) == 0)
{
continue;
}

char buf[PATH_MAX];
snprintf(buf, sizeof(buf), "%s/%s", curr, item);
SeqAppend(seq, xstrdup(buf));
}
SeqDestroy(items);
continue;
}

if (sb.st_uid == 0) // File is owned by root, bad access -> skip
{
printf("File '%s' is owned by root... Skipping\n", dir_entry);
continue;
}

char curr_dir[PATH_MAX];
snprintf(curr_dir, sizeof(curr_dir), "%s/%s", local_dir, curr);

GetFileData *filedata = calloc(1, sizeof(GetFileData));
if (filedata == NULL)
{
continue;
}

snprintf(filedata->local_file, PATH_MAX, "%s", curr_dir);
snprintf(filedata->remote_file, PATH_MAX, "%s", dir_entry);
filedata->hostname = hostname;
filedata->use_protocol_version = opts->use_protocol_version;
filedata->print_stats = opts->print_stats;

filedata->ret = ProtocolStatGet(conn, filedata->remote_file,
filedata->local_file, 0644, filedata->print_stats);
if (!filedata->ret)
{
Log(LOG_LEVEL_ERR,"Failed to get remote file: %s:%s", conn->this_server, filedata->remote_file);
}

free(filedata);
}

SeqDestroy(seq);
free(local_dir);
CFNetDisconnect(conn);
return 0;
}

static int CFNetMulti(const char *server)
{
time_t start;
Expand Down
Loading