|
44 | 44 | #include <cleanup.h> |
45 | 45 | #include <protocol.h> |
46 | 46 | #include <sequence.h> |
| 47 | +#include <files_lib.h> |
47 | 48 |
|
48 | 49 | #define ARG_UNUSED __attribute__((unused)) |
49 | 50 |
|
@@ -100,6 +101,8 @@ static const Description COMMANDS[] = |
100 | 101 | "\t\t\t(%d can be used in both the remote and output file paths when '-j' is used)"}, |
101 | 102 | {"opendir", "List files and folders in a directory", |
102 | 103 | "cf-net opendir masterfiles"}, |
| 104 | + {"getdir", "Recursively downloads files and folders in a directory", |
| 105 | + "cf-net getdir masterfiles"}, |
103 | 106 | {NULL, NULL, NULL} |
104 | 107 | }; |
105 | 108 |
|
@@ -144,6 +147,7 @@ static const char *const HINTS[] = |
144 | 147 | generator_macro(STAT) \ |
145 | 148 | generator_macro(GET) \ |
146 | 149 | generator_macro(OPENDIR) \ |
| 150 | + generator_macro(GETDIR) \ |
147 | 151 | generator_macro(MULTI) \ |
148 | 152 | generator_macro(MULTITLS) \ |
149 | 153 | generator_macro(HELP) \ |
@@ -197,6 +201,7 @@ static int CFNetGet(CFNetOptions *opts, const char *hostname, char **args); |
197 | 201 | static int CFNetOpenDir(CFNetOptions *opts, const char *hostname, char **args); |
198 | 202 | static int CFNetMulti(const char *server); |
199 | 203 | static int CFNetMultiTLS(const char *server, const char *use_protocol_version); |
| 204 | +static int CFNetGetDir(CFNetOptions *opts, const char *hostname, char **args); |
200 | 205 |
|
201 | 206 |
|
202 | 207 | //******************************************************************* |
@@ -411,6 +416,8 @@ static int CFNetCommandSwitch(CFNetOptions *opts, const char *hostname, |
411 | 416 | return CFNetGet(opts, hostname, args); |
412 | 417 | case CFNET_CMD_OPENDIR: |
413 | 418 | return CFNetOpenDir(opts, hostname, args); |
| 419 | + case CFNET_CMD_GETDIR: |
| 420 | + return CFNetGetDir(opts, hostname, args); |
414 | 421 | case CFNET_CMD_MULTI: |
415 | 422 | return CFNetMulti(hostname); |
416 | 423 | case CFNET_CMD_MULTITLS: |
@@ -976,6 +983,227 @@ static int CFNetOpenDir(ARG_UNUSED CFNetOptions *opts, const char *hostname, cha |
976 | 983 | return 0; |
977 | 984 | } |
978 | 985 |
|
| 986 | +static int CFNetGetDir( CFNetOptions *opts, const char *hostname, char **args) |
| 987 | +{ |
| 988 | + assert(opts); |
| 989 | + assert(hostname); |
| 990 | + assert(args); |
| 991 | + char *local_dir = NULL; |
| 992 | + |
| 993 | + int argc = 0; |
| 994 | + while (args[argc] != NULL) |
| 995 | + { |
| 996 | + ++argc; |
| 997 | + } |
| 998 | + |
| 999 | + static struct option longopts[] = { |
| 1000 | + { "output", required_argument, NULL, 'o' }, |
| 1001 | + { NULL, 0, NULL, 0 } |
| 1002 | + }; |
| 1003 | + assert(opts != NULL); |
| 1004 | + if (argc <= 1) |
| 1005 | + { |
| 1006 | + return invalid_command("getdir"); |
| 1007 | + } |
| 1008 | + extern int optind; |
| 1009 | + optind = 0; |
| 1010 | + extern char *optarg; |
| 1011 | + int c = 0; |
| 1012 | + const char *optstr = "o:"; |
| 1013 | + bool specified_path = false; |
| 1014 | + while ((c = getopt_long(argc, args, optstr, longopts, NULL)) |
| 1015 | + != -1) |
| 1016 | + { |
| 1017 | + switch (c) |
| 1018 | + { |
| 1019 | + case 'o': |
| 1020 | + { |
| 1021 | + if (local_dir != NULL) |
| 1022 | + { |
| 1023 | + Log(LOG_LEVEL_INFO, |
| 1024 | + "Warning: multiple occurences of -o in command, "\ |
| 1025 | + "only last one will be used."); |
| 1026 | + free(local_dir); |
| 1027 | + } |
| 1028 | + local_dir = xstrdup(optarg); |
| 1029 | + specified_path = true; |
| 1030 | + break; |
| 1031 | + } |
| 1032 | + case ':': |
| 1033 | + { |
| 1034 | + return invalid_command("getdir"); |
| 1035 | + break; |
| 1036 | + } |
| 1037 | + case '?': |
| 1038 | + { |
| 1039 | + return invalid_command("getdir"); |
| 1040 | + break; |
| 1041 | + } |
| 1042 | + default: |
| 1043 | + { |
| 1044 | + printf("Default optarg = '%s', c = '%c' = %i\n", |
| 1045 | + optarg, c, (int)c); |
| 1046 | + break; |
| 1047 | + } |
| 1048 | + } |
| 1049 | + } |
| 1050 | + args = &(args[optind]); |
| 1051 | + argc -= optind; |
| 1052 | + char *remote_dir = args[0]; |
| 1053 | + if (specified_path) |
| 1054 | + { |
| 1055 | + size_t len = strlen(local_dir) + strlen(basename(remote_dir)) + 2; // / and '\0' |
| 1056 | + char *temp = malloc(len); |
| 1057 | + if (temp == NULL) |
| 1058 | + { |
| 1059 | + free(local_dir); |
| 1060 | + return -1; |
| 1061 | + } |
| 1062 | + snprintf(temp, len, "%s/%s", local_dir, basename(remote_dir)); |
| 1063 | + free(local_dir); |
| 1064 | + local_dir = temp; |
| 1065 | + } |
| 1066 | + |
| 1067 | + if (local_dir == NULL) |
| 1068 | + { |
| 1069 | + char *base = basename(remote_dir); |
| 1070 | + local_dir = xstrdup(base); |
| 1071 | + } |
| 1072 | + |
| 1073 | + AgentConnection *conn = CFNetOpenConnection(hostname, opts->use_protocol_version); |
| 1074 | + if (conn == NULL) |
| 1075 | + { |
| 1076 | + free(local_dir); |
| 1077 | + return -1; |
| 1078 | + } |
| 1079 | + struct stat sb; |
| 1080 | + bool ret = ProtocolStat(conn, remote_dir, &sb); |
| 1081 | + if (!ret) |
| 1082 | + { |
| 1083 | + printf("Could not stat: '%s'\n", remote_dir); |
| 1084 | + free(local_dir); |
| 1085 | + CFNetDisconnect(conn); |
| 1086 | + return -1; |
| 1087 | + } |
| 1088 | + if (!S_ISDIR(sb.st_mode)) |
| 1089 | + { |
| 1090 | + printf("'%s' is not a directory, use 'get' for single file download\n", remote_dir); |
| 1091 | + free(local_dir); |
| 1092 | + CFNetDisconnect(conn); |
| 1093 | + return -1; |
| 1094 | + } |
| 1095 | + |
| 1096 | + Seq *seq = ProtocolOpenDir(conn, remote_dir); |
| 1097 | + if (seq == NULL) |
| 1098 | + { |
| 1099 | + free(local_dir); |
| 1100 | + CFNetDisconnect(conn); |
| 1101 | + return -1; |
| 1102 | + } |
| 1103 | + |
| 1104 | + for (size_t i = 0; i < SeqLength(seq); i++) |
| 1105 | + { |
| 1106 | + char dir_entry[PATH_MAX]; |
| 1107 | + char *curr = SeqAt(seq, i); |
| 1108 | + |
| 1109 | + if (strcmp(".", curr) == 0 || strcmp("..", curr) == 0) |
| 1110 | + { |
| 1111 | + continue; |
| 1112 | + } |
| 1113 | + |
| 1114 | + snprintf(dir_entry, sizeof(dir_entry), "%s/%s", remote_dir, curr); |
| 1115 | + |
| 1116 | + struct stat sb; |
| 1117 | + if (!ProtocolStat(conn, dir_entry, &sb)) |
| 1118 | + { |
| 1119 | + continue; |
| 1120 | + } |
| 1121 | + |
| 1122 | + if (S_ISDIR(sb.st_mode)) |
| 1123 | + { |
| 1124 | + bool force = false; |
| 1125 | + char path[PATH_MAX]; |
| 1126 | + int written; |
| 1127 | + |
| 1128 | + if (specified_path) |
| 1129 | + { |
| 1130 | + written = snprintf(path, sizeof(path), "%s/%s/", local_dir, curr); |
| 1131 | + } |
| 1132 | + else |
| 1133 | + { |
| 1134 | + char cwd[PATH_MAX]; |
| 1135 | + if (getcwd(cwd, sizeof(cwd)) != NULL) |
| 1136 | + { |
| 1137 | + written = snprintf(path, sizeof(path), "%s/%s/%s/", cwd, local_dir, curr); |
| 1138 | + } |
| 1139 | + else |
| 1140 | + { |
| 1141 | + Log(LOG_LEVEL_ERR, "Failed to get current working directory"); |
| 1142 | + continue; |
| 1143 | + } |
| 1144 | + } |
| 1145 | + if (written < 0 || written >= sizeof(path)) |
| 1146 | + { |
| 1147 | + Log(LOG_LEVEL_ERR, "Path too long for directory: %s", curr); |
| 1148 | + continue; |
| 1149 | + } |
| 1150 | + MakeParentDirectory(path, force, NULL); |
| 1151 | + |
| 1152 | + Seq *items = ProtocolOpenDir(conn, dir_entry); |
| 1153 | + if (items == NULL) |
| 1154 | + { |
| 1155 | + Log(LOG_LEVEL_ERR, "Failed to open subdir: %s", dir_entry); |
| 1156 | + continue; |
| 1157 | + } |
| 1158 | + |
| 1159 | + for (size_t j = 0; j < SeqLength(items); j++) |
| 1160 | + { |
| 1161 | + char *item = SeqAt(items, j); |
| 1162 | + if (strcmp(".", item) == 0 || strcmp("..", item) == 0) |
| 1163 | + { |
| 1164 | + continue; |
| 1165 | + } |
| 1166 | + |
| 1167 | + char buf[PATH_MAX]; |
| 1168 | + snprintf(buf, sizeof(buf), "%s/%s", curr, item); |
| 1169 | + SeqAppend(seq, xstrdup(buf)); |
| 1170 | + } |
| 1171 | + SeqDestroy(items); |
| 1172 | + continue; |
| 1173 | + } |
| 1174 | + |
| 1175 | + if (sb.st_uid == 0) // File is owned by root, bad access -> skip |
| 1176 | + { |
| 1177 | + printf("File '%s' is owned by root... Skipping\n", dir_entry); |
| 1178 | + continue; |
| 1179 | + } |
| 1180 | + |
| 1181 | + char curr_dir[PATH_MAX]; |
| 1182 | + snprintf(curr_dir, sizeof(curr_dir), "%s/%s", local_dir, curr); |
| 1183 | + GetFileData *filedata = calloc(1, sizeof(GetFileData)); |
| 1184 | + |
| 1185 | + snprintf(filedata->local_file, PATH_MAX, "%s", curr_dir); |
| 1186 | + snprintf(filedata->remote_file, PATH_MAX, "%s", dir_entry); |
| 1187 | + filedata->hostname = hostname; |
| 1188 | + filedata->use_protocol_version = opts->use_protocol_version; |
| 1189 | + filedata->print_stats = opts->print_stats; |
| 1190 | + |
| 1191 | + filedata->ret = ProtocolStatGet(conn, filedata->remote_file, |
| 1192 | + filedata->local_file, 0644, filedata->print_stats); |
| 1193 | + if (!filedata->ret) |
| 1194 | + { |
| 1195 | + Log(LOG_LEVEL_ERR,"Failed to get remote file: %s:%s", conn->this_server, filedata->remote_file); |
| 1196 | + } |
| 1197 | + |
| 1198 | + free(filedata); |
| 1199 | + } |
| 1200 | + |
| 1201 | + SeqDestroy(seq); |
| 1202 | + free(local_dir); |
| 1203 | + CFNetDisconnect(conn); |
| 1204 | + return 0; |
| 1205 | +} |
| 1206 | + |
979 | 1207 | static int CFNetMulti(const char *server) |
980 | 1208 | { |
981 | 1209 | time_t start; |
|
0 commit comments