This is the mail archive of the cygwin mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

File permissions and VSS failure


Summary:

I dropped a long way down the rabbit-hole on this one. I finally found the problem, and what initially seemed like a boneheaded Cygwin bug turns out to be an annoying fact of life. It has to do with file permissions and broken Windows software, specifically Visual Source Safe (VSS). But it also applies to most Windows software that attempts to modify file permissions.

I found the FAQs suggestive but not helpful enough, since they didn't lead me to understanding the problem well enough to work around it with confidence. I probably did not go far enough back in the archives, so if I'm ignoring a brilliant treatise on this subject, I apologize in advance.

Background:

I use tar to package a set of files on Unix boxes (multiple flavors), and 'download' them to my NTFS WinXP Pro box under Cygwin, where I maintain them using Windows-style tools, including Microsoft Visual Source Safe. Then, I 'upload' them back to the Linux box, where they get compiled and executed.

When I 'download' files from Unix, the permissions somehow get messed up, and VSS balks with an error 'Access to file 'whatever' denied." The MS Help claims this is permissions or a file-locking problem. It turns out, it's a permissions problem, but it's deep in the bowels of the software.

Solution:

When Cygwin is done touching the files (e.g. tar), you MUST override the original Unix permissions and make the files writeable under Cygwin. This will cause VSS to complain mightily about writeable files in some instances, but you can either override the complaint manually in VSS, or write a simple Windows program to make all the affected files Read-Only, which you can use AFTER you've used Cygwin chmod to make the file writeable.

This solution also applies to any older Windows software that attempts to make a Read-Only file writeable.

Details:

I eventually reverse-engineered the mechanism that Cygwin uses to "store" its permissions on Windows NTFS. Given the number of queries on the FAQ about file permissions, I was surprised to find few authoritative or detailed responses.

Cygwin creates and uses three NTFS security ACLs on each file it handles, for three different MS security SID values: one is the well-known group SID 'Everyone' (used for the Unix 'all' permissions), one is a group named 'None' (used for the Unix group permissions), and one is a user equal to the current user (used for the Unix user permissions). Each ACL sets separate NTFS permissions on the file analogous to the Unix permissions bits, which not only store the permissions for display, but actually enforce them under NTFS.

The NTFS ACLs provide a lot of permissions for controlling access. For files and directories, the relevant ones are:

00000001 Read Data
00000002 Write Data
00000004 Append Data
00000008 Read Extended Attributes
00000010 Write Extended Attributes
00000020 Execute
00000040 Delete Subdirectory
00000080 Read Attributes
00000010 Write Attributes
00010000 Delete
00020000 Read DAC
00040000 Write DAC
00080000 Write Owner
00100000 Synchronize

00020088 is the minimal pattern for files, present when there are no Unix permissions. Without these (Read EA, Read Attr, Read DAC) you would not be able to determine whether you had permission to discover your permissions. The owner always has a slightly higher minimal set of 001F0110, which allows the owner to change ownership, delete the file, and change all permissions.

The Unix execute bit maps to 00000020 (Execute), read to 00000001 (Read Data), and write to 00000006 (Write and Append Data). So far, so good. Because of the way ACLs interact with each other, there can be a rather complex mess of 'grant' and 'deny' ACLs, but the use of the bits is pretty straightforward.

Now, here's the rub. There is also the "normal" attributes mask, which is what almost all of the generic Windows software, such as VSS, uses exclusively to determine whether the file is writeable. There is a single bit in the normal attributes called Read-Only. This is the bit reflected in the Properties dialog box for the file, as well as the file attributes acquired through Windows calls to GetFileAttributes() and GetFileAttributesEx(), and also the Windows POSIX library _stat() and _chmod() calls. None of these calls will tell you anything at all about the ACLs on the file, and therefore they will tell you nothing about the Cygwin file permissions. Getting into the NTFS ACL calls is the usual slog through the Microsoft maze of horrors, and is not for the faint of heart: see closing notes.

In particular, software like VSS will attempt to modify the file permissions so that it can manage the file. When a file is "checked out" under VSS, it is made writeable, meaning the Read-Only bit is cleared. When it is "checked in", the Read-Only bit is set. However, VSS does not make changes to the NTFS ACL permissions. I doubt that it even knows about them - it's very old, pre-NT software. All it knows is that it "takes control" of the write permissions, makes the file writeable, and then - when it tries to actually write - gets blocked by the NTFS ACL permissions that have been set up by Cygwin and fails.

This seems to be just a fact of life: VSS isn't going to fix this, and there is nothing Cygwin could or should do about it. The solution is to make the file always writeable under Cygwin, and then let VSS have its way with the Read-Only bit in the standard attributes. This is a little awkward when VSS expects to find the file in a Read-Only state after you've handled it with Cygwin, because when Cygwin makes the file writeable (with the ACLs) it also clears the Read-Only bit. However, you can then go through with a simple Windows program that calls the POSIX library _chmod() to make the file Read-Only: it won't affect the ACLs, but it will set the Read-Only bit and keep VSS happy.

Incidentally, setting the Read-Only bit this way will make all of the write permissions in Cygwin appear to go away, which is appropriate, since an attempt to write the Read-Only file will fail. However, if you clear the Read-Only bit, all of the write permissions will come back just exactly the way you had them before, because nothing was changed in the ACLs.

Closing Notes:

If you are as foolish as me and try to get into the Windows side of this, here are a few useful hints.

First, if you are running WinXP Professional, you can gain access to the ACLs through the Windows interface, but it's tricky. They ship WinXP Pro with 'Simple Sharing' turned ON by default, which prevents you from even looking at the ACLs. To turn this OFF, you go to:

Start->MyComputer->Tools|FolderOptions->View(tab)

Down near the bottom of 'Advanced Options' is a box called 'Use Simple File Sharing (recommended)'. Turn this OFF. Now, if you look at the Properties for any file or folder, there will be an extra Security tab available, and you can view and edit the ACLs through this tab.

The notes I saw said that WinXP Home does not allow simple sharing to be turned off. I can only assume this is true.

If you try to do programmatic access to the ACLs, you are in for some pain. Here are some hints: you can't get to the ACLs at all unless you elevate your process token privileges to include SE_SECURITY_NAME - this is somewhat analogous to doing setuid() on Unix, except that under Windows, even root doesn't have root privileges. As is typical, the documentation leads you through this backwards and upside-down, using calls that don't work quite as advertised. Here's a sequence that works:

// You need the process token (not the thread token) to adjust its privileges
if (! OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &token))
<error from GetLastError()>


// You need a magic number corresponding to the SE_SECURITY_NAME text
if (! LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &tkp.Privileges[0].Luid))
	<error from GetLastError()>

// Elevate privileges
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(token, FALSE, &tkp, 0, NULL, NULL);
if ((error = GetLastError()) != 0)
	<error>

// NOW you can get the UID, GID, and DACLs for the file
// I never saw any SACLs on my system, they don't seem relevant to this
// You have to LocalFree(pudesc) when you are done, but not the other pointers
if ((error = GetNamedSecurityInfo(fnam, SE_FILE_OBJECT,
		OWNER_SECURITY_INFORMATION|
		GROUP_SECURITY_INFORMATION|
		DACL_SECURITY_INFORMATION,
		&pusid, &pgsid, &pdacl, NULL, &pudesc)) != 0)
	<error>

// Go back to normal privileges
tkp.Privileges[0].Attributes = 0;
AdjustTokenPrivileges(token, FALSE, &tkp, 0, NULL, NULL);

// Finally, extract the array of ACLs
// You have to LocalFree(pEntries) when you are done
if ((error = GetExplicitEntriesFromAcl(pdacl,&count, &pentries)) != 0)
	<error>

// pusid points to the file owner
// pgsid points to the file group
// pentries[] is an array of 'count' ACL entries for the file

In particular, notice the hodgepodge of different error reporting schemes, and the fact that sometimes 0 means success, sometimes it means failure, and in one case, means pretty much nothing at all. This caught me a couple of times while I was trying to muddle through it.


-- Joe




--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Problem reports:       http://cygwin.com/problems.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]