2 * FreeRDP: A Remote Desktop Protocol client.
3 * File System Virtual Channel
5 * Copyright 2010-2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6 * Copyright 2010-2011 Vic Lee
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
32 #include <freerdp/utils/memory.h>
33 #include <freerdp/utils/stream.h>
34 #include <freerdp/utils/svc_plugin.h>
43 #include "rdpdr_constants.h"
44 #include "rdpdr_types.h"
45 #include "disk_file.h"
47 #define FILE_TIME_SYSTEM_TO_RDP(_t) \
48 (((uint64)(_t) + 11644473600LL) * 10000000LL)
49 #define FILE_TIME_RDP_TO_SYSTEM(_t) \
50 (((_t) == 0LL || (_t) == (uint64)(-1LL)) ? 0 : (time_t)((_t) / 10000000LL - 11644473600LL))
52 #define FILE_ATTR_SYSTEM_TO_RDP(_f, _st) ( \
53 (S_ISDIR(_st.st_mode) ? FILE_ATTRIBUTE_DIRECTORY : 0) | \
54 (_f->filename[0] == '.' ? FILE_ATTRIBUTE_HIDDEN : 0) | \
55 (_f->delete_pending ? FILE_ATTRIBUTE_TEMPORARY : 0) | \
56 (st.st_mode & S_IWUSR ? 0 : FILE_ATTRIBUTE_READONLY))
59 static boolean disk_file_wildcard_match(const char* pattern, const char* filename)
61 const char *p = pattern, *f = filename;
65 * TODO: proper wildcard rules per msft's File System Behavior Overview
66 * Simple cases for now.
74 if (!c) /* shortcut */
76 /* TODO: skip to tail comparison */
88 static void disk_file_fix_path(char* path)
94 for (i = 0; i < len; i++)
99 if (len > 0 && path[len - 1] == '/')
100 path[len - 1] = '\0';
103 static char* disk_file_combine_fullpath(const char* base_path, const char* path)
107 fullpath = xmalloc(strlen(base_path) + strlen(path) + 1);
108 strcpy(fullpath, base_path);
109 strcat(fullpath, path);
110 disk_file_fix_path(fullpath);
115 static boolean disk_file_remove_dir(const char* path)
118 struct dirent* pdirent;
127 pdirent = readdir(dir);
130 if (strcmp(pdirent->d_name, ".") == 0 || strcmp(pdirent->d_name, "..") == 0)
132 pdirent = readdir(dir);
136 p = xmalloc(strlen(path) + strlen(pdirent->d_name) + 2);
137 sprintf(p, "%s/%s", path, pdirent->d_name);
138 if (stat(p, &st) != 0)
140 DEBUG_WARN("stat %s failed.", p);
143 else if (S_ISDIR(st.st_mode))
145 ret = disk_file_remove_dir(p);
147 else if (unlink(p) < 0)
149 DEBUG_WARN("unlink %s failed.", p);
159 pdirent = readdir(dir);
167 DEBUG_WARN("rmdir %s failed.", path);
175 static void disk_file_set_fullpath(DISK_FILE* file, char* fullpath)
177 xfree(file->fullpath);
178 file->fullpath = fullpath;
179 file->filename = strrchr(file->fullpath, '/');
180 if (file->filename == NULL)
181 file->filename = file->fullpath;
186 static boolean disk_file_init(DISK_FILE* file, uint32 DesiredAccess, uint32 CreateDisposition, uint32 CreateOptions)
188 const static int mode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH;
193 if (stat(file->fullpath, &st) == 0)
195 file->is_dir = (S_ISDIR(st.st_mode) ? true : false);
200 file->is_dir = ((CreateOptions & FILE_DIRECTORY_FILE) ? true : false);
203 if (mkdir(file->fullpath, mode) != 0)
213 file->dir = opendir(file->fullpath);
214 if (file->dir == NULL)
222 switch (CreateDisposition)
225 oflag = O_TRUNC | O_CREAT;
230 oflag = O_CREAT | O_EXCL;
238 case FILE_OVERWRITE_IF:
239 oflag = O_TRUNC | O_CREAT;
245 if (CreateOptions & FILE_DELETE_ON_CLOSE && DesiredAccess & DELETE)
247 file->delete_pending = true;
250 if ((DesiredAccess & GENERIC_ALL)
251 || (DesiredAccess & GENERIC_WRITE)
252 || (DesiredAccess & FILE_WRITE_DATA)
253 || (DesiredAccess & FILE_APPEND_DATA))
262 file->fd = open(file->fullpath, oflag, mode);
273 DISK_FILE* disk_file_new(const char* base_path, const char* path, uint32 id,
274 uint32 DesiredAccess, uint32 CreateDisposition, uint32 CreateOptions)
278 file = xnew(DISK_FILE);
280 file->basepath = (char*) base_path;
281 disk_file_set_fullpath(file, disk_file_combine_fullpath(base_path, path));
284 if (!disk_file_init(file, DesiredAccess, CreateDisposition, CreateOptions))
286 disk_file_free(file);
293 void disk_file_free(DISK_FILE* file)
297 if (file->dir != NULL)
300 if (file->delete_pending)
303 disk_file_remove_dir(file->fullpath);
305 unlink(file->fullpath);
308 xfree(file->pattern);
309 xfree(file->fullpath);
313 boolean disk_file_seek(DISK_FILE* file, uint64 Offset)
315 if (file->is_dir || file->fd == -1)
318 if (lseek(file->fd, Offset, SEEK_SET) == (off_t)-1)
324 boolean disk_file_read(DISK_FILE* file, uint8* buffer, uint32* Length)
328 if (file->is_dir || file->fd == -1)
331 r = read(file->fd, buffer, *Length);
339 boolean disk_file_write(DISK_FILE* file, uint8* buffer, uint32 Length)
343 if (file->is_dir || file->fd == -1)
348 r = write(file->fd, buffer, Length);
358 boolean disk_file_query_information(DISK_FILE* file, uint32 FsInformationClass, STREAM* output)
362 if (stat(file->fullpath, &st) != 0)
364 stream_write_uint32(output, 0); /* Length */
367 switch (FsInformationClass)
369 case FileBasicInformation:
370 /* http://msdn.microsoft.com/en-us/library/cc232094.aspx */
371 stream_write_uint32(output, 36); /* Length */
372 stream_check_size(output, 36);
373 stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* CreationTime */
374 stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_atime)); /* LastAccessTime */
375 stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* LastWriteTime */
376 stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_ctime)); /* ChangeTime */
377 stream_write_uint32(output, FILE_ATTR_SYSTEM_TO_RDP(file, st)); /* FileAttributes */
378 /* Reserved(4), MUST NOT be added! */
381 case FileStandardInformation:
382 /* http://msdn.microsoft.com/en-us/library/cc232088.aspx */
383 stream_write_uint32(output, 22); /* Length */
384 stream_check_size(output, 22);
385 stream_write_uint64(output, st.st_size); /* AllocationSize */
386 stream_write_uint64(output, st.st_size); /* EndOfFile */
387 stream_write_uint32(output, st.st_nlink); /* NumberOfLinks */
388 stream_write_uint8(output, file->delete_pending ? 1 : 0); /* DeletePending */
389 stream_write_uint8(output, file->is_dir ? 1 : 0); /* Directory */
390 /* Reserved(2), MUST NOT be added! */
393 case FileAttributeTagInformation:
394 /* http://msdn.microsoft.com/en-us/library/cc232093.aspx */
395 stream_write_uint32(output, 8); /* Length */
396 stream_check_size(output, 8);
397 stream_write_uint32(output, FILE_ATTR_SYSTEM_TO_RDP(file, st)); /* FileAttributes */
398 stream_write_uint32(output, 0); /* ReparseTag */
402 stream_write_uint32(output, 0); /* Length */
403 DEBUG_WARN("invalid FsInformationClass %d", FsInformationClass);
409 boolean disk_file_set_information(DISK_FILE* file, uint32 FsInformationClass, uint32 Length, STREAM* input)
417 struct timeval tv[2];
418 uint64 LastWriteTime;
419 uint32 FileAttributes;
420 uint32 FileNameLength;
422 switch (FsInformationClass)
424 case FileBasicInformation:
425 /* http://msdn.microsoft.com/en-us/library/cc232094.aspx */
426 stream_seek_uint64(input); /* CreationTime */
427 stream_seek_uint64(input); /* LastAccessTime */
428 stream_read_uint64(input, LastWriteTime);
429 stream_seek_uint64(input); /* ChangeTime */
430 stream_read_uint32(input, FileAttributes);
432 if (fstat(file->fd, &st) != 0)
435 tv[0].tv_sec = st.st_atime;
437 tv[1].tv_sec = (LastWriteTime > 0 ? FILE_TIME_RDP_TO_SYSTEM(LastWriteTime) : st.st_mtime);
439 futimes(file->fd, tv);
441 if (FileAttributes > 0)
444 if ((FileAttributes & FILE_ATTRIBUTE_READONLY) == 0)
449 fchmod(file->fd, st.st_mode);
453 case FileEndOfFileInformation:
454 /* http://msdn.microsoft.com/en-us/library/cc232067.aspx */
455 case FileAllocationInformation:
456 /* http://msdn.microsoft.com/en-us/library/cc232076.aspx */
457 stream_read_uint64(input, size);
458 if (ftruncate(file->fd, size) != 0)
462 case FileDispositionInformation:
463 /* http://msdn.microsoft.com/en-us/library/cc232098.aspx */
464 /* http://msdn.microsoft.com/en-us/library/cc241371.aspx */
466 stream_read_uint8(input, file->delete_pending);
468 file->delete_pending = 1;
471 case FileRenameInformation:
472 /* http://msdn.microsoft.com/en-us/library/cc232085.aspx */
473 stream_seek_uint8(input); /* ReplaceIfExists */
474 stream_seek_uint8(input); /* RootDirectory */
475 stream_read_uint32(input, FileNameLength);
476 uniconv = freerdp_uniconv_new();
477 s = freerdp_uniconv_in(uniconv, stream_get_tail(input), FileNameLength);
478 freerdp_uniconv_free(uniconv);
480 fullpath = disk_file_combine_fullpath(file->basepath, s);
483 if (rename(file->fullpath, fullpath) == 0)
485 DEBUG_SVC("renamed %s to %s", file->fullpath, fullpath);
486 disk_file_set_fullpath(file, fullpath);
490 DEBUG_WARN("rename %s to %s failed", file->fullpath, fullpath);
498 DEBUG_WARN("invalid FsInformationClass %d", FsInformationClass);
505 boolean disk_file_query_directory(DISK_FILE* file, uint32 FsInformationClass, uint8 InitialQuery,
506 const char* path, STREAM* output)
515 DEBUG_SVC("path %s FsInformationClass %d InitialQuery %d", path, FsInformationClass, InitialQuery);
519 stream_write_uint32(output, 0); /* Length */
520 stream_write_uint8(output, 0); /* Padding */
524 if (InitialQuery != 0)
526 rewinddir(file->dir);
527 xfree(file->pattern);
530 file->pattern = strdup(strrchr(path, '\\') + 1);
532 file->pattern = NULL;
539 ent = readdir(file->dir);
543 if (disk_file_wildcard_match(file->pattern, ent->d_name))
549 ent = readdir(file->dir);
554 DEBUG_SVC(" pattern %s not found.", file->pattern);
555 stream_write_uint32(output, 0); /* Length */
556 stream_write_uint8(output, 0); /* Padding */
560 memset(&st, 0, sizeof(struct stat));
561 ent_path = xmalloc(strlen(file->fullpath) + strlen(ent->d_name) + 2);
562 sprintf(ent_path, "%s/%s", file->fullpath, ent->d_name);
563 if (stat(ent_path, &st) != 0)
565 DEBUG_WARN("stat %s failed.", ent_path);
569 DEBUG_SVC(" pattern %s matched %s\n", file->pattern, ent_path);
571 uniconv = freerdp_uniconv_new();
572 ent_path = freerdp_uniconv_out(uniconv, ent->d_name, &len);
573 freerdp_uniconv_free(uniconv);
576 switch (FsInformationClass)
578 case FileDirectoryInformation:
579 /* http://msdn.microsoft.com/en-us/library/cc232097.aspx */
580 stream_write_uint32(output, 64 + len); /* Length */
581 stream_check_size(output, 64 + len);
582 stream_write_uint32(output, 0); /* NextEntryOffset */
583 stream_write_uint32(output, 0); /* FileIndex */
584 stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* CreationTime */
585 stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_atime)); /* LastAccessTime */
586 stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* LastWriteTime */
587 stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_ctime)); /* ChangeTime */
588 stream_write_uint64(output, st.st_size); /* EndOfFile */
589 stream_write_uint64(output, st.st_size); /* AllocationSize */
590 stream_write_uint32(output, FILE_ATTR_SYSTEM_TO_RDP(file, st)); /* FileAttributes */
591 stream_write_uint32(output, len); /* FileNameLength */
592 stream_write(output, ent_path, len);
595 case FileFullDirectoryInformation:
596 /* http://msdn.microsoft.com/en-us/library/cc232068.aspx */
597 stream_write_uint32(output, 68 + len); /* Length */
598 stream_check_size(output, 68 + len);
599 stream_write_uint32(output, 0); /* NextEntryOffset */
600 stream_write_uint32(output, 0); /* FileIndex */
601 stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* CreationTime */
602 stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_atime)); /* LastAccessTime */
603 stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* LastWriteTime */
604 stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_ctime)); /* ChangeTime */
605 stream_write_uint64(output, st.st_size); /* EndOfFile */
606 stream_write_uint64(output, st.st_size); /* AllocationSize */
607 stream_write_uint32(output, FILE_ATTR_SYSTEM_TO_RDP(file, st)); /* FileAttributes */
608 stream_write_uint32(output, len); /* FileNameLength */
609 stream_write_uint32(output, 0); /* EaSize */
610 stream_write(output, ent_path, len);
613 case FileBothDirectoryInformation:
614 /* http://msdn.microsoft.com/en-us/library/cc232095.aspx */
615 stream_write_uint32(output, 93 + len); /* Length */
616 stream_check_size(output, 93 + len);
617 stream_write_uint32(output, 0); /* NextEntryOffset */
618 stream_write_uint32(output, 0); /* FileIndex */
619 stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* CreationTime */
620 stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_atime)); /* LastAccessTime */
621 stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* LastWriteTime */
622 stream_write_uint64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_ctime)); /* ChangeTime */
623 stream_write_uint64(output, st.st_size); /* EndOfFile */
624 stream_write_uint64(output, st.st_size); /* AllocationSize */
625 stream_write_uint32(output, FILE_ATTR_SYSTEM_TO_RDP(file, st)); /* FileAttributes */
626 stream_write_uint32(output, len); /* FileNameLength */
627 stream_write_uint32(output, 0); /* EaSize */
628 stream_write_uint8(output, 0); /* ShortNameLength */
629 /* Reserved(1), MUST NOT be added! */
630 stream_write_zero(output, 24); /* ShortName */
631 stream_write(output, ent_path, len);
634 case FileNamesInformation:
635 /* http://msdn.microsoft.com/en-us/library/cc232077.aspx */
636 stream_write_uint32(output, 12 + len); /* Length */
637 stream_check_size(output, 12 + len);
638 stream_write_uint32(output, 0); /* NextEntryOffset */
639 stream_write_uint32(output, 0); /* FileIndex */
640 stream_write_uint32(output, len); /* FileNameLength */
641 stream_write(output, ent_path, len);
645 stream_write_uint32(output, 0); /* Length */
646 stream_write_uint8(output, 0); /* Padding */
647 DEBUG_WARN("invalid FsInformationClass %d", FsInformationClass);