Whamcloud - gitweb
8641380115f39b00ca30f90fb5f49c91a2ccd010
[fs/lustre-release.git] / libcfs / libcfs / winnt / winnt-fs.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
19  *
20  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
21  * CA 95054 USA or visit www.sun.com if you need additional information or
22  * have any questions.
23  *
24  * GPL HEADER END
25  */
26 /*
27  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
28  * Use is subject to license terms.
29  */
30 /*
31  * This file is part of Lustre, http://www.lustre.org/
32  * Lustre is a trademark of Sun Microsystems, Inc.
33  */
34
35 # define DEBUG_SUBSYSTEM S_LNET
36
37 #include <libcfs/libcfs.h>
38
39 const CHAR *dos_file_prefix[] = {
40             "\\??\\", "\\DosDevices\\",
41             "\\SystemRoot\\", NULL};
42
43 /*
44  * cfs_filp_open
45  *     To open or create a file in kernel mode
46  *
47  * Arguments:
48  *   name:  name of the file to be opened or created, no dos path prefix
49  *   flags: open/creation attribute options
50  *   mode:  access mode/permission to open or create
51  *   err:   error code
52  *
53  * Return Value:
54  *   the pointer to the cfs_file_t or NULL if it fails
55  *
56  * Notes: 
57  *   N/A
58  */
59
60 #define is_drv_letter_valid(x) (((x) >= 0 && (x) <= 9) || \
61                 ( ((x)|0x20) <= 'z' && ((x)|0x20) >= 'a'))
62
63 cfs_file_t *cfs_filp_open(const char *name, int flags, int mode, int *err)
64 {
65     cfs_file_t *        fp = NULL;
66
67     NTSTATUS            Status;
68
69     OBJECT_ATTRIBUTES   ObjectAttributes;
70     HANDLE              FileHandle;
71     IO_STATUS_BLOCK     IoStatus;
72     ACCESS_MASK         DesiredAccess;
73     ULONG               CreateDisposition;
74     ULONG               ShareAccess;
75     ULONG               CreateOptions;
76
77     USHORT              NameLength = 0;
78     USHORT              PrefixLength = 0;
79
80     UNICODE_STRING      UnicodeName;
81     PWCHAR              UnicodeString = NULL;
82
83     ANSI_STRING         AnsiName;
84     PUCHAR              AnsiString = NULL;
85
86     /* Analyze the flags settings */
87     if (cfs_is_flag_set(flags, O_WRONLY)) {
88         DesiredAccess = (GENERIC_WRITE | SYNCHRONIZE);
89         ShareAccess = 0;
90     }  else if (cfs_is_flag_set(flags, O_RDWR)) {
91         DesiredAccess = (GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE);
92         ShareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE;
93     } else {
94         DesiredAccess = (GENERIC_READ | SYNCHRONIZE);
95         ShareAccess = FILE_SHARE_READ;
96     }
97
98     if (cfs_is_flag_set(flags, O_CREAT)) {
99         if (cfs_is_flag_set(flags, O_EXCL)) {
100             CreateDisposition = FILE_CREATE;
101         } else {
102             CreateDisposition = FILE_OPEN_IF;
103         }
104     } else {
105         CreateDisposition = FILE_OPEN;
106     }
107
108     if (cfs_is_flag_set(flags, O_TRUNC)) {
109         if (cfs_is_flag_set(flags, O_EXCL)) {
110             CreateDisposition = FILE_OVERWRITE;
111         } else {
112             CreateDisposition = FILE_OVERWRITE_IF;
113         }
114     }
115
116     CreateOptions = 0;
117
118     if (cfs_is_flag_set(flags, O_DIRECTORY)) {
119         cfs_set_flag(CreateOptions,  FILE_DIRECTORY_FILE);
120     }
121
122     if (cfs_is_flag_set(flags, O_SYNC)) {
123          cfs_set_flag(CreateOptions, FILE_WRITE_THROUGH);
124     }
125
126     if (cfs_is_flag_set(flags, O_DIRECT)) {
127          cfs_set_flag(CreateOptions, FILE_NO_INTERMEDIATE_BUFFERING);
128     }
129
130     /* Initialize the unicode path name for the specified file */
131     NameLength = (USHORT)strlen(name);
132
133     /* Check file & path name */
134     if (name[0] != '\\') {
135         if (NameLength < 1 || name[1] != ':' || !is_drv_letter_valid(name[0])) {
136             /* invalid file path name */
137             if (err) *err = -EINVAL;
138             return NULL;
139         }
140         PrefixLength = (USHORT)strlen(dos_file_prefix[0]);
141     } else {
142         int i, j;
143         for (i=0; i < 3 && dos_file_prefix[i] != NULL; i++) {
144             j = strlen(dos_file_prefix[i]);
145             if (NameLength > j && _strnicmp(dos_file_prefix[i], name, j) == 0) {
146                 break;
147             }
148         }
149         if (i >= 3) {
150             if (err) *err = -EINVAL;
151             return NULL;
152         }
153     }
154
155     AnsiString = cfs_alloc( sizeof(CHAR) * (NameLength + PrefixLength + 1),
156                             CFS_ALLOC_ZERO);
157     if (NULL == AnsiString) {
158         if (err) *err = -ENOMEM;
159         return NULL;
160     }
161
162     UnicodeString = cfs_alloc( sizeof(WCHAR) * (NameLength + PrefixLength + 1),
163                                CFS_ALLOC_ZERO);
164     if (NULL == UnicodeString) {
165         if (err) *err = -ENOMEM;
166         cfs_free(AnsiString);
167         return NULL;
168     }
169
170     if (PrefixLength) {
171         RtlCopyMemory(&AnsiString[0], dos_file_prefix[0], PrefixLength);
172     }
173
174     RtlCopyMemory(&AnsiString[PrefixLength], name, NameLength);
175     NameLength += PrefixLength;
176
177     AnsiName.MaximumLength = NameLength + 1;
178     AnsiName.Length = NameLength;
179     AnsiName.Buffer = AnsiString;
180
181     UnicodeName.MaximumLength = (NameLength + 1) * sizeof(WCHAR);
182     UnicodeName.Length = 0;
183     UnicodeName.Buffer = (PWSTR)UnicodeString;
184
185     RtlAnsiStringToUnicodeString(&UnicodeName, &AnsiName, FALSE);
186
187     /* Setup the object attributes structure for the file. */
188     InitializeObjectAttributes(
189             &ObjectAttributes,
190             &UnicodeName,
191             OBJ_CASE_INSENSITIVE |
192             OBJ_KERNEL_HANDLE,
193             NULL,
194             NULL );
195
196     /* Now to open or create the file now */
197     Status = ZwCreateFile(
198             &FileHandle,
199             DesiredAccess,
200             &ObjectAttributes,
201             &IoStatus,
202             0,
203             FILE_ATTRIBUTE_NORMAL,
204             ShareAccess,
205             CreateDisposition,
206             CreateOptions,
207             NULL,
208             0 );
209
210     /* Check the returned status of IoStatus... */
211     if (!NT_SUCCESS(IoStatus.Status)) {
212         if (err) {
213             *err = cfs_error_code(IoStatus.Status);
214         }
215         cfs_free(UnicodeString);
216         cfs_free(AnsiString);
217         return NULL;
218     }
219
220     /* Allocate the cfs_file_t: libcfs file object */
221     fp = cfs_alloc(sizeof(cfs_file_t) + NameLength, CFS_ALLOC_ZERO);
222
223     if (NULL == fp) {
224         Status = ZwClose(FileHandle);
225         ASSERT(NT_SUCCESS(Status));
226         if (err) {
227             *err = -ENOMEM;
228         }
229         cfs_free(UnicodeString);
230         cfs_free(AnsiString);
231         return NULL;
232     }
233
234     fp->f_handle = FileHandle;
235     strcpy(fp->f_name, name);
236     fp->f_flags = flags;
237     fp->f_mode  = (mode_t)mode;
238     fp->f_count = 1;
239     if (err) {
240         *err = 0;
241     }
242
243     /* free the memory of temporary name strings */
244     cfs_free(UnicodeString);
245     cfs_free(AnsiString);
246
247     return fp;
248 }
249
250
251 /*
252  * cfs_filp_close
253  *     To close the opened file and release the filp structure
254  *
255  * Arguments:
256  *   fp:   the pointer of the cfs_file_t strcture
257  *
258  * Return Value:
259  *   ZERO: on success
260  *   Non-Zero: on failure
261  *
262  * Notes: 
263  *   N/A
264  */
265
266 int cfs_filp_close(cfs_file_t *fp)
267 {
268     NTSTATUS    Status;
269
270     ASSERT(fp != NULL);
271     ASSERT(fp->f_handle != NULL);
272
273     /* release the file handle */
274     Status = ZwClose(fp->f_handle);
275     ASSERT(NT_SUCCESS(Status));
276
277     /* free the file flip structure */
278     cfs_free(fp);
279     return 0;
280 }
281
282
283 NTSTATUS CompletionRoutine(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context)
284 {
285     /* copy the IoStatus result */
286     if (Irp->UserIosb)
287         *Irp->UserIosb = Irp->IoStatus;
288     
289     /* singal the event we set */
290     KeSetEvent((PKEVENT) Context, 0, FALSE);
291    
292     /* free the Irp we allocated */
293     IoFreeIrp(Irp);
294     
295     return STATUS_MORE_PROCESSING_REQUIRED;
296 }
297
298
299 NTSTATUS cfs_nt_filp_io(HANDLE Handle, BOOLEAN Writing, PLARGE_INTEGER Offset,
300                         ULONG Length,  PUCHAR Buffer,   PULONG Bytes)
301 {
302     NTSTATUS                status;
303     IO_STATUS_BLOCK         iosb;
304
305     PIRP                    irp = NULL;
306     PIO_STACK_LOCATION      irpSp = NULL;
307
308     PFILE_OBJECT            fileObject = NULL;
309     PDEVICE_OBJECT          deviceObject;
310
311     KEVENT                  event;
312
313     KeInitializeEvent(&event, SynchronizationEvent, FALSE);
314
315     status = ObReferenceObjectByHandle( Handle,
316                                         Writing ? FILE_WRITE_DATA : 
317                                                   FILE_READ_DATA,
318                                         *IoFileObjectType,
319                                         KernelMode,
320                                         (PVOID *) &fileObject,
321                                         NULL );
322     if (!NT_SUCCESS(status)) {
323         goto errorout;
324     }
325
326     /* query the DeviceObject in case no input */
327     deviceObject = IoGetBaseFileSystemDeviceObject(fileObject);
328
329
330     /* allocate our own irp */
331     irp = IoAllocateIrp(deviceObject->StackSize, FALSE);
332     if (NULL == irp) {
333         status = STATUS_INSUFFICIENT_RESOURCES;
334         goto errorout;
335     }
336
337     irp->Tail.Overlay.OriginalFileObject = fileObject;
338     irp->Tail.Overlay.Thread = PsGetCurrentThread();
339     irp->Tail.Overlay.AuxiliaryBuffer = (PVOID) NULL;
340     irp->PendingReturned = FALSE;
341     irp->Cancel = FALSE;
342     irp->CancelRoutine = (PDRIVER_CANCEL) NULL;
343     irp->RequestorMode = KernelMode;
344     irp->UserIosb = &iosb;
345
346     /* set up the next I/O stack location. */
347     irpSp = (PIO_STACK_LOCATION)IoGetNextIrpStackLocation(irp);
348     irpSp->MajorFunction = Writing ? IRP_MJ_WRITE : IRP_MJ_READ;
349     irpSp->FileObject = fileObject;
350     irpSp->DeviceObject = deviceObject;
351
352     if (deviceObject->Flags & DO_BUFFERED_IO) {
353         irp->AssociatedIrp.SystemBuffer = Buffer;
354         irp->UserBuffer = Buffer;
355         irp->Flags |= (ULONG) (IRP_BUFFERED_IO |
356                                IRP_INPUT_OPERATION);
357     } else if (deviceObject->Flags & DO_DIRECT_IO) {
358
359         PMDL mdl = NULL;
360
361         mdl = IoAllocateMdl(Buffer, Length, FALSE, TRUE, irp);
362         if (mdl == NULL) {
363             KsPrint((0, "cfs_nt_filp_io: failed to allocate MDL for %wZ .\n",
364                         &fileObject->FileName));
365             status = STATUS_INSUFFICIENT_RESOURCES;
366             goto errorout;
367         }
368
369         __try {
370             MmProbeAndLockPages(mdl, KernelMode, Writing ? IoReadAccess : IoWriteAccess );
371         } __except(EXCEPTION_EXECUTE_HANDLER) {
372             KsPrint((0, "cfs_nt_filp_io: failed to lock buffer %p for %wZ .\n",
373                         Buffer, &fileObject->FileName));
374             IoFreeMdl(irp->MdlAddress);
375             irp->MdlAddress = NULL;
376             status = STATUS_INSUFFICIENT_RESOURCES;
377         }
378     } else {
379         irp->UserBuffer = Buffer;
380         irp->Flags = 0;
381     }
382
383     if (Writing) {
384         irp->Flags |= IRP_WRITE_OPERATION | IRP_DEFER_IO_COMPLETION;
385         irpSp->Parameters.Write.Length = Length;
386         irpSp->Parameters.Write.ByteOffset = *Offset;
387     } else {
388         irp->Flags |= IRP_READ_OPERATION | IRP_DEFER_IO_COMPLETION;
389         irpSp->Parameters.Read.Length = Length;
390         irpSp->Parameters.Read.ByteOffset = *Offset;
391     }
392
393     /* set the Irp completion routine */
394     IoSetCompletionRoutine( irp, CompletionRoutine, 
395                             &event, TRUE, TRUE, TRUE);
396
397
398     /* issue the irp to the lower layer device */
399     status = IoCallDriver(deviceObject, irp);
400
401     /* Irp is to be cleaned up in the compleiton routine */
402     irp = NULL;
403
404     if (status == STATUS_PENDING) {
405
406         /* we need wait until operation is completed, then we can
407            get the returned status and information length */
408
409         status = KeWaitForSingleObject(
410                     &event,
411                     Executive,
412                     KernelMode,
413                     FALSE,
414                     NULL
415                     );
416         if (NT_SUCCESS(status)) {
417             status = iosb.Status;
418         }
419     }
420
421     if (NT_SUCCESS(status)) {
422         *Bytes = (ULONG)iosb.Information;
423     } else {
424         *Bytes = 0;
425     }
426
427 errorout:
428
429     if (fileObject) {
430         ObDereferenceObject(fileObject);
431     }
432
433     /* free the Irp in error case */
434     if (irp) {
435         IoFreeIrp(irp);
436     }
437
438     return status;
439 }
440
441 /*
442  * cfs_filp_read
443  *     To read data from the opened file
444  *
445  * Arguments:
446  *   fp:   the pointer of the cfs_file_t strcture
447  *   buf:  pointer to the buffer to contain the data
448  *   nbytes: size in bytes to be read from the file
449  *   pos:  offset in file where reading starts, if pos
450  *         NULL, then read from current file offset
451  *
452  * Return Value:
453  *   Actual size read into the buffer in success case
454  *   Error code in failure case
455  *
456  * Notes: 
457  *   N/A
458  */
459
460 int cfs_filp_read(cfs_file_t *fp, void *buf, size_t nbytes, loff_t *pos)
461 {
462     LARGE_INTEGER   offset;
463     NTSTATUS        status;
464     int             rc = 0;
465
466     /* Read data from the file into the specified buffer */
467     if (pos != NULL) {
468         offset.QuadPart = *pos;
469     } else {
470         offset.QuadPart = fp->f_pos;
471     }
472
473     status = cfs_nt_filp_io(fp->f_handle, 0, &offset,
474                             nbytes, buf, &rc);
475
476     if (!NT_SUCCESS(status)) {
477         rc = cfs_error_code(status);
478     }
479
480     if (rc > 0) {
481         fp->f_pos = offset.QuadPart + rc;
482         if (pos != NULL)
483             *pos = fp->f_pos;
484     }
485
486     return rc;
487 }
488
489 /*
490  * cfs_filp_wrtie
491  *     To write specified data to the opened file
492  *
493  * Arguments:
494  *   fp:   the pointer of the cfs_file_t strcture
495  *   buf:  pointer to the buffer containing the data
496  *   nbytes: size in bytes to be written to the file
497  *   pos:  offset in file where writing starts, if pos
498  *         NULL, then write to current file offset
499  *
500  * Return Value:
501  *   Actual size written into the buffer in success case
502  *   Error code in failure case
503  *
504  * Notes: 
505  *   N/A
506  */
507
508 int cfs_filp_write(cfs_file_t *fp, void *buf, size_t nbytes, loff_t *pos)
509 {
510     LARGE_INTEGER   offset;
511     NTSTATUS        status;
512     int             rc = 0;
513
514     /* Read data from the file into the specified buffer */
515     if (pos != NULL) {
516         offset.QuadPart = *pos;
517     } else {
518         offset.QuadPart = fp->f_pos;
519     }
520
521     status = cfs_nt_filp_io(fp->f_handle, 1, &offset,
522                             nbytes, buf, &rc);
523
524     if (!NT_SUCCESS(status)) {
525         rc = cfs_error_code(status);
526     }
527
528     if (rc > 0) {
529         fp->f_pos = offset.QuadPart + rc;
530         if (pos != NULL)
531             *pos = fp->f_pos;
532     }
533
534     return rc;
535 }
536
537 /*
538  * cfs_filp_fsync
539  *     To sync the dirty data of the file to disk
540  *
541  * Arguments:
542  *   fp: the pointer of the cfs_file_t strcture
543  *
544  * Return Value:
545  *   Zero:  in success case
546  *   Error code: in failure case
547  *
548  * Notes: 
549  *   Nt kernel doesn't export such a routine to flush a file,
550  *   we must allocate our own Irp and issue it to the file
551  *   system driver.
552  */
553
554 int cfs_filp_fsync(cfs_file_t *fp)
555 {
556
557     PFILE_OBJECT            FileObject;
558     PDEVICE_OBJECT          DeviceObject;
559
560     NTSTATUS                Status;
561     PIRP                    Irp;
562     KEVENT                  Event;
563     IO_STATUS_BLOCK         IoSb;
564     PIO_STACK_LOCATION      IrpSp;
565
566     /* get the FileObject and the DeviceObject */
567     Status = ObReferenceObjectByHandle(
568                 fp->f_handle,
569                 FILE_WRITE_DATA,
570                 NULL,
571                 KernelMode,
572                 (PVOID*)&FileObject,
573                 NULL );
574
575     if (!NT_SUCCESS(Status)) {
576         return cfs_error_code(Status);
577     }
578
579     DeviceObject = IoGetRelatedDeviceObject(FileObject);
580
581     /* allocate a new Irp */
582     Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
583     if (!Irp) {
584         ObDereferenceObject(FileObject);
585         return -ENOMEM;
586     }
587
588     /* intialize the event */
589     KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
590
591     /* setup the Irp */
592     Irp->UserIosb = &IoSb;
593     Irp->RequestorMode = KernelMode;
594
595     Irp->Tail.Overlay.Thread = PsGetCurrentThread();
596     Irp->Tail.Overlay.OriginalFileObject = FileObject;
597
598     /* setup the Irp stack location */
599     IrpSp = IoGetNextIrpStackLocation(Irp);
600
601     IrpSp->MajorFunction = IRP_MJ_FLUSH_BUFFERS;
602     IrpSp->DeviceObject = DeviceObject;
603     IrpSp->FileObject = FileObject;
604
605     IoSetCompletionRoutine( Irp, CompletionRoutine,
606                             &Event, TRUE, TRUE, TRUE);
607
608
609     /* issue the Irp to the underlying file system driver */
610     IoCallDriver(DeviceObject, Irp);
611
612     /* wait until it is finished */
613     KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
614
615     /* cleanup our reference on it */
616     ObDereferenceObject(FileObject);
617
618     Status = IoSb.Status;
619
620     return cfs_error_code(Status);
621 }
622
623 /*
624  * cfs_get_file
625  *     To increase the reference of the file object
626  *
627  * Arguments:
628  *   fp:   the pointer of the cfs_file_t strcture
629  *
630  * Return Value:
631  *   Zero:  in success case
632  *   Non-Zero: in failure case
633  *
634  * Notes: 
635  *   N/A
636  */
637
638 int cfs_get_file(cfs_file_t *fp)
639 {
640     InterlockedIncrement(&(fp->f_count));
641     return 0;
642 }
643
644
645 /*
646  * cfs_put_file
647  *     To decrease the reference of the file object
648  *
649  * Arguments:
650  *   fp:   the pointer of the cfs_file_t strcture
651  *
652  * Return Value:
653  *   Zero:  in success case
654  *   Non-Zero: in failure case
655  *
656  * Notes: 
657  *   N/A
658  */
659
660 int cfs_put_file(cfs_file_t *fp)
661 {
662     if (InterlockedDecrement(&(fp->f_count)) == 0) {
663         cfs_filp_close(fp);
664     }
665
666     return 0;
667 }
668
669
670 /*
671  * cfs_file_count
672  *   To query the reference count of the file object
673  *
674  * Arguments:
675  *   fp:   the pointer of the cfs_file_t strcture
676  *
677  * Return Value:
678  *   the reference count of the file object
679  *
680  * Notes: 
681  *   N/A
682  */
683
684 int cfs_file_count(cfs_file_t *fp)
685 {
686     return (int)(fp->f_count);
687 }
688
689 struct dentry *dget(struct dentry *de)
690 {
691     if (de) {
692         cfs_atomic_inc(&de->d_count);
693     }
694     return de;
695 }
696
697 void dput(struct dentry *de)
698 {
699     if (!de || cfs_atomic_read(&de->d_count) == 0) {
700         return;
701     }
702     if (cfs_atomic_dec_and_test(&de->d_count)) {
703         cfs_free(de);
704     }
705 }