* autoload.cc (NtQueryEaFile): Define.
(NtSetEaFile): Define. * fhandler.cc (fhandler_base::open): Use appropriate open flags in query case when allow_ntea is set. * ntdll.h (struct _FILE_GET_EA_INFORMATION): Define. (struct _FILE_FULL_EA_INFORMATION): Define. (NtQueryEaFile): Declare. (NtSetEaFile): Declare. * ntea.cc (read_ea): Rename from NTReadEA and rewrite using NtQueryEaFile. (write_ea): Rename from NTWriteEA and rewrite using NtSetEaFile. * path.cc (get_symlink_ea): Make static. Add handle parameter to accomodate new read_ea call. (set_symlink_ea): Make static. Add handle parameter to accomodate new write_ea call. (symlink_worker): Call set_symlink_ea while file is still open. (symlink_info::check): Call get_symlink_ea after file has been opened. * security.cc (get_file_attribute): Accomodate new read_ea call. (set_file_attribute): Accomodate new write_ea call. * security.h (read_ea): Change declaration accordingly. (write_ea): Ditto.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/* ntea.cc: code for manipulating NTEA information
|
||||
|
||||
Copyright 1997, 1998, 2000, 2001 Red Hat, Inc.
|
||||
Copyright 1997, 1998, 2000, 2001, 2006 Red Hat, Inc.
|
||||
|
||||
Written by Sergey S. Okhapkin (sos@prospect.com.ru)
|
||||
|
||||
@@ -11,315 +11,162 @@ Cygwin license. Please consult the file "CYGWIN_LICENSE" for
|
||||
details. */
|
||||
|
||||
#include "winsup.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ntdef.h>
|
||||
#include "security.h"
|
||||
#include "ntdll.h"
|
||||
|
||||
/* Default to not using NTEA information */
|
||||
bool allow_ntea;
|
||||
|
||||
/*
|
||||
From Windows NT DDK:
|
||||
|
||||
FILE_FULL_EA_INFORMATION provides extended attribute information.
|
||||
This structure is used primarily by network drivers.
|
||||
|
||||
Members
|
||||
|
||||
NextEntryOffset
|
||||
The offset of the next FILE_FULL_EA_INFORMATION-type entry. This member is
|
||||
zero if no other entries follow this one.
|
||||
|
||||
Flags
|
||||
Can be zero or can be set with FILE_NEED_EA, indicating that the file to which
|
||||
the EA belongs cannot be interpreted without understanding the associated
|
||||
extended attributes.
|
||||
|
||||
EaNameLength
|
||||
The length in bytes of the EaName array. This value does not include a
|
||||
zero-terminator to EaName.
|
||||
|
||||
EaValueLength
|
||||
The length in bytes of each EA value in the array.
|
||||
|
||||
EaName
|
||||
An array of characters naming the EA for this entry.
|
||||
|
||||
Comments
|
||||
This structure is longword-aligned. If a set of FILE_FULL_EA_INFORMATION
|
||||
entries is buffered, NextEntryOffset value in each entry, except the last,
|
||||
falls on a longword boundary.
|
||||
The value(s) associated with each entry follows the EaName array. That is, an
|
||||
EA's values are located at EaName + (EaNameLength + 1).
|
||||
*/
|
||||
|
||||
typedef struct _FILE_FULL_EA_INFORMATION {
|
||||
ULONG NextEntryOffset;
|
||||
UCHAR Flags;
|
||||
UCHAR EaNameLength;
|
||||
USHORT EaValueLength;
|
||||
CHAR EaName[1];
|
||||
} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION;
|
||||
|
||||
/* Functions prototypes */
|
||||
|
||||
int NTReadEA (const char *file, const char *attrname, char *buf, int len);
|
||||
static PFILE_FULL_EA_INFORMATION NTReadEARaw (HANDLE file, int *len);
|
||||
BOOL NTWriteEA(const char *file, const char *attrname, char *buf, int len);
|
||||
|
||||
/*
|
||||
* NTReadEA - read file's Extended Attribute.
|
||||
* read_ea - read file's Extended Attribute.
|
||||
*
|
||||
* Parameters:
|
||||
* file - pointer to filename
|
||||
* attrname- pointer to EA name (case insensitivy. EAs are sored in upper
|
||||
* case).
|
||||
* attrname- pointer to EA name (case insensitiv)
|
||||
* attrbuf - pointer to buffer to store EA's value.
|
||||
* len - length of attrbuf.
|
||||
* Return value:
|
||||
* 0 - if file or attribute "attrname" not found.
|
||||
* N - number of bytes stored in attrbuf if succes.
|
||||
* N - number of bytes stored in attrbuf if success.
|
||||
* -1 - attrbuf too small for EA value.
|
||||
*/
|
||||
|
||||
int __stdcall
|
||||
NTReadEA (const char *file, const char *attrname, char *attrbuf, int len)
|
||||
read_ea (HANDLE hdl, const char *file, const char *attrname, char *attrbuf,
|
||||
int len)
|
||||
{
|
||||
HANDLE hFileSource;
|
||||
int eafound = 0;
|
||||
PFILE_FULL_EA_INFORMATION ea, sea;
|
||||
int easize = 0;
|
||||
IO_STATUS_BLOCK io;
|
||||
|
||||
hFileSource = CreateFile (file, FILE_READ_EA,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
&sec_none_nih, // sa
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL);
|
||||
/* Prepare buffer which receives the result. */
|
||||
ULONG flen = sizeof (FILE_FULL_EA_INFORMATION) + strlen (attrname)
|
||||
+ len + 1;
|
||||
PFILE_FULL_EA_INFORMATION fea = (PFILE_FULL_EA_INFORMATION) alloca (flen);
|
||||
/* Prepare buffer specifying the EA to search for. */
|
||||
ULONG glen = sizeof (FILE_GET_EA_INFORMATION) + strlen (attrname);
|
||||
PFILE_GET_EA_INFORMATION gea = (PFILE_GET_EA_INFORMATION) alloca (glen);
|
||||
gea->NextEntryOffset = 0;
|
||||
gea->EaNameLength = strlen (attrname);
|
||||
strcpy (gea->EaName, attrname);
|
||||
|
||||
if (hFileSource == INVALID_HANDLE_VALUE)
|
||||
return 0;
|
||||
|
||||
/* Read in raw array of EAs */
|
||||
ea = sea = NTReadEARaw (hFileSource, &easize);
|
||||
|
||||
/* Search for requested attribute */
|
||||
while (sea)
|
||||
{
|
||||
if (strcasematch (ea->EaName, attrname)) /* EA found */
|
||||
{
|
||||
if (ea->EaValueLength > len)
|
||||
{
|
||||
eafound = -1; /* buffer too small */
|
||||
break;
|
||||
}
|
||||
memcpy (attrbuf, ea->EaName + (ea->EaNameLength + 1),
|
||||
ea->EaValueLength);
|
||||
eafound = ea->EaValueLength;
|
||||
break;
|
||||
}
|
||||
if ((ea->NextEntryOffset == 0) || ((int) ea->NextEntryOffset > easize))
|
||||
break;
|
||||
ea = (PFILE_FULL_EA_INFORMATION) ((char *) ea + ea->NextEntryOffset);
|
||||
}
|
||||
|
||||
if (sea)
|
||||
free (sea);
|
||||
CloseHandle (hFileSource);
|
||||
|
||||
return eafound;
|
||||
}
|
||||
|
||||
/*
|
||||
* NTReadEARaw - internal routine to read EAs array to malloced buffer. The
|
||||
* caller should free this buffer after usage.
|
||||
* Parameters:
|
||||
* hFileSource - handle to file. This handle should have FILE_READ_EA
|
||||
* rights.
|
||||
* len - pointer to int variable where length of buffer will
|
||||
* be stored.
|
||||
* Return value:
|
||||
* pointer to buffer with file's EAs, or NULL if any error occured.
|
||||
*/
|
||||
|
||||
static PFILE_FULL_EA_INFORMATION
|
||||
NTReadEARaw (HANDLE hFileSource, int *len)
|
||||
{
|
||||
WIN32_STREAM_ID StreamId;
|
||||
DWORD dwBytesWritten;
|
||||
LPVOID lpContext;
|
||||
DWORD StreamSize;
|
||||
PFILE_FULL_EA_INFORMATION eafound = NULL;
|
||||
|
||||
lpContext = NULL;
|
||||
StreamSize = sizeof (WIN32_STREAM_ID) - sizeof (WCHAR**);
|
||||
|
||||
/* Read the WIN32_STREAM_ID in */
|
||||
|
||||
while (BackupRead (hFileSource, (LPBYTE) &StreamId, StreamSize,
|
||||
&dwBytesWritten,
|
||||
FALSE, // don't abort yet
|
||||
FALSE, // don't process security
|
||||
&lpContext))
|
||||
/* If no incoming hdl is given, the loop only runs once, trying to
|
||||
open the file and to query the EA. If an incoming hdl is given,
|
||||
the loop runs twice, first trying to query with the given hdl.
|
||||
If this fails it tries to open the file and to query with that
|
||||
handle again. */
|
||||
HANDLE h = hdl;
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
int ret = 0;
|
||||
while (true)
|
||||
{
|
||||
DWORD sl,sh;
|
||||
|
||||
if (dwBytesWritten == 0) /* No more Stream IDs */
|
||||
break;
|
||||
/* skip StreamName */
|
||||
if (StreamId.dwStreamNameSize)
|
||||
if (!hdl && (h = CreateFile (file, FILE_READ_EA,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
&sec_none_nih, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS, NULL))
|
||||
== INVALID_HANDLE_VALUE)
|
||||
{
|
||||
unsigned char *buf;
|
||||
buf = (unsigned char *) malloc (StreamId.dwStreamNameSize);
|
||||
|
||||
if (buf == NULL)
|
||||
break;
|
||||
|
||||
if (!BackupRead (hFileSource, buf, // buffer to read
|
||||
StreamId.dwStreamNameSize, // num bytes to read
|
||||
&dwBytesWritten,
|
||||
FALSE, // don't abort yet
|
||||
FALSE, // don't process security
|
||||
&lpContext)) // Stream name read error
|
||||
{
|
||||
free (buf);
|
||||
break;
|
||||
}
|
||||
free (buf);
|
||||
debug_printf ("Opening %s for querying EA %s failed, %E",
|
||||
file, attrname);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Is it EA stream? */
|
||||
if (StreamId.dwStreamId == BACKUP_EA_DATA)
|
||||
{
|
||||
unsigned char *buf;
|
||||
buf = (unsigned char *) malloc (StreamId.Size.LowPart);
|
||||
|
||||
if (buf == NULL)
|
||||
break;
|
||||
if (!BackupRead (hFileSource, buf, // buffer to read
|
||||
StreamId.Size.LowPart, // num bytes to write
|
||||
&dwBytesWritten,
|
||||
FALSE, // don't abort yet
|
||||
FALSE, // don't process security
|
||||
&lpContext))
|
||||
{
|
||||
free (buf); /* EA read error */
|
||||
break;
|
||||
}
|
||||
eafound = (PFILE_FULL_EA_INFORMATION) buf;
|
||||
*len = StreamId.Size.LowPart;
|
||||
break;
|
||||
}
|
||||
/* Skip current stream */
|
||||
if (!BackupSeek (hFileSource,
|
||||
StreamId.Size.LowPart,
|
||||
StreamId.Size.HighPart,
|
||||
&sl,
|
||||
&sh,
|
||||
&lpContext))
|
||||
break;
|
||||
status = NtQueryEaFile (h, &io, fea, flen, FALSE, gea, glen, NULL, TRUE);
|
||||
if (NT_SUCCESS (status) || !hdl)
|
||||
break;
|
||||
debug_printf ("1. chance, %x = NtQueryEaFile (%s, %s), Win32 error %d",
|
||||
status, file, attrname, RtlNtStatusToDosError (status));
|
||||
hdl = NULL;
|
||||
}
|
||||
if (!hdl)
|
||||
CloseHandle (h);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
ret = -1;
|
||||
debug_printf ("%x = NtQueryEaFile (%s, %s), Win32 error %d",
|
||||
status, file, attrname, RtlNtStatusToDosError (status));
|
||||
}
|
||||
if (!fea->EaValueLength)
|
||||
ret = 0;
|
||||
else
|
||||
{
|
||||
memcpy (attrbuf, fea->EaName + fea->EaNameLength + 1,
|
||||
fea->EaValueLength);
|
||||
ret = fea->EaValueLength;
|
||||
}
|
||||
|
||||
/* free context */
|
||||
BackupRead (
|
||||
hFileSource,
|
||||
NULL, // buffer to write
|
||||
0, // number of bytes to write
|
||||
&dwBytesWritten,
|
||||
TRUE, // abort
|
||||
FALSE, // don't process security
|
||||
&lpContext);
|
||||
|
||||
return eafound;
|
||||
out:
|
||||
debug_printf ("%d = read_ea (%x, %s, %s, %x, %d)", ret, hdl, file, attrname,
|
||||
attrbuf, len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* NTWriteEA - write file's Extended Attribute.
|
||||
* write_ea - write file's Extended Attribute.
|
||||
*
|
||||
* Parameters:
|
||||
* file - pointer to filename
|
||||
* attrname- pointer to EA name (case insensitivy. EAs are sored in upper
|
||||
* case).
|
||||
* buf - pointer to buffer with EA value.
|
||||
* len - length of buf.
|
||||
* attrname- pointer to EA name (case insensitiv)
|
||||
* attrbuf - pointer to buffer with EA value.
|
||||
* len - length of attrbuf.
|
||||
* Return value:
|
||||
* true if success, false otherwice.
|
||||
* Note: if len=0 given EA will be deleted.
|
||||
*/
|
||||
|
||||
BOOL __stdcall
|
||||
NTWriteEA (const char *file, const char *attrname, const char *buf, int len)
|
||||
write_ea (HANDLE hdl, const char *file, const char *attrname,
|
||||
const char *attrbuf, int len)
|
||||
{
|
||||
HANDLE hFileSource;
|
||||
WIN32_STREAM_ID StreamId;
|
||||
DWORD dwBytesWritten;
|
||||
LPVOID lpContext;
|
||||
DWORD StreamSize, easize;
|
||||
bool bSuccess = false;
|
||||
PFILE_FULL_EA_INFORMATION ea;
|
||||
IO_STATUS_BLOCK io;
|
||||
|
||||
hFileSource = CreateFile (file, FILE_WRITE_EA,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
&sec_none_nih, // sa
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL);
|
||||
/* Prepare buffer specifying the EA to write back. */
|
||||
ULONG flen = sizeof (FILE_FULL_EA_INFORMATION) + strlen (attrname)
|
||||
+ len + 1;
|
||||
PFILE_FULL_EA_INFORMATION fea = (PFILE_FULL_EA_INFORMATION) alloca (flen);
|
||||
fea->NextEntryOffset = 0;
|
||||
fea->Flags = 0;
|
||||
fea->EaNameLength = strlen (attrname);
|
||||
fea->EaValueLength = len;
|
||||
strcpy (fea->EaName, attrname);
|
||||
memcpy (fea->EaName + fea->EaNameLength + 1, attrbuf, len);
|
||||
|
||||
if (hFileSource == INVALID_HANDLE_VALUE)
|
||||
return FALSE;
|
||||
/* If no incoming hdl is given, the loop only runs once, trying to
|
||||
open the file and to set the EA. If an incoming hdl is given,
|
||||
the loop runs twice, first trying to set the EA with the given hdl.
|
||||
If this fails it tries to open the file and to set the EA with that
|
||||
handle again. */
|
||||
HANDLE h = hdl;
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
bool ret = false;
|
||||
while (true)
|
||||
{
|
||||
if (!hdl && (h = CreateFile (file, FILE_READ_EA,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
&sec_none_nih, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS, NULL))
|
||||
== INVALID_HANDLE_VALUE)
|
||||
{
|
||||
debug_printf ("Opening %s for setting EA %s failed, %E",
|
||||
file, attrname);
|
||||
goto out;
|
||||
}
|
||||
status = NtSetEaFile (h, &io, fea, flen);
|
||||
if (NT_SUCCESS (status) || !hdl)
|
||||
break;
|
||||
debug_printf ("1. chance, %x = NtQueryEaFile (%s, %s), Win32 error %d",
|
||||
status, file, attrname, RtlNtStatusToDosError (status));
|
||||
hdl = NULL;
|
||||
}
|
||||
if (!hdl)
|
||||
CloseHandle (h);
|
||||
if (!NT_SUCCESS (status))
|
||||
debug_printf ("%x = NtQueryEaFile (%s, %s), Win32 error %d",
|
||||
status, file, attrname, RtlNtStatusToDosError (status));
|
||||
else
|
||||
ret = true;
|
||||
|
||||
lpContext = NULL;
|
||||
StreamSize = sizeof (WIN32_STREAM_ID) - sizeof (WCHAR**);
|
||||
|
||||
/* FILE_FULL_EA_INFORMATION structure is longword-aligned */
|
||||
easize = sizeof (*ea) - sizeof (WCHAR**) + strlen (attrname) + 1 + len
|
||||
+ (sizeof (DWORD) - 1);
|
||||
easize &= ~(sizeof (DWORD) - 1);
|
||||
|
||||
if ((ea = (PFILE_FULL_EA_INFORMATION) malloc (easize)) == NULL)
|
||||
goto cleanup;
|
||||
|
||||
memset (ea, 0, easize);
|
||||
ea->EaNameLength = strlen (attrname);
|
||||
ea->EaValueLength = len;
|
||||
strcpy (ea->EaName, attrname);
|
||||
memcpy (ea->EaName + (ea->EaNameLength + 1), buf, len);
|
||||
|
||||
StreamId.dwStreamId = BACKUP_EA_DATA;
|
||||
StreamId.dwStreamAttributes = 0;
|
||||
StreamId.Size.HighPart = 0;
|
||||
StreamId.Size.LowPart = easize;
|
||||
StreamId.dwStreamNameSize = 0;
|
||||
|
||||
if (!BackupWrite (hFileSource, (LPBYTE) &StreamId, StreamSize,
|
||||
&dwBytesWritten,
|
||||
FALSE, // don't abort yet
|
||||
FALSE, // don't process security
|
||||
&lpContext))
|
||||
goto cleanup;
|
||||
|
||||
if (!BackupWrite (hFileSource, (LPBYTE) ea, easize,
|
||||
&dwBytesWritten,
|
||||
FALSE, // don't abort yet
|
||||
FALSE, // don't process security
|
||||
&lpContext))
|
||||
goto cleanup;
|
||||
|
||||
bSuccess = true;
|
||||
/* free context */
|
||||
|
||||
cleanup:
|
||||
BackupRead (hFileSource,
|
||||
NULL, // buffer to write
|
||||
0, // number of bytes to write
|
||||
&dwBytesWritten,
|
||||
TRUE, // abort
|
||||
FALSE, // don't process security
|
||||
&lpContext);
|
||||
|
||||
CloseHandle (hFileSource);
|
||||
if (ea)
|
||||
free (ea);
|
||||
|
||||
return bSuccess;
|
||||
out:
|
||||
debug_printf ("%d = write_ea (%x, %s, %s, %x, %d)", ret, hdl, file, attrname,
|
||||
attrbuf, len);
|
||||
return ret;
|
||||
}
|
||||
|
Reference in New Issue
Block a user