Stream Rename

Ken,

you wrote on Wednesday, March 27, 2002, 17:21:26:

KG> I’ve noticed on WIN2K that when an NTFS stream is renamed (by
KG> explorer) the file name is not included as part of the “to” file,
KG> only the stream name. Is this normal behavior?

Yes.

KG> I’ve tried do a rename via the win32 API “MoveFile”, but it says the
KG> syntax is incorrect when I try to. The SDK documents say that there
KG> is no “official or supported way to rename a stream”. So what is the
KG> unofficial behavior that a file system/filter is to expect?

Below is an example, which is the only way I know to rename a stream. It
does use the native API but not any undocumented functions in there. The
functions and structures are documented in the DDK/IFS Kit. I have
copied the neccessary stuff from the DDK headers
(FILE_RENAME_INFORMATION is documented in the IFS Kit), you need to link
with ntdll.lib. The trick is to open the stream as a separate “file”,
relative to the “parent file”.

The sample assumes that there is a file C:\Test.txt with a named stream
named ‘:TestStream’ which is supposed to be renamed to ‘:RenamedStream’.
Type

echo hello>c:\test.txt:TestStream

in a DOS box to create this file/stream.

========================================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <windows.h>

typedef LONG NTSTATUS;
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)

typedef enum _FILE_INFORMATION_CLASS {
FileDirectoryInformation = 1,
FileFullDirectoryInformation, // 2
FileBothDirectoryInformation, // 3
FileBasicInformation, // 4 wdm
FileStandardInformation, // 5 wdm
FileInternalInformation, // 6
FileEaInformation, // 7
FileAccessInformation, // 8
FileNameInformation, // 9
FileRenameInformation, // 10
FileLinkInformation, // 11
FileNamesInformation, // 12
FileDispositionInformation, // 13
FilePositionInformation, // 14 wdm
FileFullEaInformation, // 15
FileModeInformation, // 16
FileAlignmentInformation, // 17
FileAllInformation, // 18
FileAllocationInformation, // 19
FileEndOfFileInformation, // 20 wdm
FileAlternateNameInformation, // 21
FileStreamInformation, // 22
FilePipeInformation, // 23
FilePipeLocalInformation, // 24
FilePipeRemoteInformation, // 25
FileMailslotQueryInformation, // 26
FileMailslotSetInformation, // 27
FileCompressionInformation, // 28
FileObjectIdInformation, // 29
FileCompletionInformation, // 30
FileMoveClusterInformation, // 31
FileQuotaInformation, // 32
FileReparsePointInformation, // 33
FileNetworkOpenInformation, // 34
FileAttributeTagInformation, // 35
FileTrackingInformation, // 36
FileMaximumInformation
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;

typedef struct _FILE_RENAME_INFORMATION {
BOOLEAN ReplaceIfExists;
HANDLE RootDirectory;
ULONG FileNameLength;
WCHAR FileName[1];
} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION;

#define FILE_OPEN 0x00000001
#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
#define OBJ_CASE_INSENSITIVE 0x00000040L

typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
void * SecurityDescriptor;
void * SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;

typedef struct _IO_STATUS_BLOCK {
union {
NTSTATUS Status;
void * Pointer;
};
ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

#define InitializeObjectAttributes( p, n, a, r, s ) { <br> (p)->Length = sizeof(OBJECT_ATTRIBUTES); <br> (p)->RootDirectory = r; <br> (p)->Attributes = a; <br> (p)->ObjectName = n; <br> (p)->SecurityDescriptor = s; <br> (p)->SecurityQualityOfService = NULL; <br> }

void NTAPI RtlInitUnicodeString(
PUNICODE_STRING DestinationString,
PCWSTR SourceString
);

NTSTATUS NTAPI NtCreateFile(
PHANDLE FileHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PIO_STATUS_BLOCK IoStatusBlock,
PLARGE_INTEGER AllocationSize,
ULONG FileAttributes,
ULONG ShareAccess,
ULONG CreateDisposition,
ULONG CreateOptions,
void * EaBuffer,
ULONG EaLength
);

NTSTATUS NTAPI NtSetInformationFile(
HANDLE FileHandle,
PIO_STATUS_BLOCK IoStatusBlock,
void * FileInformation,
ULONG Length,
FILE_INFORMATION_CLASS FileInformationClass
);

NTSTATUS NTAPI NtClose(
HANDLE Handle
);

int main(int argc,char argv[])
{
WCHAR szFileName[] = L"\??\C:\Test.txt";
WCHAR szStreamName[] = L":TestStream";
WCHAR szNewStreamName[] = L":RenamedStream";

FILE_RENAME_INFORMATION pFri = NULL;
ULONG BufferSize;

UNICODE_STRING Name;
IO_STATUS_BLOCK Iosb;
OBJECT_ATTRIBUTES FileObject;

HANDLE hFile;
HANDLE hStream;

NTSTATUS ntStatus;

// Open the file
RtlInitUnicodeString(&Name, szFileName);
InitializeObjectAttributes(
&FileObject,
&Name,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);

ntStatus = NtCreateFile(
&hFile,
DELETE | SYNCHRONIZE,
&FileObject,
&Iosb,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_DELETE,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0
);

if (STATUS_SUCCESS != ntStatus)
{
printf(“Could not open file %ls, error %lX\n”, szFileName, ntStatus);
return -1;
}

// Open the stream
RtlInitUnicodeString(&Name, szStreamName);
InitializeObjectAttributes(
&FileObject,
&Name,
OBJ_CASE_INSENSITIVE,
hFile, // file is specified as “root directory” for named stream
NULL
);

ntStatus = NtCreateFile(
&hStream,
DELETE | SYNCHRONIZE,
&FileObject,
&Iosb,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_DELETE,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0
);

if (STATUS_SUCCESS != ntStatus)
{
printf(“Could not open stream %ls, error %lX\n”, szStreamName, ntStatus);
NtClose(hFile);
return -1;
}

BufferSize = sizeof(FILE_RENAME_INFORMATION) + (wcslen(szNewStreamName) * sizeof(WCHAR)) + sizeof(WCHAR);
pFri = malloc( BufferSize );

if (NULL == pFri)
printf(“Out of memory\n”);
else
{
pFri->ReplaceIfExists = TRUE;
pFri->RootDirectory = NULL;
pFri->FileNameLength = wcslen(szNewStreamName) * sizeof(WCHAR);
wcscpy(pFri->FileName, szNewStreamName);

ntStatus = NtSetInformationFile(
hStream,
&Iosb,
pFri,
BufferSize,
FileRenameInformation
);

if (STATUS_SUCCESS != ntStatus)
printf(“Unable to rename stream, error %lX\n”, ntStatus);
}

if (NULL != pFri)
free(pFri);
NtClose(hStream);
NtClose(hFile);

return 0;
}

Ralf.

/
======================== Foot shot for today =========================
Kann Datei footshot.txt nicht ?ffnen
======================================================================
/</windows.h></wchar.h></string.h></stdlib.h></stdio.h>

Ralf,
Extemely helpful! Thanks!
Ken

-----Original Message-----
=46rom: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com]On Behalf Of Ralf Buschmann
Sent: Wednesday, March 27, 2002 2:55 PM
To: File Systems Developers
Subject: [ntfsd] Re: Stream Rename

Ken,

you wrote on Wednesday, March 27, 2002, 17:21:26:

KG> I’ve noticed on WIN2K that when an NTFS stream is renamed (by
KG> explorer) the file name is not included as part of the “to” file,
KG> only the stream name. Is this normal behavior?

Yes.

KG> I’ve tried do a rename via the win32 API “MoveFile”, but it says =
the
KG> syntax is incorrect when I try to. The SDK documents say that the=
re
KG> is no “official or supported way to rename a stream”. So what is =
the
KG> unofficial behavior that a file system/filter is to expect?

Below is an example, which is the only way I know to rename a stream.=
It
does use the native API but not any undocumented functions in there. =
The
functions and structures are documented in the DDK/IFS Kit. I have
copied the neccessary stuff from the DDK headers
(FILE_RENAME_INFORMATION is documented in the IFS Kit), you need to l=
ink
with ntdll.lib. The trick is to open the stream as a separate “file”,
relative to the “parent file”.

The sample assumes that there is a file C:\Test.txt with a named stre=
am
named ‘:TestStream’ which is supposed to be renamed to ‘:RenamedStrea=
m’.
Type

echo hello>c:\test.txt:TestStream

in a DOS box to create this file/stream.

=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <windows.h>

typedef LONG NTSTATUS;
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)

typedef enum _FILE_INFORMATION_CLASS {
FileDirectoryInformation =3D 1,
FileFullDirectoryInformation, // 2
FileBothDirectoryInformation, // 3
FileBasicInformation, // 4 wdm
FileStandardInformation, // 5 wdm
FileInternalInformation, // 6
FileEaInformation, // 7
FileAccessInformation, // 8
FileNameInformation, // 9
FileRenameInformation, // 10
FileLinkInformation, // 11
FileNamesInformation, // 12
FileDispositionInformation, // 13
FilePositionInformation, // 14 wdm
FileFullEaInformation, // 15
FileModeInformation, // 16
FileAlignmentInformation, // 17
FileAllInformation, // 18
FileAllocationInformation, // 19
FileEndOfFileInformation, // 20 wdm
FileAlternateNameInformation, // 21
FileStreamInformation, // 22
FilePipeInformation, // 23
FilePipeLocalInformation, // 24
FilePipeRemoteInformation, // 25
FileMailslotQueryInformation, // 26
FileMailslotSetInformation, // 27
FileCompressionInformation, // 28
FileObjectIdInformation, // 29
FileCompletionInformation, // 30
FileMoveClusterInformation, // 31
FileQuotaInformation, // 32
FileReparsePointInformation, // 33
FileNetworkOpenInformation, // 34
FileAttributeTagInformation, // 35
FileTrackingInformation, // 36
FileMaximumInformation
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;

typedef struct _FILE_RENAME_INFORMATION {
BOOLEAN ReplaceIfExists;
HANDLE RootDirectory;
ULONG FileNameLength;
WCHAR FileName[1];
} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION;

#define FILE_OPEN 0x00000001
#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
#define OBJ_CASE_INSENSITIVE 0x00000040L

typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
void * SecurityDescriptor;
void * SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;

typedef struct _IO_STATUS_BLOCK {
union {
NTSTATUS Status;
void * Pointer;
};
ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

#define InitializeObjectAttributes( p, n, a, r, s ) { <br> (p)->Length =3D sizeof(OBJECT_ATTRIBUTES); <br> (p)->RootDirectory =3D r; <br> (p)->Attributes =3D a; <br> (p)->ObjectName =3D n; <br> (p)->SecurityDescriptor =3D s; <br> (p)->SecurityQualityOfService =3D NULL; <br> }

void NTAPI RtlInitUnicodeString(
PUNICODE_STRING DestinationString,
PCWSTR SourceString
);

NTSTATUS NTAPI NtCreateFile(
PHANDLE FileHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PIO_STATUS_BLOCK IoStatusBlock,
PLARGE_INTEGER AllocationSize,
ULONG FileAttributes,
ULONG ShareAccess,
ULONG CreateDisposition,
ULONG CreateOptions,
void * EaBuffer,
ULONG EaLength
);

NTSTATUS NTAPI NtSetInformationFile(
HANDLE FileHandle,
PIO_STATUS_BLOCK IoStatusBlock,
void * FileInformation,
ULONG Length,
FILE_INFORMATION_CLASS FileInformationClass
);

NTSTATUS NTAPI NtClose(
HANDLE Handle
);

int main(int argc,char *argv)
{
WCHAR szFileName =3D L"\??\C:\Test.txt";
WCHAR szStreamName =3D L":TestStream";
WCHAR szNewStreamName =3D L":RenamedStream";

FILE_RENAME_INFORMATION pFri =3D NULL;
ULONG BufferSize;

UNICODE_STRING Name;
IO_STATUS_BLOCK Iosb;
OBJECT_ATTRIBUTES FileObject;

HANDLE hFile;
HANDLE hStream;

NTSTATUS ntStatus;

// Open the file
RtlInitUnicodeString(&Name, szFileName);
InitializeObjectAttributes(
&FileObject,
&Name,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);

ntStatus =3D NtCreateFile(
&hFile,
DELETE | SYNCHRONIZE,
&FileObject,
&Iosb,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_DELETE,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0
);

if (STATUS_SUCCESS !=3D ntStatus)
{
printf(“Could not open file %ls, error %lX\n”, szFile=
Name,
ntStatus);
return -1;
}

// Open the stream
RtlInitUnicodeString(&Name, szStreamName);
InitializeObjectAttributes(
&FileObject,
&Name,
OBJ_CASE_INSENSITIVE,
hFile, // file is specified as “r=
oot
directory” for named stream
NULL
);

ntStatus =3D NtCreateFile(
&hStream,
DELETE | SYNCHRONIZE,
&FileObject,
&Iosb,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_DELETE,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0
);

if (STATUS_SUCCESS !=3D ntStatus)
{
printf(“Could not open stream %ls, error %lX\n”,
szStreamName, ntStatus);
NtClose(hFile);
return -1;
}

BufferSize =3D sizeof(FILE_RENAME_INFORMATION) +
(wcslen(szNewStreamName) * sizeof(WCHAR)) + sizeof(WCHAR);
pFri =3D malloc( BufferSize );

if (NULL =3D=3D pFri)
printf(“Out of memory\n”);
else
{
pFri->ReplaceIfExists =3D TRUE;
pFri->RootDirectory =3D NULL;
pFri->FileNameLength =3D wcslen(szNewStreamName)
sizeof(WCHAR);
wcscpy(pFri->FileName, szNewStreamName);

ntStatus =3D NtSetInformationFile(
hStream,
&Iosb,
pFri,
BufferSize,
FileRenameInformation
);

if (STATUS_SUCCESS !=3D ntStatus)
printf(“Unable to rename stream, error %lX\n”=
,
ntStatus);
}

if (NULL !=3D pFri)
free(pFri);
NtClose(hStream);
NtClose(hFile);

return 0;
}

Ralf.

/
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D Foot shot for today =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
Kann Datei footshot.txt nicht =F6ffnen
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D
/


You are currently subscribed to ntfsd as: xxxxx@legato.com
To unsubscribe send a blank email to %%email.unsub%%</windows.h></wchar.h></string.h></stdlib.h></stdio.h>

Ken,

you wrote on Wednesday, March 27, 2002, 21:14:38:

KG> Ralf,
KG> Extemely helpful! Thanks!

Oh, and you can use the same technique to delete an individual stream
(FILE_DISPOSITION_INFORMATION) or attach a new stream to a file (just
create the new “stream file”).

Ralf.

Ralf,
Thanks, the docs do say though that DeleteFile will delete a stream.

Ken

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com]On Behalf Of Ralf Buschmann
Sent: Wednesday, March 27, 2002 3:33 PM
To: File Systems Developers
Subject: [ntfsd] Re: Stream Rename

Ken,

you wrote on Wednesday, March 27, 2002, 21:14:38:

KG> Ralf,
KG> Extemely helpful! Thanks!

Oh, and you can use the same technique to delete an individual stream
(FILE_DISPOSITION_INFORMATION) or attach a new stream to a file (just
create the new “stream file”).

Ralf.


You are currently subscribed to ntfsd as: xxxxx@legato.com
To unsubscribe send a blank email to %%email.unsub%%

Hi Ralf,
We created a routine that performs the rename without having to open the
main file as the parent and opening the stream directly. It seems to work!
What was the reason you felt it needed to be related to the parent file?

Thanks,
Ken

DWORD RenameStream(
LPCTSTR pszFileName, // full file name, including
stream
LPCTSTR pszNewStream) // new stream name,
including leading ‘:’
{
CBuffer RenameBuf;
FILE_RENAME_INFORMATION *pFri = NULL;
IO_STATUS_BLOCK iosb;

HANDLE hFile = CreateFile(pszFileName, DELETE | SYNCHRONIZE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return GetLastError();
RenameBuf.SetSize(sizeof(FILE_RENAME_INFORMATION) +
(lstrlen(pszNewStream) + 1) * sizeof(TCHAR));
pFri = (FILE_RENAME_INFORMATION *)RenameBuf.GetData();
pFri->ReplaceIfExists = TRUE;
pFri->RootDirectory = NULL;
pFri->FileNameLength = wcslen(pszNewStream) * sizeof(TCHAR);
wcscpy(pFri->FileName, pszNewStream);
DWORD dwError =
LsaNtStatusToWinError(NTDll.NtSetInformationFile(hFile, &iosb, pFri,
RenameBuf.GetSize(), FileRenameInformation));
(void)CloseHandle(hFile);
return dwError;
}

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com]On Behalf Of Ralf Buschmann
Sent: Wednesday, March 27, 2002 3:33 PM
To: File Systems Developers
Subject: [ntfsd] Re: Stream Rename

Ken,

you wrote on Wednesday, March 27, 2002, 21:14:38:

KG> Ralf,
KG> Extemely helpful! Thanks!

Oh, and you can use the same technique to delete an individual stream
(FILE_DISPOSITION_INFORMATION) or attach a new stream to a file (just
create the new “stream file”).

Ralf.


You are currently subscribed to ntfsd as: xxxxx@legato.com
To unsubscribe send a blank email to %%email.unsub%%

Ken,

you wrote on Wednesday, March 27, 2002, 23:41:23:

KG> We created a routine that performs the rename without having to open
KG> the main file as the parent and opening the stream directly. It
KG> seems to work!

Great!

KG> What was the reason you felt it needed to be related to the parent
KG> file?

I’m working on a project that almost exclusively uses the native API and
had a similar problem (having the handle to a file and a list of named
streams on the file, I needed to do something with each individual
stream). So I tried a few things to get “from the file to the stream”
and that was the first option that worked, so I stopped looking :slight_smile:

Ralf.