Initial commit - from Precise source
[freerdp-ubuntu-pcb-backport.git] / channels / rdpdr / disk / disk_file.c
1 /**
2  * FreeRDP: A Remote Desktop Protocol client.
3  * File System Virtual Channel
4  *
5  * Copyright 2010-2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6  * Copyright 2010-2011 Vic Lee
7  *
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
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
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.
19  */
20
21 #ifndef _WIN32
22 #include <sys/time.h>
23 #endif
24
25 #include "config.h"
26 #include <errno.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <time.h>
31 #include <sys/stat.h>
32 #include <freerdp/utils/memory.h>
33 #include <freerdp/utils/stream.h>
34 #include <freerdp/utils/svc_plugin.h>
35
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39 #ifdef HAVE_FCNTL_H
40 #include <fcntl.h>
41 #endif
42
43 #include "rdpdr_constants.h"
44 #include "rdpdr_types.h"
45 #include "disk_file.h"
46
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))
51
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))
57
58
59 static boolean disk_file_wildcard_match(const char* pattern, const char* filename)
60 {
61         const char *p = pattern, *f = filename;
62         char c;
63
64         /*
65          * TODO: proper wildcard rules per msft's File System Behavior Overview
66          * Simple cases for now.
67          */
68         f = filename;
69         while ((c = *p++))
70         {
71                 if (c == '*')
72                 {
73                         c = *p++;
74                         if (!c) /* shortcut */
75                                 return true;
76                         /* TODO: skip to tail comparison */
77                 }
78                 if (c != *f++)
79                         return false;
80         }
81
82         if (!*f)
83                 return true;
84
85         return false;
86 }
87
88 static void disk_file_fix_path(char* path)
89 {
90         int len;
91         int i;
92
93         len = strlen(path);
94         for (i = 0; i < len; i++)
95         {
96                 if (path[i] == '\\')
97                         path[i] = '/';
98         }
99         if (len > 0 && path[len - 1] == '/')
100                 path[len - 1] = '\0';
101 }
102
103 static char* disk_file_combine_fullpath(const char* base_path, const char* path)
104 {
105         char* fullpath;
106
107         fullpath = xmalloc(strlen(base_path) + strlen(path) + 1);
108         strcpy(fullpath, base_path);
109         strcat(fullpath, path);
110         disk_file_fix_path(fullpath);
111
112         return fullpath;
113 }
114
115 static boolean disk_file_remove_dir(const char* path)
116 {
117         DIR* dir;
118         struct dirent* pdirent;
119         struct stat st;
120         char* p;
121         boolean ret = true;
122
123         dir = opendir(path);
124         if (dir == NULL)
125                 return false;
126
127         pdirent = readdir(dir);
128         while (pdirent)
129         {
130                 if (strcmp(pdirent->d_name, ".") == 0 || strcmp(pdirent->d_name, "..") == 0)
131                 {
132                         pdirent = readdir(dir);
133                         continue;
134                 }
135
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)
139                 {
140                         DEBUG_WARN("stat %s failed.", p);
141                         ret = false;
142                 }
143                 else if (S_ISDIR(st.st_mode))
144                 {
145                         ret = disk_file_remove_dir(p);
146                 }
147                 else if (unlink(p) < 0)
148                 {
149                         DEBUG_WARN("unlink %s failed.", p);
150                         ret = false;
151                 }
152                 else
153                         ret = true;
154                 xfree(p);
155
156                 if (!ret)
157                         break;
158
159                 pdirent = readdir(dir);
160         }
161
162         closedir(dir);
163         if (ret)
164         {
165                 if (rmdir(path) < 0)
166                 {
167                         DEBUG_WARN("rmdir %s failed.", path);
168                         ret = false;
169                 }
170         }
171
172         return ret;
173 }
174
175 static void disk_file_set_fullpath(DISK_FILE* file, char* fullpath)
176 {
177         xfree(file->fullpath);
178         file->fullpath = fullpath;
179         file->filename = strrchr(file->fullpath, '/');
180         if (file->filename == NULL)
181                 file->filename = file->fullpath;
182         else
183                 file->filename += 1;
184 }
185
186 static boolean disk_file_init(DISK_FILE* file, uint32 DesiredAccess, uint32 CreateDisposition, uint32 CreateOptions)
187 {
188         const static int mode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH;
189         struct stat st;
190         boolean exists;
191         int oflag = 0;
192
193         if (stat(file->fullpath, &st) == 0)
194         {
195                 file->is_dir = (S_ISDIR(st.st_mode) ? true : false);
196                 exists = true;
197         }
198         else
199         {
200                 file->is_dir = ((CreateOptions & FILE_DIRECTORY_FILE) ? true : false);
201                 if (file->is_dir)
202                 {
203                         if (mkdir(file->fullpath, mode) != 0)
204                         {
205                                 file->err = errno;
206                                 return true;
207                         }
208                 }
209                 exists = false;
210         }
211         if (file->is_dir)
212         {
213                 file->dir = opendir(file->fullpath);
214                 if (file->dir == NULL)
215                 {
216                         file->err = errno;
217                         return true;
218                 }
219         }
220         else
221         {
222                 switch (CreateDisposition)
223                 {
224                         case FILE_SUPERSEDE:
225                                 oflag = O_TRUNC | O_CREAT;
226                                 break;
227                         case FILE_OPEN:
228                                 break;
229                         case FILE_CREATE:
230                                 oflag = O_CREAT | O_EXCL;
231                                 break;
232                         case FILE_OPEN_IF:
233                                 oflag = O_CREAT;
234                                 break;
235                         case FILE_OVERWRITE:
236                                 oflag = O_TRUNC;
237                                 break;
238                         case FILE_OVERWRITE_IF:
239                                 oflag = O_TRUNC | O_CREAT;
240                                 break;
241                         default:
242                                 break;
243                 }
244
245                 if (CreateOptions & FILE_DELETE_ON_CLOSE && DesiredAccess & DELETE)
246                 {
247                         file->delete_pending = true;
248                 }
249
250                 if ((DesiredAccess & GENERIC_ALL)
251                         || (DesiredAccess & GENERIC_WRITE)
252                         || (DesiredAccess & FILE_WRITE_DATA)
253                         || (DesiredAccess & FILE_APPEND_DATA))
254                 {
255                         oflag |= O_RDWR;
256                 }
257                 else
258                 {
259                         oflag |= O_RDONLY;
260                 }
261
262                 file->fd = open(file->fullpath, oflag, mode);
263                 if (file->fd == -1)
264                 {
265                         file->err = errno;
266                         return true;
267                 }
268         }
269
270         return true;
271 }
272
273 DISK_FILE* disk_file_new(const char* base_path, const char* path, uint32 id,
274         uint32 DesiredAccess, uint32 CreateDisposition, uint32 CreateOptions)
275 {
276         DISK_FILE* file;
277
278         file = xnew(DISK_FILE);
279         file->id = id;
280         file->basepath = (char*) base_path;
281         disk_file_set_fullpath(file, disk_file_combine_fullpath(base_path, path));
282         file->fd = -1;
283
284         if (!disk_file_init(file, DesiredAccess, CreateDisposition, CreateOptions))
285         {
286                 disk_file_free(file);
287                 return NULL;
288         }
289
290         return file;
291 }
292
293 void disk_file_free(DISK_FILE* file)
294 {
295         if (file->fd != -1)
296                 close(file->fd);
297         if (file->dir != NULL)
298                 closedir(file->dir);
299
300         if (file->delete_pending)
301         {
302                 if (file->is_dir)
303                         disk_file_remove_dir(file->fullpath);
304                 else
305                         unlink(file->fullpath);
306         }
307
308         xfree(file->pattern);
309         xfree(file->fullpath);
310         xfree(file);
311 }
312
313 boolean disk_file_seek(DISK_FILE* file, uint64 Offset)
314 {
315         if (file->is_dir || file->fd == -1)
316                 return false;
317
318         if (lseek(file->fd, Offset, SEEK_SET) == (off_t)-1)
319                 return false;
320
321         return true;
322 }
323
324 boolean disk_file_read(DISK_FILE* file, uint8* buffer, uint32* Length)
325 {
326         ssize_t r;
327
328         if (file->is_dir || file->fd == -1)
329                 return false;
330
331         r = read(file->fd, buffer, *Length);
332         if (r < 0)
333                 return false;
334         *Length = (uint32)r;
335
336         return true;
337 }
338
339 boolean disk_file_write(DISK_FILE* file, uint8* buffer, uint32 Length)
340 {
341         ssize_t r;
342
343         if (file->is_dir || file->fd == -1)
344                 return false;
345
346         while (Length > 0)
347         {
348                 r = write(file->fd, buffer, Length);
349                 if (r == -1)
350                         return false;
351                 Length -= r;
352                 buffer += r;
353         }
354
355         return true;
356 }
357
358 boolean disk_file_query_information(DISK_FILE* file, uint32 FsInformationClass, STREAM* output)
359 {
360         struct stat st;
361
362         if (stat(file->fullpath, &st) != 0)
363         {
364                 stream_write_uint32(output, 0); /* Length */
365                 return false;
366         }
367         switch (FsInformationClass)
368         {
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! */
379                         break;
380
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! */
391                         break;
392
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 */
399                         break;
400
401                 default:
402                         stream_write_uint32(output, 0); /* Length */
403                         DEBUG_WARN("invalid FsInformationClass %d", FsInformationClass);
404                         return false;
405         }
406         return true;
407 }
408
409 boolean disk_file_set_information(DISK_FILE* file, uint32 FsInformationClass, uint32 Length, STREAM* input)
410 {
411         char* s;
412         mode_t m;
413         uint64 size;
414         char* fullpath;
415         struct stat st;
416         UNICONV* uniconv;
417         struct timeval tv[2];
418         uint64 LastWriteTime;
419         uint32 FileAttributes;
420         uint32 FileNameLength;
421
422         switch (FsInformationClass)
423         {
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);
431
432                         if (fstat(file->fd, &st) != 0)
433                                 return false;
434
435                         tv[0].tv_sec = st.st_atime;
436                         tv[0].tv_usec = 0;
437                         tv[1].tv_sec = (LastWriteTime > 0 ? FILE_TIME_RDP_TO_SYSTEM(LastWriteTime) : st.st_mtime);
438                         tv[1].tv_usec = 0;
439                         futimes(file->fd, tv);
440
441                         if (FileAttributes > 0)
442                         {
443                                 m = st.st_mode;
444                                 if ((FileAttributes & FILE_ATTRIBUTE_READONLY) == 0)
445                                         m |= S_IWUSR;
446                                 else
447                                         m &= ~S_IWUSR;
448                                 if (m != st.st_mode)
449                                         fchmod(file->fd, st.st_mode);
450                         }
451                         break;
452
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)
459                                 return false;
460                         break;
461
462                 case FileDispositionInformation:
463                         /* http://msdn.microsoft.com/en-us/library/cc232098.aspx */
464                         /* http://msdn.microsoft.com/en-us/library/cc241371.aspx */
465                         if (Length)
466                                 stream_read_uint8(input, file->delete_pending);
467                         else
468                                 file->delete_pending = 1;
469                         break;
470
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);
479
480                         fullpath = disk_file_combine_fullpath(file->basepath, s);
481                         xfree(s);
482
483                         if (rename(file->fullpath, fullpath) == 0)
484                         {
485                                 DEBUG_SVC("renamed %s to %s", file->fullpath, fullpath);
486                                 disk_file_set_fullpath(file, fullpath);
487                         }
488                         else
489                         {
490                                 DEBUG_WARN("rename %s to %s failed", file->fullpath, fullpath);
491                                 free(fullpath);
492                                 return false;
493                         }
494
495                         break;
496
497                 default:
498                         DEBUG_WARN("invalid FsInformationClass %d", FsInformationClass);
499                         return false;
500         }
501
502         return true;
503 }
504
505 boolean disk_file_query_directory(DISK_FILE* file, uint32 FsInformationClass, uint8 InitialQuery,
506         const char* path, STREAM* output)
507 {
508         struct dirent* ent;
509         char* ent_path;
510         struct stat st;
511         UNICONV* uniconv;
512         size_t len;
513         boolean ret;
514
515         DEBUG_SVC("path %s FsInformationClass %d InitialQuery %d", path, FsInformationClass, InitialQuery);
516
517         if (!file->dir)
518         {
519                 stream_write_uint32(output, 0); /* Length */
520                 stream_write_uint8(output, 0); /* Padding */
521                 return false;
522         }
523
524         if (InitialQuery != 0)
525         {
526                 rewinddir(file->dir);
527                 xfree(file->pattern);
528
529                 if (path[0])
530                         file->pattern = strdup(strrchr(path, '\\') + 1);
531                 else
532                         file->pattern = NULL;
533         }
534
535         if (file->pattern)
536         {
537                 do
538                 {
539                         ent = readdir(file->dir);
540                         if (ent == NULL)
541                                 continue;
542
543                         if (disk_file_wildcard_match(file->pattern, ent->d_name))
544                                 break;
545                 } while (ent);
546         }
547         else
548         {
549                 ent = readdir(file->dir);
550         }
551
552         if (ent == NULL)
553         {
554                 DEBUG_SVC("  pattern %s not found.", file->pattern);
555                 stream_write_uint32(output, 0); /* Length */
556                 stream_write_uint8(output, 0); /* Padding */
557                 return false;
558         }
559
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)
564         {
565                 DEBUG_WARN("stat %s failed.", ent_path);
566         }
567         xfree(ent_path);
568
569         DEBUG_SVC("  pattern %s matched %s\n", file->pattern, ent_path);
570
571         uniconv = freerdp_uniconv_new();
572         ent_path = freerdp_uniconv_out(uniconv, ent->d_name, &len);
573         freerdp_uniconv_free(uniconv);
574
575         ret = true;
576         switch (FsInformationClass)
577         {
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);
593                         break;
594
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);
611                         break;
612
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);
632                         break;
633
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);
642                         break;
643
644                 default:
645                         stream_write_uint32(output, 0); /* Length */
646                         stream_write_uint8(output, 0); /* Padding */
647                         DEBUG_WARN("invalid FsInformationClass %d", FsInformationClass);
648                         ret = false;
649                         break;
650         }
651
652         xfree(ent_path);
653
654         return ret;
655 }