Initial commit - from Precise source
[freerdp-ubuntu-pcb-backport.git] / channels / rdpdr / disk / disk_main.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 #include "config.h"
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <freerdp/utils/memory.h>
27 #include <freerdp/utils/stream.h>
28 #include <freerdp/utils/unicode.h>
29 #include <freerdp/utils/list.h>
30 #include <freerdp/utils/thread.h>
31 #include <freerdp/utils/svc_plugin.h>
32
33 #include "rdpdr_constants.h"
34 #include "rdpdr_types.h"
35 #include "disk_file.h"
36
37 typedef struct _DISK_DEVICE DISK_DEVICE;
38 struct _DISK_DEVICE
39 {
40         DEVICE device;
41
42         char* path;
43         LIST* files;
44
45         LIST* irp_list;
46         freerdp_thread* thread;
47 };
48
49
50 static uint32
51 disk_map_posix_err(int fs_errno)
52 {
53         uint32 rc;
54
55         /* try to return NTSTATUS version of error code */
56         switch (fs_errno)
57         {
58                 case EPERM:
59                 case EACCES:
60                         rc = STATUS_ACCESS_DENIED;
61                         break;
62                 case ENOENT:
63                         rc = STATUS_NO_SUCH_FILE;
64                         break;
65                 case EBUSY:
66                         rc = STATUS_DEVICE_BUSY;
67                         break;
68                 case EEXIST:
69                         rc  = STATUS_OBJECT_NAME_COLLISION;
70                         break;
71                 case EISDIR:
72                         rc = STATUS_FILE_IS_A_DIRECTORY;
73                         break;
74
75                 default:
76                         rc = STATUS_UNSUCCESSFUL;
77                         break;
78         }
79         DEBUG_SVC("errno 0x%x mapped to 0x%x\n", fs_errno, rc);
80         return rc;
81 }
82
83 static DISK_FILE* disk_get_file_by_id(DISK_DEVICE* disk, uint32 id)
84 {
85         LIST_ITEM* item;
86         DISK_FILE* file;
87
88         for (item = disk->files->head; item; item = item->next)
89         {
90                 file = (DISK_FILE*)item->data;
91                 if (file->id == id)
92                         return file;
93         }
94         return NULL;
95 }
96
97 static void disk_process_irp_create(DISK_DEVICE* disk, IRP* irp)
98 {
99         DISK_FILE* file;
100         uint32 DesiredAccess;
101         uint32 CreateDisposition;
102         uint32 CreateOptions;
103         uint32 PathLength;
104         UNICONV* uniconv;
105         char* path;
106         uint32 FileId;
107         uint8 Information;
108
109         stream_read_uint32(irp->input, DesiredAccess);
110         stream_seek(irp->input, 16); /* AllocationSize(8), FileAttributes(4), SharedAccess(4) */
111         stream_read_uint32(irp->input, CreateDisposition);
112         stream_read_uint32(irp->input, CreateOptions);
113         stream_read_uint32(irp->input, PathLength);
114
115         uniconv = freerdp_uniconv_new();
116         path = freerdp_uniconv_in(uniconv, stream_get_tail(irp->input), PathLength);
117         freerdp_uniconv_free(uniconv);
118
119         FileId = irp->devman->id_sequence++;
120         file = disk_file_new(disk->path, path, FileId,
121                 DesiredAccess, CreateDisposition, CreateOptions);
122
123         if (file == NULL)
124         {
125                 irp->IoStatus = STATUS_UNSUCCESSFUL;
126                 FileId = 0;
127                 Information = 0;
128
129                 DEBUG_WARN("failed to create %s.", path);
130         }
131         else if (file->err)
132         {
133                 FileId = 0;
134                 Information = 0;
135
136                 /* map errno to windows result*/
137                 irp->IoStatus = disk_map_posix_err(file->err);
138
139                 disk_file_free(file);
140         }
141         else
142         {
143                 list_enqueue(disk->files, file);
144
145                 switch (CreateDisposition)
146                 {
147                         case FILE_SUPERSEDE:
148                         case FILE_OPEN:
149                         case FILE_CREATE:
150                         case FILE_OVERWRITE:
151                                 Information = FILE_SUPERSEDED;
152                                 break;
153                         case FILE_OPEN_IF:
154                                 Information = FILE_OPENED;
155                                 break;
156                         case FILE_OVERWRITE_IF:
157                                 Information = FILE_OVERWRITTEN;
158                                 break;
159                         default:
160                                 Information = 0;
161                                 break;
162                 }
163                 DEBUG_SVC("%s(%d) created.", file->fullpath, file->id);
164         }
165
166         stream_write_uint32(irp->output, FileId);
167         stream_write_uint8(irp->output, Information);
168
169         xfree(path);
170
171         irp->Complete(irp);
172 }
173
174 static void disk_process_irp_close(DISK_DEVICE* disk, IRP* irp)
175 {
176         DISK_FILE* file;
177
178         file = disk_get_file_by_id(disk, irp->FileId);
179
180         if (file == NULL)
181         {
182                 irp->IoStatus = STATUS_UNSUCCESSFUL;
183
184                 DEBUG_WARN("FileId %d not valid.", irp->FileId);
185         }
186         else
187         {
188                 DEBUG_SVC("%s(%d) closed.", file->fullpath, file->id);
189
190                 list_remove(disk->files, file);
191                 disk_file_free(file);
192         }
193
194         stream_write_zero(irp->output, 5); /* Padding(5) */
195
196         irp->Complete(irp);
197 }
198
199 static void disk_process_irp_read(DISK_DEVICE* disk, IRP* irp)
200 {
201         DISK_FILE* file;
202         uint32 Length;
203         uint64 Offset;
204         uint8* buffer = NULL;
205
206         stream_read_uint32(irp->input, Length);
207         stream_read_uint64(irp->input, Offset);
208
209         file = disk_get_file_by_id(disk, irp->FileId);
210
211         if (file == NULL)
212         {
213                 irp->IoStatus = STATUS_UNSUCCESSFUL;
214                 Length = 0;
215
216                 DEBUG_WARN("FileId %d not valid.", irp->FileId);
217         }
218         else if (!disk_file_seek(file, Offset))
219         {
220                 irp->IoStatus = STATUS_UNSUCCESSFUL;
221                 Length = 0;
222
223                 DEBUG_WARN("seek %s(%d) failed.", file->fullpath, file->id);
224         }
225         else
226         {
227                 buffer = (uint8*)xmalloc(Length);
228                 if (!disk_file_read(file, buffer, &Length))
229                 {
230                         irp->IoStatus = STATUS_UNSUCCESSFUL;
231                         xfree(buffer);
232                         buffer = NULL;
233                         Length = 0;
234
235                         DEBUG_WARN("read %s(%d) failed.", file->fullpath, file->id);
236                 }
237                 else
238                 {
239                         DEBUG_SVC("read %llu-%llu from %s(%d).", Offset, Offset + Length, file->fullpath, file->id);
240                 }
241         }
242
243         stream_write_uint32(irp->output, Length);
244         if (Length > 0)
245         {
246                 stream_check_size(irp->output, Length);
247                 stream_write(irp->output, buffer, Length);
248         }
249         xfree(buffer);
250
251         irp->Complete(irp);
252 }
253
254 static void disk_process_irp_write(DISK_DEVICE* disk, IRP* irp)
255 {
256         DISK_FILE* file;
257         uint32 Length;
258         uint64 Offset;
259
260         stream_read_uint32(irp->input, Length);
261         stream_read_uint64(irp->input, Offset);
262         stream_seek(irp->input, 20); /* Padding */
263
264         file = disk_get_file_by_id(disk, irp->FileId);
265
266         if (file == NULL)
267         {
268                 irp->IoStatus = STATUS_UNSUCCESSFUL;
269                 Length = 0;
270
271                 DEBUG_WARN("FileId %d not valid.", irp->FileId);
272         }
273         else if (!disk_file_seek(file, Offset))
274         {
275                 irp->IoStatus = STATUS_UNSUCCESSFUL;
276                 Length = 0;
277
278                 DEBUG_WARN("seek %s(%d) failed.", file->fullpath, file->id);
279         }
280         else if (!disk_file_write(file, stream_get_tail(irp->input), Length))
281         {
282                 irp->IoStatus = STATUS_UNSUCCESSFUL;
283                 Length = 0;
284
285                 DEBUG_WARN("write %s(%d) failed.", file->fullpath, file->id);
286         }
287         else
288         {
289                 DEBUG_SVC("write %llu-%llu to %s(%d).", Offset, Offset + Length, file->fullpath, file->id);
290         }
291
292         stream_write_uint32(irp->output, Length);
293         stream_write_uint8(irp->output, 0); /* Padding */
294
295         irp->Complete(irp);
296 }
297
298 static void disk_process_irp_query_information(DISK_DEVICE* disk, IRP* irp)
299 {
300         DISK_FILE* file;
301         uint32 FsInformationClass;
302
303         stream_read_uint32(irp->input, FsInformationClass);
304
305         file = disk_get_file_by_id(disk, irp->FileId);
306
307         if (file == NULL)
308         {
309                 irp->IoStatus = STATUS_UNSUCCESSFUL;
310
311                 DEBUG_WARN("FileId %d not valid.", irp->FileId);
312         }
313         else if (!disk_file_query_information(file, FsInformationClass, irp->output))
314         {
315                 irp->IoStatus = STATUS_UNSUCCESSFUL;
316
317                 DEBUG_WARN("FsInformationClass %d on %s(%d) failed.", FsInformationClass, file->fullpath, file->id);
318         }
319         else
320         {
321                 DEBUG_SVC("FsInformationClass %d on %s(%d).", FsInformationClass, file->fullpath, file->id);
322         }
323
324         irp->Complete(irp);
325 }
326
327 static void disk_process_irp_set_information(DISK_DEVICE* disk, IRP* irp)
328 {
329         DISK_FILE* file;
330         uint32 FsInformationClass;
331         uint32 Length;
332
333         stream_read_uint32(irp->input, FsInformationClass);
334         stream_read_uint32(irp->input, Length);
335         stream_seek(irp->input, 24); /* Padding */
336
337         file = disk_get_file_by_id(disk, irp->FileId);
338
339         if (file == NULL)
340         {
341                 irp->IoStatus = STATUS_UNSUCCESSFUL;
342
343                 DEBUG_WARN("FileId %d not valid.", irp->FileId);
344         }
345         else if (!disk_file_set_information(file, FsInformationClass, Length, irp->input))
346         {
347                 irp->IoStatus = STATUS_UNSUCCESSFUL;
348
349                 DEBUG_WARN("FsInformationClass %d on %s(%d) failed.", FsInformationClass, file->fullpath, file->id);
350         }
351         else
352         {
353                 DEBUG_SVC("FsInformationClass %d on %s(%d) ok.", FsInformationClass, file->fullpath, file->id);
354         }
355
356         stream_write_uint32(irp->output, Length);
357
358         irp->Complete(irp);
359 }
360
361 static void disk_process_irp_query_volume_information(DISK_DEVICE* disk, IRP* irp)
362 {
363         uint32 FsInformationClass;
364         STREAM* output = irp->output;
365
366         stream_read_uint32(irp->input, FsInformationClass);
367
368         switch (FsInformationClass)
369         {
370                 case FileFsVolumeInformation:
371                         /* http://msdn.microsoft.com/en-us/library/cc232108.aspx */
372                         stream_write_uint32(output, 34); /* Length */
373                         stream_check_size(output, 34);
374                         stream_write_uint64(output, 0); /* VolumeCreationTime */
375                         stream_write_uint32(output, 0); /* VolumeSerialNumber */
376                         stream_write_uint32(output, 16); /* VolumeLabelLength */
377                         stream_write_uint8(output, 0); /* SupportsObjects */
378                         stream_write_uint8(output, 0); /* Reserved */
379                         stream_write(output, "F\0R\0E\0E\0R\0D\0P\0\0\0", 16); /* VolumeLabel (Unicode) */
380                         break;
381
382                 case FileFsSizeInformation:
383                         /* http://msdn.microsoft.com/en-us/library/cc232107.aspx */
384                         stream_write_uint32(output, 24); /* Length */
385                         stream_check_size(output, 24);
386                         stream_write_uint64(output, 0x1000000); /* TotalAllocationUnits */
387                         stream_write_uint64(output, 0x800000); /* AvailableAllocationUnits */
388                         stream_write_uint32(output, 1); /* SectorsPerAllocationUnit */
389                         stream_write_uint32(output, 0x400); /* BytesPerSector */
390                         break;
391
392                 case FileFsAttributeInformation:
393                         /* http://msdn.microsoft.com/en-us/library/cc232101.aspx */
394                         stream_write_uint32(output, 22); /* Length */
395                         stream_check_size(output, 22);
396                         stream_write_uint32(output,
397                                 FILE_CASE_SENSITIVE_SEARCH | 
398                                 FILE_CASE_PRESERVED_NAMES | 
399                                 FILE_UNICODE_ON_DISK); /* FileSystemAttributes */
400                         stream_write_uint32(output, 510); /* MaximumComponentNameLength */
401                         stream_write_uint32(output, 10); /* FileSystemNameLength */
402                         stream_write(output, "F\0A\0T\03\02\0", 10); /* FileSystemName */
403                         break;
404
405                 case FileFsFullSizeInformation:
406                         /* http://msdn.microsoft.com/en-us/library/cc232104.aspx */
407                         stream_write_uint32(output, 32); /* Length */
408                         stream_check_size(output, 32);
409                         stream_write_uint64(output, 0x1000000); /* TotalAllocationUnits */
410                         stream_write_uint64(output, 0x800000); /* CallerAvailableAllocationUnits */
411                         stream_write_uint64(output, 0x800000); /* ActualAvailableAllocationUnits */
412                         stream_write_uint32(output, 1); /* SectorsPerAllocationUnit */
413                         stream_write_uint32(output, 0x400); /* BytesPerSector */
414                         break;
415
416                 case FileFsDeviceInformation:
417                         /* http://msdn.microsoft.com/en-us/library/cc232109.aspx */
418                         stream_write_uint32(output, 8); /* Length */
419                         stream_check_size(output, 8);
420                         stream_write_uint32(output, FILE_DEVICE_DISK); /* DeviceType */
421                         stream_write_uint32(output, 0); /* Characteristics */
422                         break;
423
424                 default:
425                         irp->IoStatus = STATUS_UNSUCCESSFUL;
426                         stream_write_uint32(output, 0); /* Length */
427                         DEBUG_WARN("invalid FsInformationClass %d", FsInformationClass);
428                         break;
429         }
430
431         irp->Complete(irp);
432 }
433
434 static void disk_process_irp_query_directory(DISK_DEVICE* disk, IRP* irp)
435 {
436         DISK_FILE* file;
437         uint32 FsInformationClass;
438         uint8 InitialQuery;
439         uint32 PathLength;
440         UNICONV* uniconv;
441         char* path;
442
443         stream_read_uint32(irp->input, FsInformationClass);
444         stream_read_uint8(irp->input, InitialQuery);
445         stream_read_uint32(irp->input, PathLength);
446         stream_seek(irp->input, 23); /* Padding */
447
448         uniconv = freerdp_uniconv_new();
449         path = freerdp_uniconv_in(uniconv, stream_get_tail(irp->input), PathLength);
450         freerdp_uniconv_free(uniconv);
451
452         file = disk_get_file_by_id(disk, irp->FileId);
453
454         if (file == NULL)
455         {
456                 irp->IoStatus = STATUS_UNSUCCESSFUL;
457                 stream_write_uint32(irp->output, 0); /* Length */
458                 DEBUG_WARN("FileId %d not valid.", irp->FileId);
459         }
460         else if (!disk_file_query_directory(file, FsInformationClass, InitialQuery, path, irp->output))
461         {
462                 irp->IoStatus = STATUS_NO_MORE_FILES;
463         }
464
465         xfree(path);
466
467         irp->Complete(irp);
468 }
469
470 static void disk_process_irp_directory_control(DISK_DEVICE* disk, IRP* irp)
471 {
472         switch (irp->MinorFunction)
473         {
474                 case IRP_MN_QUERY_DIRECTORY:
475                         disk_process_irp_query_directory(disk, irp);
476                         break;
477
478                 case IRP_MN_NOTIFY_CHANGE_DIRECTORY: /* TODO */
479                         irp->Discard(irp);
480                         break;
481
482                 default:
483                         DEBUG_WARN("MinorFunction 0x%X not supported", irp->MinorFunction);
484                         irp->IoStatus = STATUS_NOT_SUPPORTED;
485                         stream_write_uint32(irp->output, 0); /* Length */
486                         irp->Complete(irp);
487                         break;
488         }
489 }
490
491 static void disk_process_irp_device_control(DISK_DEVICE* disk, IRP* irp)
492 {
493         stream_write_uint32(irp->output, 0); /* OutputBufferLength */
494         irp->Complete(irp);
495 }
496
497 static void disk_process_irp(DISK_DEVICE* disk, IRP* irp)
498 {
499         switch (irp->MajorFunction)
500         {
501                 case IRP_MJ_CREATE:
502                         disk_process_irp_create(disk, irp);
503                         break;
504
505                 case IRP_MJ_CLOSE:
506                         disk_process_irp_close(disk, irp);
507                         break;
508
509                 case IRP_MJ_READ:
510                         disk_process_irp_read(disk, irp);
511                         break;
512
513                 case IRP_MJ_WRITE:
514                         disk_process_irp_write(disk, irp);
515                         break;
516
517                 case IRP_MJ_QUERY_INFORMATION:
518                         disk_process_irp_query_information(disk, irp);
519                         break;
520
521                 case IRP_MJ_SET_INFORMATION:
522                         disk_process_irp_set_information(disk, irp);
523                         break;
524
525                 case IRP_MJ_QUERY_VOLUME_INFORMATION:
526                         disk_process_irp_query_volume_information(disk, irp);
527                         break;
528
529                 case IRP_MJ_DIRECTORY_CONTROL:
530                         disk_process_irp_directory_control(disk, irp);
531                         break;
532
533                 case IRP_MJ_DEVICE_CONTROL:
534                         disk_process_irp_device_control(disk, irp);
535                         break;
536
537                 default:
538                         DEBUG_WARN("MajorFunction 0x%X not supported", irp->MajorFunction);
539                         irp->IoStatus = STATUS_NOT_SUPPORTED;
540                         irp->Complete(irp);
541                         break;
542         }
543 }
544
545 static void disk_process_irp_list(DISK_DEVICE* disk)
546 {
547         IRP* irp;
548
549         while (1)
550         {
551                 if (freerdp_thread_is_stopped(disk->thread))
552                         break;
553
554                 freerdp_thread_lock(disk->thread);
555                 irp = (IRP*)list_dequeue(disk->irp_list);
556                 freerdp_thread_unlock(disk->thread);
557
558                 if (irp == NULL)
559                         break;
560
561                 disk_process_irp(disk, irp);
562         }
563 }
564
565 static void* disk_thread_func(void* arg)
566 {
567         DISK_DEVICE* disk = (DISK_DEVICE*)arg;
568
569         while (1)
570         {
571                 freerdp_thread_wait(disk->thread);
572
573                 if (freerdp_thread_is_stopped(disk->thread))
574                         break;
575
576                 freerdp_thread_reset(disk->thread);
577                 disk_process_irp_list(disk);
578         }
579
580         freerdp_thread_quit(disk->thread);
581
582         return NULL;
583 }
584
585 static void disk_irp_request(DEVICE* device, IRP* irp)
586 {
587         DISK_DEVICE* disk = (DISK_DEVICE*)device;
588
589         freerdp_thread_lock(disk->thread);
590         list_enqueue(disk->irp_list, irp);
591         freerdp_thread_unlock(disk->thread);
592
593         freerdp_thread_signal(disk->thread);
594 }
595
596 static void disk_free(DEVICE* device)
597 {
598         DISK_DEVICE* disk = (DISK_DEVICE*)device;
599         IRP* irp;
600         DISK_FILE* file;
601
602         freerdp_thread_stop(disk->thread);
603         freerdp_thread_free(disk->thread);
604         
605         while ((irp = (IRP*)list_dequeue(disk->irp_list)) != NULL)
606                 irp->Discard(irp);
607         list_free(disk->irp_list);
608
609         while ((file = (DISK_FILE*)list_dequeue(disk->files)) != NULL)
610                 disk_file_free(file);
611         list_free(disk->files);
612         xfree(disk);
613 }
614
615 int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
616 {
617         DISK_DEVICE* disk;
618         char* name;
619         char* path;
620         int i, len;
621
622         name = (char*)pEntryPoints->plugin_data->data[1];
623         path = (char*)pEntryPoints->plugin_data->data[2];
624
625         if (name[0] && path[0])
626         {
627                 disk = xnew(DISK_DEVICE);
628
629                 disk->device.type = RDPDR_DTYP_FILESYSTEM;
630                 disk->device.name = name;
631                 disk->device.IRPRequest = disk_irp_request;
632                 disk->device.Free = disk_free;
633
634                 len = strlen(name);
635                 disk->device.data = stream_new(len + 1);
636                 for (i = 0; i <= len; i++)
637                         stream_write_uint8(disk->device.data, name[i] < 0 ? '_' : name[i]);
638
639                 disk->path = path;
640                 disk->files = list_new();
641
642                 disk->irp_list = list_new();
643                 disk->thread = freerdp_thread_new();
644
645                 pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*)disk);
646
647                 freerdp_thread_start(disk->thread, disk_thread_func, disk);
648         }
649
650         return 0;
651 }