65.9K
CodeProject is changing. Read more.
Home

How to determine the owner of both local and remote files

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.43/5 (11 votes)

Jun 21, 2006

CPOL

2 min read

viewsIcon

64096

downloadIcon

1075

Code to determine the owner of both local and remote files.

Sample Image - fileowner.png

Introduction

I needed this code for a project I contributed to Blackball, Inc. I found all kinds of examples on the web, but they all fell short for various reasons. One problem was that most of the examples I found did not work on a remote location. Another issue was that many of the examples I found did not properly deal with 'UNC vs. Drive Letter' path issues... including drive letter paths that map to 'Physical Drives vs. Shares'. This code will also work on folders.

Demo Project

The demo project download is a quick way to take a look at how the code can be used.

If you just want to grab the code and start using it in your own project, you need to copy the file name getowner.cpp into your project folder and then add it to your project. The file is dependent on Win32 and RTL libraries.

Points of Interest

The code within the getowner.cpp file is really just a 'C' function and will work in a 'C' project if you move the variable declarations to the top of the function. The function is called GetOwner() and is detailed in Figure 1. The GetOwner() function takes three parameters: the path to the item in question, a pointer to a buffer to contain the result, and the size of the buffer in bytes. The numbered comments within the code spell out the intent of each step. Interesting APIs used are WNetGetUniversalName(), GetFileSecurity(), GetSecurityDescriptorOwner(), and LookupAccountSid().

#include <windows.h> // For the 'Win32' API.
#include <shlwapi.h> // For the 'Path' API.
#include <winnetwk.h> // For the 'WNet' API.


// Function name   : GetOwner

// Description     : Determines the 'Owner' of a given file or folder.

// Return type     : UINT is S_OK if successful; A Win32 'ERROR_' value otherwise.

// Argument        : LPCWSTR szFileOrFolderPathName is the fully
//                   qualified path of the file or folder to examine.

// Argument        : LPWSTR pUserNameBuffer is a pointer to a buffer
//                   used to contain the resulting 'Owner' string.

// Argument        : int nSizeInBytes is the size of the buffer.

UINT GetOwner(LPCWSTR szFileOrFolderPathName, LPWSTR pUserNameBuffer, int nSizeInBytes) {
    // 1) Validate the path:

    // 1.1) Length should not be 0.

    // 1.2) Path must point to an existing file or folder.

    if(!lstrlen(szFileOrFolderPathName) || !PathFileExist(szFileOrFolderPathName))
        return ERROR_INVALID_PARAMETER;

    // 2) Validate the buffer:

    // 2.1) Size must not be 0.

    // 2.2) Pointer must not be NULL.

    if(nSizeInBytes<=0 || pUserNameBuffer==NULL)
        return ERROR_INVALID_PARAMETER;

    // 3) Convert the path to UNC if it is not already UNC
    //    so that we can extract a machine name from it:

    // 3.1) Use a big buffer... some OS's can have a path that is 32768 chars in length.

    WCHAR szUNCPathName[32767] = {0};
    // 3.2) If path is not UNC...

    if(!PathIsUNC(szFileOrFolderPathName)) {
        // 3.3) Mask the big buffer into a UNIVERSAL_NAME_INFO.

        DWORD dwUniSize = 32767*sizeof(WCHAR);
        UNIVERSAL_NAME_INFO* pUNI = (UNIVERSAL_NAME_INFO*)szUNCPathName;
        // 3.4) Attempt to get the UNC version of the path into the big buffer.

        if(!WNetGetUniversalName(szFileOrFolderPathName, 
            UNIVERSAL_NAME_INFO_LEVEL, pUNI, &dwUniSize)) {
            // 3.5) If successful, copy the UNC version into the buffer.

            lstrcpy(szUNCPathName, pUNI->lpUniversalName);
        } else {
            // 3.6) If not successful, copy the original path into the buffer.

            lstrcpy(szUNCPathName,szFileOrFolderPathName);
        }
    } else {
        // 3.7) Path is already UNC, copy the original path into the buffer.

        lstrcpy(szUNCPathName,szFileOrFolderPathName);
    }

    // 4) If path is UNC (will not be the case for local physical
    //    drive paths) we want to extract the machine name:

    // 4.1) Use a buffer bug enough to hold a machine name per Win32.

    WCHAR szMachineName[MAX_COMPUTERNAME_LENGTH+1] = {0};
    // 4.2) If path is UNC...

    if(PathIsUNC(szUNCPathName)) {
        // 4.3) Use PathFindNextComponent() to skip past the double backslashes.

        LPWSTR lpMachineName = PathFindNextComponent(szUNCPathName);
        // 4.4) Walk the the rest of the path to find the end of the machine name.

        int nPos = 0;
        LPWSTR lpNextSlash = lpMachineName;
        while((lpNextSlash[0] != L'\\') && (lpNextSlash[0] != L'\0')) {
            nPos++;
            lpNextSlash++;
        }
        // 4.5) Copyt the machine name into the buffer.

        lstrcpyn(szMachineName, lpMachineName, nPos+1);
    }

    // 5) Derive the 'Owner' by getting the owner's Security ID from
    //    a Security Descriptor associated with the file or folder indicated in the path.

    // 5.1) Get a security descriptor for the file
    //      or folder that contains the Owner Security Information.

    // 5.1.1) Use GetFileSecurity() with some null params to get the required buffer size.

    // 5.1.2) We don't really care about the return value.

    // 5.1.3) The error code must be ERROR_INSUFFICIENT_BUFFER for us to continue.

    unsigned long   uSizeNeeded = 0;
    GetFileSecurity(szUNCPathName, OWNER_SECURITY_INFORMATION, 0, 0, &uSizeNeeded);
    UINT uRet = GetLastError();
    if(uRet==ERROR_INSUFFICIENT_BUFFER && uSizeNeeded) {
        uRet = S_OK; // Clear the ERROR_INSUFFICIENT_BUFFER


        // 5.2) Allocate the buffer for the Security Descriptor, check for out of memory

        LPBYTE lpSecurityBuffer = (LPBYTE) malloc(uSizeNeeded * sizeof(BYTE));
        if(!lpSecurityBuffer) {
            return ERROR_NOT_ENOUGH_MEMORY;
        }

        // 5.2) Get the Security Descriptor that contains
        // the Owner Security Information into the buffer, check for errors

        if(!GetFileSecurity(szUNCPathName, OWNER_SECURITY_INFORMATION, 
                            lpSecurityBuffer, uSizeNeeded, &uSizeNeeded)) {
            free(lpSecurityBuffer);
            return GetLastError();
        }

        // 5.3) Get the the owner's Security ID (SID)
        // from the Security Descriptor, check for errors

        PSID pSID = NULL;
        BOOL bOwnerDefaulted = FALSE;
        if(!GetSecurityDescriptorOwner(lpSecurityBuffer, &pSID, &bOwnerDefaulted)) {
            free(lpSecurityBuffer);
            return GetLastError();
        }
  
        // 5.4) Get the size of the buffers needed
        //      for the owner information (domain and name)

        // 5.4.1) Use LookupAccountSid() with buffer sizes
        //        set to zero to get the required buffer sizes.

        // 5.4.2) We don't really care about the return value.

        // 5.4.3) The error code must be ERROR_INSUFFICIENT_BUFFER for us to continue.

        LPWSTR   pName = NULL;
        LPWSTR   pDomain = NULL;
        unsigned long   uNameLen = 0;
        unsigned long   uDomainLen = 0;
        SID_NAME_USE    sidNameUse = SidTypeUser;
        LookupAccountSid(szMachineName, pSID, pName, &uNameLen, 
                         pDomain, &uDomainLen, &sidNameUse);
        uRet = GetLastError();
        if((uRet==ERROR_INSUFFICIENT_BUFFER) && uNameLen && uDomainLen) {
            uRet = S_OK; // Clear the ERROR_INSUFFICIENT_BUFFER


            // 5.5) Allocate the required buffers, check for out of memory

            pName = (LPWSTR) malloc(uNameLen * sizeof(WCHAR));
            pDomain = (LPWSTR) malloc(uDomainLen * sizeof(WCHAR));
            if(!pName || !pDomain) {
                free(lpSecurityBuffer);
                return ERROR_NOT_ENOUGH_MEMORY;
            }

            // 5.6) Get domain and username

            if(!LookupAccountSid(szMachineName, pSID, pName, 
                      &uNameLen, pDomain, &uDomainLen, &sidNameUse)) {
                free(pName);
                free(pDomain);
                free(lpSecurityBuffer);
                return GetLastError();
            }

            // 5.7) Build the owner string from the domain and username

            if(nSizeBytes > ((uNameLen+uDomainLen+1)*sizeof(WCHAR))) {
                lstrcpy(pUserNameBuffer, pDomain);
                lstrcat(pUserNameBuffer, L"\\");
                lstrcat(pUserNameBuffer, pName);
            } else {
                uRet = ERROR_INSUFFICIENT_BUFFER;

            }

            // 5.8) Release memory
            free(pName);
            free(pDomain);
        }
        // 5.9) Release memory
        free(lpSecurityBuffer);
    }
    return uRet;
}

(Figure 1)

If I left out any details you think should be mentioned in the article, please let me know.

If you could take one last second to rate this article or even leave a comment, it would be much appreciated.

Thanks for reading!