Hello all,

This is a rant with supporting code illustrating the amount of work it takes to change the owner of a file on Windows and on Linux. If you don't want to hear me bitch, skip the rest of this node. If you really don't want to hear it, vote this node down and I'll take the hint. :)

I sent this email to the other (primarily Linux Perl) developers in my company. I thought I'd post it here so all can partake of the schadenfreude:

Here's how you change a file owner in Windows (for which you have to use C):

// #includes omitted for the sake of sanity HANDLE token; char *filename = "somefile.txt"; char *newuser = "someuser"; DWORD len; PSECURITY_DESCRIPTOR security = NULL; PSID sidPtr = NULL; int retValue = 1; // Get the privileges you need if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILE +GES, &token)) { SetPrivilege(token, "SeTakeOwnershipPrivilege", 1); SetPrivilege(token, "SeSecurityPrivilege", 1); SetPrivilege(token, "SeBackupPrivilege", 1); SetPrivilege(token, "SeRestorePrivilege", 1); } else retValue = 0; // Create the security descriptor if (retValue) { GetFileSecurity(filename, OWNER_SECURITY_INFORMATION, secu +rity, 0, &len); security = (PSECURITY_DESCRIPTOR)malloc(len); if (!InitializeSecurityDescriptor(security, SECURITY_DESCRIPTOR_REVISION)) retValue = 0; } // Get the sid for the username if (retValue) { char domainbuf[4096]; DWORD sidSize = 0; DWORD bufSize = 4096; SID_NAME_USE sidUse; LookupAccountName(NULL, newuser, sidPtr, &sidSize, domainb +uf, &bufSize, &sidUse); sid = (PSID)malloc(sidSize); if (!LookupAccountName(NULL, string, (PSID)sid, &sidSize, +domainbuf, &bufSize, &sidUse)) retValue = 0; } } // Set the sid to be the new owner if (retValue && !SetSecurityDescriptorOwner(security, sidPtr, +0)) retValue = 0; // Save the security descriptor if (retValue) retValue = SetFileSecurity(filename, OWNER_SECURITY_INFORM +ATION, security); if (security) free(security); if (sid) free(sid); return retValue;
Here's how you do it on Linux (for which you can use perl):
my $filename = 'somefile'; my $newuser = 'someuser'; my $newuid = getpwname($newuser)[2]; my $oldguid = stat($filename)[5]; return ($newuid && chown($newuid, $oldguid, $filename));
Of course, you could do the Linux in one fairly readable line, if you were so inclined...
my $filename = 'somefile'; my $newuser = 'someuser'; return getpwname($newuser) && chown(getpwnam($newuser)[2], sta +t($filename)[5], $filename);
Some of the code bloat comes from the poorly designed API, and some comes from the memory management you have to do in C. Note also that this procedure is very poorly documented; I had to pick through dozens of code fragments, Microsoft help files, and newgroup entries to get this. I got the perl code in a couple minutes from the camel head book.

The upshot? C takes a long time to develop in and sucks for some things (feel free to show these pieces of code the next time someone asks why C takes longer to develop in than Perl). And Microsoft just plain sucks.


btw: I am aware that there is a Win32::Perms module available that does this. Sadly, the Perms module fails at setting access control lists. I therefore had to write my own, and figured I should have my module do the owner/group stuff as well.