1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 #include "fs.h"
21 #include "download.h"
22 #include "upload.h"
23
24 #include <guacamole/client.h>
25 #include <guacamole/object.h>
26 #include <guacamole/pool.h>
27 #include <guacamole/protocol.h>
28 #include <guacamole/socket.h>
29 #include <guacamole/string.h>
30 #include <guacamole/user.h>
31 #include <winpr/file.h>
32 #include <winpr/nt.h>
33
34 #include <dirent.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <fnmatch.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/stat.h>
42 #include <sys/statvfs.h>
43 #include <unistd.h>
44
guac_rdp_fs_alloc(guac_client * client,const char * drive_path,int create_drive_path,int disable_download,int disable_upload)45 guac_rdp_fs* guac_rdp_fs_alloc(guac_client* client, const char* drive_path,
46 int create_drive_path, int disable_download, int disable_upload) {
47
48 /* Create drive path if it does not exist */
49 if (create_drive_path) {
50 guac_client_log(client, GUAC_LOG_DEBUG,
51 "%s: Creating directory \"%s\" if necessary.",
52 __func__, drive_path);
53
54 /* Log error if directory creation fails */
55 if (mkdir(drive_path, S_IRWXU) && errno != EEXIST) {
56 guac_client_log(client, GUAC_LOG_ERROR,
57 "Unable to create directory \"%s\": %s",
58 drive_path, strerror(errno));
59 }
60 }
61
62 guac_rdp_fs* fs = malloc(sizeof(guac_rdp_fs));
63
64 fs->client = client;
65 fs->drive_path = strdup(drive_path);
66 fs->file_id_pool = guac_pool_alloc(0);
67 fs->open_files = 0;
68 fs->disable_download = disable_download;
69 fs->disable_upload = disable_upload;
70
71 return fs;
72
73 }
74
guac_rdp_fs_free(guac_rdp_fs * fs)75 void guac_rdp_fs_free(guac_rdp_fs* fs) {
76 guac_pool_free(fs->file_id_pool);
77 free(fs->drive_path);
78 free(fs);
79 }
80
guac_rdp_fs_alloc_object(guac_rdp_fs * fs,guac_user * user)81 guac_object* guac_rdp_fs_alloc_object(guac_rdp_fs* fs, guac_user* user) {
82
83 /* Init filesystem */
84 guac_object* fs_object = guac_user_alloc_object(user);
85 fs_object->get_handler = guac_rdp_download_get_handler;
86
87 /* Assign handler only if uploads are not disabled. */
88 if (!fs->disable_upload)
89 fs_object->put_handler = guac_rdp_upload_put_handler;
90
91 fs_object->data = fs;
92
93 /* Send filesystem to user */
94 guac_protocol_send_filesystem(user->socket, fs_object, "Shared Drive");
95 guac_socket_flush(user->socket);
96
97 return fs_object;
98
99 }
100
guac_rdp_fs_expose(guac_user * user,void * data)101 void* guac_rdp_fs_expose(guac_user* user, void* data) {
102
103 guac_rdp_fs* fs = (guac_rdp_fs*) data;
104
105 /* No need to expose if there is no filesystem or the user has left */
106 if (user == NULL || fs == NULL)
107 return NULL;
108
109 /* Allocate and expose filesystem object for user */
110 return guac_rdp_fs_alloc_object(fs, user);
111
112 }
113
114 /**
115 * Translates an absolute Windows path to an absolute path which is within the
116 * "drive path" specified in the connection settings. No checking is performed
117 * on the path provided, which is assumed to have already been normalized and
118 * validated as absolute.
119 *
120 * @param fs
121 * The filesystem containing the file whose path is being translated.
122 *
123 * @param virtual_path
124 * The absolute path to the file on the simulated filesystem, relative to
125 * the simulated filesystem root.
126 *
127 * @param real_path
128 * The buffer in which to store the absolute path to the real file on the
129 * local filesystem.
130 */
__guac_rdp_fs_translate_path(guac_rdp_fs * fs,const char * virtual_path,char * real_path)131 static void __guac_rdp_fs_translate_path(guac_rdp_fs* fs,
132 const char* virtual_path, char* real_path) {
133
134 /* Get drive path */
135 char* drive_path = fs->drive_path;
136
137 int i;
138
139 /* Start with path from settings */
140 for (i=0; i<GUAC_RDP_FS_MAX_PATH-1; i++) {
141
142 /* Break on end-of-string */
143 char c = *(drive_path++);
144 if (c == 0)
145 break;
146
147 /* Copy character */
148 *(real_path++) = c;
149
150 }
151
152 /* Translate path */
153 for (; i<GUAC_RDP_FS_MAX_PATH-1; i++) {
154
155 /* Stop at end of string */
156 char c = *(virtual_path++);
157 if (c == 0)
158 break;
159
160 /* Translate backslashes to forward slashes */
161 if (c == '\\')
162 c = '/';
163
164 /* Store in real path buffer */
165 *(real_path++)= c;
166
167 }
168
169 /* Null terminator */
170 *real_path = 0;
171
172 }
173
guac_rdp_fs_get_errorcode(int err)174 int guac_rdp_fs_get_errorcode(int err) {
175
176 /* Translate errno codes to GUAC_RDP_FS codes */
177 if (err == ENFILE) return GUAC_RDP_FS_ENFILE;
178 if (err == ENOENT) return GUAC_RDP_FS_ENOENT;
179 if (err == ENOTDIR) return GUAC_RDP_FS_ENOTDIR;
180 if (err == ENOSPC) return GUAC_RDP_FS_ENOSPC;
181 if (err == EISDIR) return GUAC_RDP_FS_EISDIR;
182 if (err == EACCES) return GUAC_RDP_FS_EACCES;
183 if (err == EEXIST) return GUAC_RDP_FS_EEXIST;
184 if (err == EINVAL) return GUAC_RDP_FS_EINVAL;
185 if (err == ENOSYS) return GUAC_RDP_FS_ENOSYS;
186 if (err == ENOTSUP) return GUAC_RDP_FS_ENOTSUP;
187
188 /* Default to invalid parameter */
189 return GUAC_RDP_FS_EINVAL;
190
191 }
192
guac_rdp_fs_get_status(int err)193 int guac_rdp_fs_get_status(int err) {
194
195 /* Translate GUAC_RDP_FS error code to RDPDR status code */
196 if (err == GUAC_RDP_FS_ENFILE) return STATUS_NO_MORE_FILES;
197 if (err == GUAC_RDP_FS_ENOENT) return STATUS_NO_SUCH_FILE;
198 if (err == GUAC_RDP_FS_ENOTDIR) return STATUS_NOT_A_DIRECTORY;
199 if (err == GUAC_RDP_FS_ENOSPC) return STATUS_DISK_FULL;
200 if (err == GUAC_RDP_FS_EISDIR) return STATUS_FILE_IS_A_DIRECTORY;
201 if (err == GUAC_RDP_FS_EACCES) return STATUS_ACCESS_DENIED;
202 if (err == GUAC_RDP_FS_EEXIST) return STATUS_OBJECT_NAME_COLLISION;
203 if (err == GUAC_RDP_FS_EINVAL) return STATUS_INVALID_PARAMETER;
204 if (err == GUAC_RDP_FS_ENOSYS) return STATUS_NOT_IMPLEMENTED;
205 if (err == GUAC_RDP_FS_ENOTSUP) return STATUS_NOT_SUPPORTED;
206
207 /* Default to invalid parameter */
208 return STATUS_INVALID_PARAMETER;
209
210 }
211
guac_rdp_fs_open(guac_rdp_fs * fs,const char * path,int access,int file_attributes,int create_disposition,int create_options)212 int guac_rdp_fs_open(guac_rdp_fs* fs, const char* path,
213 int access, int file_attributes, int create_disposition,
214 int create_options) {
215
216 char real_path[GUAC_RDP_FS_MAX_PATH];
217 char normalized_path[GUAC_RDP_FS_MAX_PATH];
218
219 struct stat file_stat;
220 int fd;
221 int file_id;
222 guac_rdp_fs_file* file;
223
224 int flags = 0;
225
226 guac_client_log(fs->client, GUAC_LOG_DEBUG,
227 "%s: path=\"%s\", access=0x%x, file_attributes=0x%x, "
228 "create_disposition=0x%x, create_options=0x%x",
229 __func__, path, access, file_attributes,
230 create_disposition, create_options);
231
232 /* If no files available, return too many open */
233 if (fs->open_files >= GUAC_RDP_FS_MAX_FILES) {
234 guac_client_log(fs->client, GUAC_LOG_DEBUG,
235 "%s: Too many open files.",
236 __func__, path);
237 return GUAC_RDP_FS_ENFILE;
238 }
239
240 /* If path empty, transform to root path */
241 if (path[0] == '\0')
242 path = "\\";
243
244 /* If path is relative, the file does not exist */
245 else if (path[0] != '\\' && path[0] != '/') {
246 guac_client_log(fs->client, GUAC_LOG_DEBUG,
247 "%s: Access denied - supplied path \"%s\" is relative.",
248 __func__, path);
249 return GUAC_RDP_FS_ENOENT;
250 }
251
252 /* Translate access into flags */
253 if (access & GENERIC_ALL)
254 flags = O_RDWR;
255 else if ((access & ( GENERIC_WRITE
256 | FILE_WRITE_DATA
257 | FILE_APPEND_DATA))
258 && (access & (GENERIC_READ | FILE_READ_DATA)))
259 flags = O_RDWR;
260 else if (access & ( GENERIC_WRITE
261 | FILE_WRITE_DATA
262 | FILE_APPEND_DATA))
263 flags = O_WRONLY;
264 else
265 flags = O_RDONLY;
266
267 /* Normalize path, return no-such-file if invalid */
268 if (guac_rdp_fs_normalize_path(path, normalized_path)) {
269 guac_client_log(fs->client, GUAC_LOG_DEBUG,
270 "%s: Normalization of path \"%s\" failed.", __func__, path);
271 return GUAC_RDP_FS_ENOENT;
272 }
273
274 guac_client_log(fs->client, GUAC_LOG_DEBUG,
275 "%s: Normalized path \"%s\" to \"%s\".",
276 __func__, path, normalized_path);
277
278 /* Translate normalized path to real path */
279 __guac_rdp_fs_translate_path(fs, normalized_path, real_path);
280
281 guac_client_log(fs->client, GUAC_LOG_DEBUG,
282 "%s: Translated path \"%s\" to \"%s\".",
283 __func__, normalized_path, real_path);
284
285 switch (create_disposition) {
286
287 /* Create if not exist, fail otherwise */
288 case FILE_CREATE:
289 flags |= O_CREAT | O_EXCL;
290 break;
291
292 /* Open file if exists and do not overwrite, fail otherwise */
293 case FILE_OPEN:
294 /* No flag necessary - default functionality of open */
295 break;
296
297 /* Open if exists, create otherwise */
298 case FILE_OPEN_IF:
299 flags |= O_CREAT;
300 break;
301
302 /* Overwrite if exists, fail otherwise */
303 case FILE_OVERWRITE:
304 flags |= O_TRUNC;
305 break;
306
307 /* Overwrite if exists, create otherwise */
308 case FILE_OVERWRITE_IF:
309 flags |= O_CREAT | O_TRUNC;
310 break;
311
312 /* Supersede (replace) if exists, otherwise create */
313 case FILE_SUPERSEDE:
314 unlink(real_path);
315 flags |= O_CREAT | O_TRUNC;
316 break;
317
318 /* Unrecognised disposition */
319 default:
320 return GUAC_RDP_FS_ENOSYS;
321
322 }
323
324 /* Create directory first, if necessary */
325 if ((create_options & FILE_DIRECTORY_FILE) && (flags & O_CREAT)) {
326
327 /* Create directory */
328 if (mkdir(real_path, S_IRWXU)) {
329 if (errno != EEXIST || (flags & O_EXCL)) {
330 guac_client_log(fs->client, GUAC_LOG_DEBUG,
331 "%s: mkdir() failed: %s",
332 __func__, strerror(errno));
333 return guac_rdp_fs_get_errorcode(errno);
334 }
335 }
336
337 /* Unset O_CREAT and O_EXCL as directory must exist before open() */
338 flags &= ~(O_CREAT | O_EXCL);
339
340 }
341
342 guac_client_log(fs->client, GUAC_LOG_DEBUG,
343 "%s: native open: real_path=\"%s\", flags=0x%x",
344 __func__, real_path, flags);
345
346 /* Open file */
347 fd = open(real_path, flags, S_IRUSR | S_IWUSR);
348
349 /* If file open failed as we're trying to write a dir, retry as read-only */
350 if (fd == -1 && errno == EISDIR) {
351 flags &= ~(O_WRONLY | O_RDWR);
352 flags |= O_RDONLY;
353 fd = open(real_path, flags, S_IRUSR | S_IWUSR);
354 }
355
356 if (fd == -1) {
357 guac_client_log(fs->client, GUAC_LOG_DEBUG,
358 "%s: open() failed: %s", __func__, strerror(errno));
359 return guac_rdp_fs_get_errorcode(errno);
360 }
361
362 /* Get file ID, init file */
363 file_id = guac_pool_next_int(fs->file_id_pool);
364 file = &(fs->files[file_id]);
365 file->id = file_id;
366 file->fd = fd;
367 file->dir = NULL;
368 file->dir_pattern[0] = '\0';
369 file->absolute_path = strdup(normalized_path);
370 file->real_path = strdup(real_path);
371 file->bytes_written = 0;
372
373 guac_client_log(fs->client, GUAC_LOG_DEBUG,
374 "%s: Opened \"%s\" as file_id=%i",
375 __func__, normalized_path, file_id);
376
377 /* Attempt to pull file information */
378 if (fstat(fd, &file_stat) == 0) {
379
380 /* Load size and times */
381 file->size = file_stat.st_size;
382 file->ctime = WINDOWS_TIME(file_stat.st_ctime);
383 file->mtime = WINDOWS_TIME(file_stat.st_mtime);
384 file->atime = WINDOWS_TIME(file_stat.st_atime);
385
386 /* Set type */
387 if (S_ISDIR(file_stat.st_mode))
388 file->attributes = FILE_ATTRIBUTE_DIRECTORY;
389 else
390 file->attributes = FILE_ATTRIBUTE_NORMAL;
391
392 }
393
394 /* If information cannot be retrieved, fake it */
395 else {
396
397 /* Init information to 0, lacking any alternative */
398 file->size = 0;
399 file->ctime = 0;
400 file->mtime = 0;
401 file->atime = 0;
402 file->attributes = FILE_ATTRIBUTE_NORMAL;
403
404 }
405
406 fs->open_files++;
407
408 return file_id;
409
410 }
411
guac_rdp_fs_read(guac_rdp_fs * fs,int file_id,uint64_t offset,void * buffer,int length)412 int guac_rdp_fs_read(guac_rdp_fs* fs, int file_id, uint64_t offset,
413 void* buffer, int length) {
414
415 int bytes_read;
416
417 guac_rdp_fs_file* file = guac_rdp_fs_get_file(fs, file_id);
418 if (file == NULL) {
419 guac_client_log(fs->client, GUAC_LOG_DEBUG,
420 "%s: Read from bad file_id: %i", __func__, file_id);
421 return GUAC_RDP_FS_EINVAL;
422 }
423
424 /* Attempt read */
425 lseek(file->fd, offset, SEEK_SET);
426 bytes_read = read(file->fd, buffer, length);
427
428 /* Translate errno on error */
429 if (bytes_read < 0)
430 return guac_rdp_fs_get_errorcode(errno);
431
432 return bytes_read;
433
434 }
435
guac_rdp_fs_write(guac_rdp_fs * fs,int file_id,uint64_t offset,void * buffer,int length)436 int guac_rdp_fs_write(guac_rdp_fs* fs, int file_id, uint64_t offset,
437 void* buffer, int length) {
438
439 int bytes_written;
440
441 guac_rdp_fs_file* file = guac_rdp_fs_get_file(fs, file_id);
442 if (file == NULL) {
443 guac_client_log(fs->client, GUAC_LOG_DEBUG,
444 "%s: Write to bad file_id: %i", __func__, file_id);
445 return GUAC_RDP_FS_EINVAL;
446 }
447
448 /* Attempt write */
449 lseek(file->fd, offset, SEEK_SET);
450 bytes_written = write(file->fd, buffer, length);
451
452 /* Translate errno on error */
453 if (bytes_written < 0)
454 return guac_rdp_fs_get_errorcode(errno);
455
456 file->bytes_written += bytes_written;
457 return bytes_written;
458
459 }
460
guac_rdp_fs_rename(guac_rdp_fs * fs,int file_id,const char * new_path)461 int guac_rdp_fs_rename(guac_rdp_fs* fs, int file_id,
462 const char* new_path) {
463
464 char real_path[GUAC_RDP_FS_MAX_PATH];
465 char normalized_path[GUAC_RDP_FS_MAX_PATH];
466
467 guac_rdp_fs_file* file = guac_rdp_fs_get_file(fs, file_id);
468 if (file == NULL) {
469 guac_client_log(fs->client, GUAC_LOG_DEBUG,
470 "%s: Rename of bad file_id: %i", __func__, file_id);
471 return GUAC_RDP_FS_EINVAL;
472 }
473
474 /* Normalize path, return no-such-file if invalid */
475 if (guac_rdp_fs_normalize_path(new_path, normalized_path)) {
476 guac_client_log(fs->client, GUAC_LOG_DEBUG,
477 "%s: Normalization of path \"%s\" failed.",
478 __func__, new_path);
479 return GUAC_RDP_FS_ENOENT;
480 }
481
482 /* Translate normalized path to real path */
483 __guac_rdp_fs_translate_path(fs, normalized_path, real_path);
484
485 guac_client_log(fs->client, GUAC_LOG_DEBUG,
486 "%s: Renaming \"%s\" -> \"%s\"",
487 __func__, file->real_path, real_path);
488
489 /* Perform rename */
490 if (rename(file->real_path, real_path)) {
491 guac_client_log(fs->client, GUAC_LOG_DEBUG,
492 "%s: rename() failed: \"%s\" -> \"%s\"",
493 __func__, file->real_path, real_path);
494 return guac_rdp_fs_get_errorcode(errno);
495 }
496
497 return 0;
498
499 }
500
guac_rdp_fs_delete(guac_rdp_fs * fs,int file_id)501 int guac_rdp_fs_delete(guac_rdp_fs* fs, int file_id) {
502
503 /* Get file */
504 guac_rdp_fs_file* file = guac_rdp_fs_get_file(fs, file_id);
505 if (file == NULL) {
506 guac_client_log(fs->client, GUAC_LOG_DEBUG,
507 "%s: Delete of bad file_id: %i", __func__, file_id);
508 return GUAC_RDP_FS_EINVAL;
509 }
510
511 /* If directory, attempt removal */
512 if (file->attributes & FILE_ATTRIBUTE_DIRECTORY) {
513 if (rmdir(file->real_path)) {
514 guac_client_log(fs->client, GUAC_LOG_DEBUG,
515 "%s: rmdir() failed: \"%s\"", __func__, file->real_path);
516 return guac_rdp_fs_get_errorcode(errno);
517 }
518 }
519
520 /* Otherwise, attempt deletion */
521 else if (unlink(file->real_path)) {
522 guac_client_log(fs->client, GUAC_LOG_DEBUG,
523 "%s: unlink() failed: \"%s\"", __func__, file->real_path);
524 return guac_rdp_fs_get_errorcode(errno);
525 }
526
527 return 0;
528
529 }
530
guac_rdp_fs_truncate(guac_rdp_fs * fs,int file_id,int length)531 int guac_rdp_fs_truncate(guac_rdp_fs* fs, int file_id, int length) {
532
533 /* Get file */
534 guac_rdp_fs_file* file = guac_rdp_fs_get_file(fs, file_id);
535 if (file == NULL) {
536 guac_client_log(fs->client, GUAC_LOG_DEBUG,
537 "%s: Delete of bad file_id: %i", __func__, file_id);
538 return GUAC_RDP_FS_EINVAL;
539 }
540
541 /* Attempt truncate */
542 if (ftruncate(file->fd, length)) {
543 guac_client_log(fs->client, GUAC_LOG_DEBUG,
544 "%s: ftruncate() to %i bytes failed: \"%s\"",
545 __func__, length, file->real_path);
546 return guac_rdp_fs_get_errorcode(errno);
547 }
548
549 return 0;
550
551 }
552
guac_rdp_fs_close(guac_rdp_fs * fs,int file_id)553 void guac_rdp_fs_close(guac_rdp_fs* fs, int file_id) {
554
555 guac_rdp_fs_file* file = guac_rdp_fs_get_file(fs, file_id);
556 if (file == NULL) {
557 guac_client_log(fs->client, GUAC_LOG_DEBUG,
558 "%s: Ignoring close for bad file_id: %i",
559 __func__, file_id);
560 return;
561 }
562
563 file = &(fs->files[file_id]);
564
565 guac_client_log(fs->client, GUAC_LOG_DEBUG,
566 "%s: Closed \"%s\" (file_id=%i)",
567 __func__, file->absolute_path, file_id);
568
569 /* Close directory, if open */
570 if (file->dir != NULL)
571 closedir(file->dir);
572
573 /* Close file */
574 close(file->fd);
575
576 /* Free name */
577 free(file->absolute_path);
578 free(file->real_path);
579
580 /* Free ID back to pool */
581 guac_pool_free_int(fs->file_id_pool, file_id);
582 fs->open_files--;
583
584 }
585
guac_rdp_fs_read_dir(guac_rdp_fs * fs,int file_id)586 const char* guac_rdp_fs_read_dir(guac_rdp_fs* fs, int file_id) {
587
588 guac_rdp_fs_file* file;
589
590 struct dirent* result;
591
592 /* Only read if file ID is valid */
593 if (file_id < 0 || file_id >= GUAC_RDP_FS_MAX_FILES)
594 return NULL;
595
596 file = &(fs->files[file_id]);
597
598 /* Open directory if not yet open, stop if error */
599 if (file->dir == NULL) {
600 file->dir = fdopendir(file->fd);
601 if (file->dir == NULL)
602 return NULL;
603 }
604
605 /* Read next entry, stop if error or no more entries */
606 if ((result = readdir(file->dir)) == NULL)
607 return NULL;
608
609 /* Return filename */
610 return result->d_name;
611
612 }
613
guac_rdp_fs_basename(const char * path)614 const char* guac_rdp_fs_basename(const char* path) {
615
616 for (const char* c = path; *c != '\0'; c++) {
617
618 /* Reset beginning of path if a path separator is found */
619 if (*c == '/' || *c == '\\')
620 path = c + 1;
621
622 }
623
624 /* path now points to the first character after the last path separator */
625 return path;
626
627 }
628
guac_rdp_fs_normalize_path(const char * path,char * abs_path)629 int guac_rdp_fs_normalize_path(const char* path, char* abs_path) {
630
631 int path_depth = 0;
632 const char* path_components[GUAC_RDP_MAX_PATH_DEPTH];
633
634 /* If original path is not absolute, normalization fails */
635 if (path[0] != '\\' && path[0] != '/')
636 return 1;
637
638 /* Create scratch copy of path excluding leading slash (we will be
639 * replacing path separators with null terminators and referencing those
640 * substrings directly as path components) */
641 char path_scratch[GUAC_RDP_FS_MAX_PATH - 1];
642 int length = guac_strlcpy(path_scratch, path + 1,
643 sizeof(path_scratch));
644
645 /* Fail if provided path is too long */
646 if (length >= sizeof(path_scratch))
647 return 1;
648
649 /* Locate all path components within path */
650 const char* current_path_component = &(path_scratch[0]);
651 for (int i = 0; i <= length; i++) {
652
653 /* If current character is a path separator, parse as component */
654 char c = path_scratch[i];
655 if (c == '/' || c == '\\' || c == '\0') {
656
657 /* Terminate current component */
658 path_scratch[i] = '\0';
659
660 /* If component refers to parent, just move up in depth */
661 if (strcmp(current_path_component, "..") == 0) {
662 if (path_depth > 0)
663 path_depth--;
664 }
665
666 /* Otherwise, if component not current directory, add to list */
667 else if (strcmp(current_path_component, ".") != 0
668 && strcmp(current_path_component, "") != 0) {
669
670 /* Fail normalization if path is too deep */
671 if (path_depth >= GUAC_RDP_MAX_PATH_DEPTH)
672 return 1;
673
674 path_components[path_depth++] = current_path_component;
675
676 }
677
678 /* Update start of next component */
679 current_path_component = &(path_scratch[i+1]);
680
681 } /* end if separator */
682
683 /* We do not currently support named streams */
684 else if (c == ':')
685 return 1;
686
687 } /* end for each character */
688
689 /* Add leading slash for resulting absolute path */
690 abs_path[0] = '\\';
691
692 /* Append normalized components to path, separated by slashes */
693 guac_strljoin(abs_path + 1, path_components, path_depth,
694 "\\", GUAC_RDP_FS_MAX_PATH - 1);
695
696 return 0;
697
698 }
699
guac_rdp_fs_convert_path(const char * parent,const char * rel_path,char * abs_path)700 int guac_rdp_fs_convert_path(const char* parent, const char* rel_path, char* abs_path) {
701
702 int length;
703 char combined_path[GUAC_RDP_FS_MAX_PATH];
704
705 /* Copy parent path */
706 length = guac_strlcpy(combined_path, parent, sizeof(combined_path));
707
708 /* Add trailing slash */
709 length += guac_strlcpy(combined_path + length, "\\",
710 sizeof(combined_path) - length);
711
712 /* Copy remaining path */
713 length += guac_strlcpy(combined_path + length, rel_path,
714 sizeof(combined_path) - length);
715
716 /* Normalize into provided buffer */
717 return guac_rdp_fs_normalize_path(combined_path, abs_path);
718
719 }
720
guac_rdp_fs_get_file(guac_rdp_fs * fs,int file_id)721 guac_rdp_fs_file* guac_rdp_fs_get_file(guac_rdp_fs* fs, int file_id) {
722
723 /* Validate ID */
724 if (file_id < 0 || file_id >= GUAC_RDP_FS_MAX_FILES)
725 return NULL;
726
727 /* Return file at given ID */
728 return &(fs->files[file_id]);
729
730 }
731
guac_rdp_fs_matches(const char * filename,const char * pattern)732 int guac_rdp_fs_matches(const char* filename, const char* pattern) {
733 return fnmatch(pattern, filename, FNM_NOESCAPE) != 0;
734 }
735
guac_rdp_fs_get_info(guac_rdp_fs * fs,guac_rdp_fs_info * info)736 int guac_rdp_fs_get_info(guac_rdp_fs* fs, guac_rdp_fs_info* info) {
737
738 /* Read FS information */
739 struct statvfs fs_stat;
740 if (statvfs(fs->drive_path, &fs_stat))
741 return guac_rdp_fs_get_errorcode(errno);
742
743 /* Assign to structure */
744 info->blocks_available = fs_stat.f_bfree;
745 info->blocks_total = fs_stat.f_blocks;
746 info->block_size = fs_stat.f_bsize;
747 return 0;
748
749 }
750
guac_rdp_fs_append_filename(char * fullpath,const char * path,const char * filename)751 int guac_rdp_fs_append_filename(char* fullpath, const char* path,
752 const char* filename) {
753
754 int i;
755
756 /* Disallow "." as a filename */
757 if (strcmp(filename, ".") == 0)
758 return 0;
759
760 /* Disallow ".." as a filename */
761 if (strcmp(filename, "..") == 0)
762 return 0;
763
764 /* Copy path, append trailing slash */
765 for (i=0; i<GUAC_RDP_FS_MAX_PATH; i++) {
766
767 /*
768 * Append trailing slash only if:
769 * 1) Trailing slash is not already present
770 * 2) Path is non-empty
771 */
772
773 char c = path[i];
774 if (c == '\0') {
775 if (i > 0 && path[i-1] != '/' && path[i-1] != '\\')
776 fullpath[i++] = '/';
777 break;
778 }
779
780 /* Copy character if not end of string */
781 fullpath[i] = c;
782
783 }
784
785 /* Append filename */
786 for (; i<GUAC_RDP_FS_MAX_PATH; i++) {
787
788 char c = *(filename++);
789 if (c == '\0')
790 break;
791
792 /* Filenames may not contain slashes */
793 if (c == '\\' || c == '/')
794 return 0;
795
796 /* Append each character within filename */
797 fullpath[i] = c;
798
799 }
800
801 /* Verify path length is within maximum */
802 if (i == GUAC_RDP_FS_MAX_PATH)
803 return 0;
804
805 /* Terminate path string */
806 fullpath[i] = '\0';
807
808 /* Append was successful */
809 return 1;
810
811 }
812
813