Author: fireball
Date: Fri Jan 23 04:26:02 2009
New Revision: 39034
URL:
http://svn.reactos.org/svn/reactos?rev=39034&view=rev
Log:
Alex Vlasov
- Implement operations with directory entries.
Modified:
trunk/reactos/drivers/filesystems/fastfat_new/direntry.c
Modified: trunk/reactos/drivers/filesystems/fastfat_new/direntry.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/drivers/filesystems/fastfa…
==============================================================================
--- trunk/reactos/drivers/filesystems/fastfat_new/direntry.c [iso-8859-1] (original)
+++ trunk/reactos/drivers/filesystems/fastfat_new/direntry.c [iso-8859-1] Fri Jan 23
04:26:02 2009
@@ -3,7 +3,7 @@
* LICENSE: GPL - See COPYING in the top level directory
* FILE: drivers/filesystems/fastfat/direntry.c
* PURPOSE: Directory entries
- * PROGRAMMERS:
+ * PROGRAMMERS: Alexey Vlasov
*/
/* INCLUDES *****************************************************************/
@@ -11,7 +11,598 @@
#define NDEBUG
#include "fastfat.h"
+/* PROTOTYPES ***************************************************************/
+#define BYTES_PER_DIRENT_LOG 0x05
+
+typedef struct _FAT_ENUM_DIR_CONTEXT *PFAT_ENUM_DIR_CONTEXT;
+
+typedef ULONG (*PFAT_COPY_DIRENT_ROUTINE) (struct _FAT_ENUM_DIR_CONTEXT *, PDIR_ENTRY,
PVOID);
+
+typedef struct _FAT_ENUM_DIR_CONTEXT
+{
+ PFILE_OBJECT FileObject;
+ LARGE_INTEGER PageOffset;
+ LONGLONG BeyoundLastEntryOffset;
+ PVOID PageBuffer;
+ PBCB PageBcb;
+
+ /*
+ * We should always cache short file name
+ * because we never know if there is a long
+ * name for the dirent and when we find out
+ * that our base dirent might become unpinned.
+ */
+ UCHAR DirentFileName[RTL_FIELD_SIZE(DIR_ENTRY, FileName) + 1];
+ PFAT_COPY_DIRENT_ROUTINE CopyDirent;
+ LONGLONG BytesPerClusterMask;
+ /*
+ * The following fields are
+ * set by the copy routine
+ */
+ PULONG NextEntryOffset;
+ PULONG FileNameLength;
+ PWCHAR FileName;
+} FAT_ENUM_DIR_CONTEXT;
+
+typedef enum _FILE_TIME_INDEX {
+ FileCreationTime = 0,
+ FileLastAccessTime,
+ FileLastWriteTime,
+ FileChangeTime
+} FILE_TIME_INDEX;
+
+VOID
+FatQueryFileTimes(
+ OUT PLARGE_INTEGER FileTimes,
+ IN PDIR_ENTRY Dirent);
+
+VOID
+Fat8dot3ToUnicodeString(
+ OUT PUNICODE_STRING FileName,
+ IN PUCHAR ShortName,
+ IN UCHAR Flags);
+
+ULONG
+FatDirentToDirInfo(
+ IN OUT PFAT_ENUM_DIR_CONTEXT Context,
+ IN PDIR_ENTRY Dirent,
+ IN PVOID Buffer);
+
+ULONG
+FatDirentToFullDirInfo(
+ IN OUT PFAT_ENUM_DIR_CONTEXT Context,
+ IN PDIR_ENTRY Dirent,
+ IN PVOID Buffer);
+
+ULONG
+FatDirentToIdFullDirInfo(
+ IN OUT PFAT_ENUM_DIR_CONTEXT Context,
+ IN PDIR_ENTRY Dirent,
+ IN PVOID Buffer);
+
+ULONG
+FatDirentToBothDirInfo(
+ IN OUT PFAT_ENUM_DIR_CONTEXT Context,
+ IN PDIR_ENTRY Dirent,
+ IN PVOID Buffer);
+
+ULONG
+FatDirentToIdBothDirInfo(
+ IN OUT PFAT_ENUM_DIR_CONTEXT Context,
+ IN PDIR_ENTRY Dirent,
+ IN PVOID Buffer);
+
+ULONG
+FatDirentToNamesInfo(
+ IN OUT PFAT_ENUM_DIR_CONTEXT Context,
+ IN PDIR_ENTRY Dirent,
+ IN PVOID Buffer);
+
+ULONG
+FatDirentToObjectIdInfo(
+ IN OUT PFAT_ENUM_DIR_CONTEXT Context,
+ IN PDIR_ENTRY Dirent,
+ IN PVOID Buffer);
+
/* FUNCTIONS *****************************************************************/
+FORCEINLINE
+VOID
+FatDateTimeToSystemTime(
+ OUT PLARGE_INTEGER SystemTime,
+ IN PFAT_DATETIME FatDateTime,
+ IN UCHAR TenMs OPTIONAL)
+{
+ TIME_FIELDS TimeFields;
+
+ /*
+ * Setup time fields.
+ */
+ TimeFields.Year = FatDateTime->Date.Year + 1980;
+ TimeFields.Month = FatDateTime->Date.Month;
+ TimeFields.Day = FatDateTime->Date.Day;
+ TimeFields.Hour = FatDateTime->Time.Hour;
+ TimeFields.Minute = FatDateTime->Time.Minute;
+ TimeFields.Second = (FatDateTime->Time.DoubleSeconds << 1);
+ /*
+ * Adjust up to 10 milliseconds
+ * if the parameter was supplied
+ */
+ if (ARGUMENT_PRESENT(TenMs))
+ {
+ TimeFields.Second += TenMs / 100;
+ TimeFields.Milliseconds = (TenMs % 100) * 10;
+ }
+ else
+ {
+ TimeFields.Milliseconds = 0;
+ }
+ /*
+ * Fix seconds value that might get beyoud the bound.
+ */
+ if (TimeFields.Second > 59)
+ TimeFields.Second = 0;
+ /*
+ * Perform ceonversion to system time if possible.
+ */
+ if (RtlTimeFieldsToTime(&TimeFields, SystemTime)) {
+ /* Convert to system time */
+ ExLocalTimeToSystemTime( SystemTime, SystemTime );
+ }
+ else
+ {
+ /* Set to default time if conversion failed */
+ *SystemTime = FatGlobalData.DefaultFileTime;
+ }
+}
+
+VOID
+FatQueryFileTimes(
+ OUT PLARGE_INTEGER FileTimes,
+ IN PDIR_ENTRY Dirent)
+{
+ /*
+ * Convert LastWriteTime
+ */
+ FatDateTimeToSystemTime(
+ &FileTimes[FileLastWriteTime],
+ &Dirent->LastWriteDateTime, 0);
+ /*
+ * All other time fileds are valid (according to MS)
+ * only if Win31 compatability mode is set.
+ */
+ if (FatGlobalData.Win31FileSystem)
+ {
+ /* We can avoid calling conversion routine
+ * if time in dirent is 0 or equals to already
+ * known time (LastWriteTime).
+ */
+ if (Dirent->CreationDateTime.Value == 0)
+ {
+ /* Set it to default time */
+ FileTimes[FileCreationTime] = FatGlobalData.DefaultFileTime;
+ }
+ else if (Dirent->CreationDateTime.Value
+ == Dirent->LastWriteDateTime.Value)
+ {
+ /* Assign the already known time */
+ FileTimes[FileCreationTime] = FileTimes[FileLastWriteTime];
+ /* Adjust milliseconds from extra dirent field */
+ FileTimes[FileCreationTime].QuadPart
+ += (ULONG) Dirent->CreationTimeTenMs * 100000;
+ }
+ else
+ {
+ /* Perform conversion */
+ FatDateTimeToSystemTime(
+ &FileTimes[FileCreationTime],
+ &Dirent->CreationDateTime,
+ Dirent->CreationTimeTenMs);
+ }
+ if (Dirent->LastAccessDate.Value == 0)
+ {
+ /* Set it to default time */
+ FileTimes[FileLastAccessTime] = FatGlobalData.DefaultFileTime;
+ }
+ else if (Dirent->LastAccessDate.Value
+ == Dirent->LastWriteDateTime.Date.Value)
+ {
+ /* Assign the already known time */
+ FileTimes[FileLastAccessTime] = FileTimes[FileLastWriteTime];
+ }
+ else
+ {
+ /* Perform conversion */
+ FAT_DATETIME LastAccessDateTime;
+
+ LastAccessDateTime.Date.Value = Dirent->LastAccessDate.Value;
+ LastAccessDateTime.Time.Value = 0;
+ FatDateTimeToSystemTime(
+ &FileTimes[FileLastAccessTime],
+ &LastAccessDateTime, 0);
+ }
+ }
+}
+
+VOID
+Fat8dot3ToUnicodeString(
+ OUT PUNICODE_STRING FileName,
+ IN PUCHAR ShortName,
+ IN UCHAR Flags)
+{
+ PCHAR Name;
+ UCHAR Index, Ext = 0;
+ OEM_STRING Oem;
+
+ Name = Add2Ptr(FileName->Buffer, 0x0c, PCHAR);
+ RtlCopyMemory(Name, ShortName, 0x0b);
+
+ /* Restore the name byte used to mark deleted entries. */
+ if (Name[0] == 0x05)
+ Name[0] |= 0xe0;
+
+ /* Locate the end of name part. */
+ for (Index = 0; Index < 0x08
+ && Name[Index] != 0x20; Index++);
+
+ /* Locate the end of extension part */
+ if (Name[0x08] != 0x20)
+ {
+ Ext = 0x2;
+ Name[Index++] = 0x2e;
+ Name[Index++] = Name[0x08];
+ if ((Name[Index] = Name[0x09]) != 0x20)
+ {
+ Index ++; Ext ++;
+ }
+ if ((Name[Index] = Name[0x0a]) != 0x20)
+ {
+ Index ++; Ext ++;
+ }
+ }
+ /* Perform Oem to Unicode conversion. */
+ Oem.Buffer = Name;
+ Oem.Length = Index;
+ Oem.MaximumLength = Index;
+ RtlOemStringToUnicodeString(FileName, &Oem, FALSE);
+ Index = FlagOn(Flags, FAT_CASE_LOWER_BASE|FAT_CASE_LOWER_EXT);
+ if (Index > 0)
+ {
+ /* Downcase the whole name */
+ if (Index == (FAT_CASE_LOWER_BASE|FAT_CASE_LOWER_EXT))
+ {
+ RtlUpcaseUnicodeString(FileName, FileName, FALSE);
+ }
+ else
+ {
+ if (Index == FAT_CASE_LOWER_EXT)
+ {
+ /* Set extension for downcase */
+ Oem.Length = Ext * sizeof(WCHAR);
+ Oem.Buffer = Add2Ptr(
+ FileName->Buffer,
+ FileName->Length - Oem.Length,
+ PSTR);
+ }
+ else
+ {
+ /* Set base name for downcase */
+ Oem.Buffer = (PSTR) FileName->Buffer;
+ Oem.Length = FileName->Length
+ - Ext * sizeof(WCHAR);
+ }
+ Oem.MaximumLength = Oem.Length;
+ RtlUpcaseUnicodeString(
+ (PUNICODE_STRING)&Oem,
+ (PUNICODE_STRING)&Oem,
+ FALSE);
+ }
+ }
+}
+
+ULONG
+FatDirentToDirInfo(
+ IN OUT PFAT_ENUM_DIR_CONTEXT Context,
+ IN PDIR_ENTRY Dirent,
+ IN PVOID Buffer)
+{
+ PFILE_DIRECTORY_INFORMATION Info;
+ //UNICODE_STRING FileName;
+
+ Info = (PFILE_DIRECTORY_INFORMATION) Buffer;
+ /* Setup Attributes */
+ Info->FileAttributes = Dirent->Attributes;
+ /* Setup times */
+ FatQueryFileTimes(&Info->CreationTime, Dirent);
+ /* Setup sizes */
+ Info->EndOfFile.QuadPart = Dirent->FileSize;
+ Info->AllocationSize.QuadPart =
+ (Context->BytesPerClusterMask + Dirent->FileSize)
+ & ~(Context->BytesPerClusterMask);
+ //FileName.Buffer = Info->ShortName;
+ //FileName.MaximumLength = sizeof(Info->ShortName);
+ // FatQueryShortName(&FileName, Dirent);
+ // Info->ShortNameLength = (CCHAR) FileName.Length;
+ Info->NextEntryOffset = sizeof(*Info);
+ /*
+ * Associate LFN buffer and length pointers
+ * of this entry with the context.
+ */
+ Context->NextEntryOffset = &Info->NextEntryOffset;
+ Context->FileName = Info->FileName;
+ Context->FileNameLength = &Info->FileNameLength;
+ return Info->NextEntryOffset;
+}
+
+ULONG
+FatDirentToFullDirInfo(
+ IN OUT PFAT_ENUM_DIR_CONTEXT Context,
+ IN PDIR_ENTRY Dirent,
+ IN PVOID Buffer)
+{
+ PFILE_FULL_DIR_INFORMATION Info;
+ //UNICODE_STRING FileName;
+
+ Info = (PFILE_FULL_DIR_INFORMATION) Buffer;
+ /* Setup Attributes */
+ Info->FileAttributes = Dirent->Attributes;
+ /* Setup times */
+ FatQueryFileTimes(&Info->CreationTime, Dirent);
+ /* Setup sizes */
+ Info->EndOfFile.QuadPart = Dirent->FileSize;
+ Info->AllocationSize.QuadPart =
+ (Context->BytesPerClusterMask + Dirent->FileSize)
+ & ~(Context->BytesPerClusterMask);
+ //FileName.Buffer = Info->ShortName;
+ //FileName.MaximumLength = sizeof(Info->ShortName);
+ // FatQueryShortName(&FileName, Dirent);
+ // Info->ShortNameLength = (CCHAR) FileName.Length;
+ Info->NextEntryOffset = sizeof(*Info);
+ /*
+ * Associate LFN buffer and length pointers
+ * of this entry with the context.
+ */
+ Context->NextEntryOffset = &Info->NextEntryOffset;
+ Context->FileName = Info->FileName;
+ Context->FileNameLength = &Info->FileNameLength;
+ return Info->NextEntryOffset;
+}
+
+ULONG
+FatDirentToIdFullDirInfo(
+ IN OUT PFAT_ENUM_DIR_CONTEXT Context,
+ IN PDIR_ENTRY Dirent,
+ IN PVOID Buffer)
+{
+ PFILE_ID_FULL_DIR_INFORMATION Info;
+ //UNICODE_STRING FileName;
+
+ Info = (PFILE_ID_FULL_DIR_INFORMATION) Buffer;
+ /* Setup Attributes */
+ Info->FileAttributes = Dirent->Attributes;
+ /* Setup times */
+ FatQueryFileTimes(&Info->CreationTime, Dirent);
+ /* Setup sizes */
+ Info->EndOfFile.QuadPart = Dirent->FileSize;
+ Info->AllocationSize.QuadPart =
+ (Context->BytesPerClusterMask + Dirent->FileSize)
+ & ~(Context->BytesPerClusterMask);
+ //FileName.Buffer = Info->ShortName;
+ //FileName.MaximumLength = sizeof(Info->ShortName);
+ // FatQueryShortName(&FileName, Dirent);
+ // Info->ShortNameLength = (CCHAR) FileName.Length;
+ Info->NextEntryOffset = sizeof(*Info);
+ /*
+ * Associate LFN buffer and length pointers
+ * of this entry with the context.
+ */
+ Context->NextEntryOffset = &Info->NextEntryOffset;
+ Context->FileName = Info->FileName;
+ Context->FileNameLength = &Info->FileNameLength;
+ return Info->NextEntryOffset;
+}
+
+ULONG
+FatDirentToBothDirInfo(
+ IN OUT PFAT_ENUM_DIR_CONTEXT Context,
+ IN PDIR_ENTRY Dirent,
+ IN PVOID Buffer)
+{
+ PFILE_BOTH_DIR_INFORMATION Info;
+ UNICODE_STRING FileName;
+
+ Info = (PFILE_BOTH_DIR_INFORMATION) Buffer;
+ /* Setup Attributes */
+ Info->FileAttributes = Dirent->Attributes;
+ /* Setup times */
+ FatQueryFileTimes(&Info->CreationTime, Dirent);
+ /* Setup sizes */
+ Info->EndOfFile.QuadPart = Dirent->FileSize;
+ Info->AllocationSize.QuadPart =
+ (Context->BytesPerClusterMask + Dirent->FileSize)
+ & ~(Context->BytesPerClusterMask);
+ FileName.Buffer = Info->ShortName;
+ FileName.MaximumLength = sizeof(Info->ShortName);
+ Fat8dot3ToUnicodeString(&FileName, Dirent->FileName, Dirent->Case);
+ Info->ShortNameLength = (CCHAR) FileName.Length;
+ Info->NextEntryOffset = sizeof(*Info);
+ /*
+ * Associate LFN buffer and length pointers
+ * of this entry with the context.
+ */
+ Context->NextEntryOffset = &Info->NextEntryOffset;
+ Context->FileName = Info->FileName;
+ Context->FileNameLength = &Info->FileNameLength;
+ return Info->NextEntryOffset;
+}
+
+ULONG
+FatDirentToIdBothDirInfo(
+ IN OUT PFAT_ENUM_DIR_CONTEXT Context,
+ IN PDIR_ENTRY Dirent,
+ IN PVOID Buffer)
+{
+ PFILE_ID_BOTH_DIR_INFORMATION Info;
+ UNICODE_STRING FileName;
+
+ Info = (PFILE_ID_BOTH_DIR_INFORMATION) Buffer;
+ /* Setup Attributes */
+ Info->FileAttributes = Dirent->Attributes;
+ /* Setup times */
+ FatQueryFileTimes(&Info->CreationTime, Dirent);
+ /* Setup sizes */
+ Info->EndOfFile.QuadPart = Dirent->FileSize;
+ Info->AllocationSize.QuadPart =
+ (Context->BytesPerClusterMask + Dirent->FileSize)
+ & ~(Context->BytesPerClusterMask);
+ FileName.Buffer = Info->ShortName;
+ FileName.MaximumLength = sizeof(Info->ShortName);
+ Fat8dot3ToUnicodeString(&FileName, Dirent->FileName, Dirent->Case);
+ Info->ShortNameLength = (CCHAR) FileName.Length;
+ Info->NextEntryOffset = sizeof(*Info);
+ /*
+ * Associate LFN buffer and length pointers
+ * of this entry with the context.
+ */
+ Context->NextEntryOffset = &Info->NextEntryOffset;
+ Context->FileName = Info->FileName;
+ Context->FileNameLength = &Info->FileNameLength;
+ return Info->NextEntryOffset;
+}
+
+ULONG
+FatDirentToNamesInfo(
+ IN OUT PFAT_ENUM_DIR_CONTEXT Context,
+ IN PDIR_ENTRY Dirent,
+ IN PVOID Buffer)
+{
+ PFILE_NAMES_INFORMATION Info;
+
+ Info = (PFILE_NAMES_INFORMATION) Buffer;
+// FatQueryShortName(&FileName, Dirent);
+ Info->NextEntryOffset = sizeof(*Info);
+ /*
+ * Associate LFN buffer and length pointers
+ * of this entry with the context.
+ */
+ Context->NextEntryOffset = &Info->NextEntryOffset;
+ Context->FileName = Info->FileName;
+ Context->FileNameLength = &Info->FileNameLength;
+ return Info->NextEntryOffset;
+}
+
+ULONG
+FatDirentToObjectIdInfo(
+ IN OUT PFAT_ENUM_DIR_CONTEXT Context,
+ IN PDIR_ENTRY Dirent,
+ IN PVOID Buffer)
+{
+ PFILE_OBJECTID_INFORMATION Info;
+
+ Info = (PFILE_OBJECTID_INFORMATION) Buffer;
+ return sizeof(*Info);
+}
+
+ULONG
+FatEnumerateDirents(
+ IN OUT PFAT_ENUM_DIR_CONTEXT Context,
+ IN ULONG Index,
+ IN BOOLEAN CanWait)
+{
+ LONGLONG PageOffset;
+ SIZE_T OffsetWithinPage, PageValidLength;
+ PUCHAR Entry, BeyoudLastEntry;
+ /*
+ * Determine page offset and the offset within page
+ * for the first cluster.
+ */
+ PageValidLength = PAGE_SIZE;
+ PageOffset = ((LONGLONG) Index) << BYTES_PER_DIRENT_LOG;
+ OffsetWithinPage = (SIZE_T) (PageOffset & (PAGE_SIZE - 1));
+ PageOffset -= OffsetWithinPage;
+ /*
+ * Check if the context already has the required page mapped.
+ * Map the first page is necessary.
+ */
+ if (PageOffset != Context->PageOffset.QuadPart)
+ {
+ Context->PageOffset.QuadPart = PageOffset;
+ if (Context->PageBcb != NULL)
+ {
+ CcUnpinData(Context->PageBcb);
+ Context->PageBcb = NULL;
+ }
+ if (!CcMapData(Context->FileObject, &Context->PageOffset,
+ PAGE_SIZE, CanWait, &Context->PageBcb, &Context->PageBuffer))
+ {
+ Context->PageOffset.QuadPart = 0LL;
+ ExRaiseStatus(STATUS_CANT_WAIT);
+ }
+ }
+ Entry = Add2Ptr(Context->PageBuffer, OffsetWithinPage, PUCHAR);
+ /*
+ * Next Page Offset.
+ */
+ PageOffset = Context->PageOffset.QuadPart + PAGE_SIZE;
+ if (PageOffset > Context->BeyoundLastEntryOffset)
+ PageValidLength = (SIZE_T) (Context->BeyoundLastEntryOffset
+ - Context->PageOffset.QuadPart);
+ BeyoudLastEntry = Add2Ptr(Context->PageBuffer, PageValidLength, PUCHAR);
+ while (TRUE)
+ {
+ do
+ {
+ if (*Entry == FAT_DIRENT_NEVER_USED)
+ return 0; // TODO: return something reasonable.
+ if (*Entry == FAT_DIRENT_DELETED)
+ {
+ continue;
+ }
+ if (Entry[0x0a] == FAT_DIRENT_ATTR_LFN)
+ {
+ PLONG_FILE_NAME_ENTRY Lfnent;
+ Lfnent = (PLONG_FILE_NAME_ENTRY) Entry;
+ }
+ else
+ {
+ PDIR_ENTRY Dirent;
+ Dirent = (PDIR_ENTRY) Entry;
+ RtlCopyMemory(Context->DirentFileName,
+ Dirent->FileName,
+ sizeof(Dirent->FileName));
+ }
+ }
+ while (++Entry < BeyoudLastEntry);
+ /*
+ * Check if this is the last available entry.
+ */
+ if (PageValidLength < PAGE_SIZE)
+ break;
+ /*
+ * We are getting beyound current page and
+ * are still in the continous run, map the next page.
+ */
+ Context->PageOffset.QuadPart = PageOffset;
+ CcUnpinData(Context->PageBcb);
+ if (!CcMapData(Context->FileObject,
+ &Context->PageOffset, PAGE_SIZE, CanWait,
+ &Context->PageBcb, &Context->PageBuffer))
+ {
+ Context->PageBcb = NULL;
+ Context->PageOffset.QuadPart = 0LL;
+ ExRaiseStatus(STATUS_CANT_WAIT);
+ }
+ Entry = (PUCHAR) Context->PageBuffer;
+ /*
+ * Next Page Offset.
+ */
+ PageOffset = Context->PageOffset.QuadPart + PAGE_SIZE;
+ if (PageOffset > Context->BeyoundLastEntryOffset)
+ PageValidLength = (SIZE_T) (Context->BeyoundLastEntryOffset
+ - Context->PageOffset.QuadPart);
+ BeyoudLastEntry = Add2Ptr(Context->PageBuffer, PageValidLength, PUCHAR);
+ }
+ return 0;
+}
/* EOF */