[cvsnt] Problem with NTFS-Extended Attributes
Andreas Winter
awinter at codewrights.biz
Fri Dec 3 13:16:37 GMT 2004
Hello,
we run a CVSNT-Client on Windows-2000 and XP (2.0.58d) using a
Standard-CVS Server on Linux.
Whenever a file is produced from the CVSNT-Client
(checkout/update/export) this file has so called Extended-Attributes
which are only defined in NTFS. These EAs are not set prior checkin by
us. These EAs are not visible using Explorer and we dont know the
meaning of it. The effect is that such files can not be copied into our
samba fileserver. It creates an empty file on the target dir but stops
with an error about "source may be in use" - which is wrong of course.
They can be copied when the destination is using NTFS or FAT.
When the files are zipped/unzipped the EAs are deleted and the file then
can be copied everywhere.
The question is - is that a bug or a feature of CVSNT?
Can anybody reproduce the behaviour?
Is the EA using configurable (switch off)?
Which information is stored into the EA?
Do we have maybe misconfigured our samba (running unchanged since years)?
Best regards,
Andreas Winter
PS:
Below is the output of a tool showing the attributes (source below)
for a fresh file produced from cvsnt.
stream [0] "":
type: security
size: 176
stream [0] "":
type: data
size: 469
stream [0] "":
type: extended attributes
size: 22
After zipping/unzipping the part with type: extended attributes is missing.
Below is a source we found - which can display the EA in a file.
We found that source during a google session - it is not from us.
---------------------------------------
// ntfsstream.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
typedef unsigned char byte;
#pragma hdrstop
#define err doerr( __FILE__, __LINE__ )
void doerr( const char *file, int line )
{
DWORD e;
e = GetLastError();
if ( e == 0 )
return;
printf( "%s(%d): gle = %lu\n", file, line, e );
exit( 2 );
}
void enableprivs()
{
HANDLE hToken;
byte buf[sizeof TOKEN_PRIVILEGES * 2];
TOKEN_PRIVILEGES & tkp = *( (TOKEN_PRIVILEGES *) buf );
if ( ! OpenProcessToken( GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken ) )
err;
// enable SeBackupPrivilege, SeRestorePrivilege
if ( !LookupPrivilegeValue( NULL, SE_BACKUP_NAME,
&tkp.Privileges[0].Luid ) )
err;
if ( !LookupPrivilegeValue( NULL, SE_RESTORE_NAME,
&tkp.Privileges[1].Luid ) )
err;
tkp.PrivilegeCount = 2;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
tkp.Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges( hToken, FALSE, &tkp, sizeof tkp,
NULL, NULL );
}
void dumphdr( WIN32_STREAM_ID & wsi )
{
const char *p;
printf( "\nstream [%lu] \"%S\":\n", wsi.dwStreamNameSize,
wsi.dwStreamNameSize? wsi.cStreamName: L"" );
switch ( wsi.dwStreamId )
{
case BACKUP_DATA:
p = "data";
break;
case BACKUP_EA_DATA:
p = "extended attributes";
break;
case BACKUP_SECURITY_DATA:
p = "security";
break;
case BACKUP_ALTERNATE_DATA:
p = "other streams";
break;
case BACKUP_LINK:
p = "link";
break;
default:
p = "unknown";
break;
}
printf( " type: %s\n", p );
printf( " size: %I64d\n", wsi.Size.QuadPart );
}
int main( int argc, char *argv[] )
{
HANDLE fh;
if ( argc != 2 )
{
printf( "usage: dump_ntfs_streams {file}\n" );
return 1;
}
// SeBackupPrivilege is not necessary to enumerate streams --
// but it helps if you are an admin/backup-operator and need
// to scan files to which you have no permissions
enableprivs();
fh = CreateFile( argv[1], GENERIC_READ, 0, NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL );
if ( fh == INVALID_HANDLE_VALUE || fh == NULL )
err;
byte buf[4096];
DWORD numread, numtoskip;
void *ctx = NULL;
WIN32_STREAM_ID & wsi = *( (WIN32_STREAM_ID *) buf );
numtoskip = 0;
while ( 1 )
{
// we are at the start of a stream header. read it.
if ( ! BackupRead( fh, buf, 20, &numread, FALSE, TRUE, &ctx ) )
err;
if ( numread == 0 )
break;
if ( wsi.dwStreamNameSize > 0 )
{
if ( ! BackupRead( fh, buf + 20, wsi.dwStreamNameSize, &numread,
FALSE, TRUE, &ctx ) )
err;
if ( numread != wsi.dwStreamNameSize )
break;
}
dumphdr( wsi );
// skip stream data
if ( wsi.Size.QuadPart > 0 )
{
DWORD lo, hi;
BackupSeek( fh, 0xffffffffL, 0x7fffffffL, &lo, &hi, &ctx );
}
}
// make NT release the context
BackupRead( fh, buf, 0, &numread, TRUE, FALSE, &ctx );
CloseHandle( fh );
return 0;
}
---------------------------
Best regards,
Andreas Winter
More information about the cvsnt
mailing list