3.7.1 ProblemYou need to resolve the path of a file provided by a user to determine the actual file that it refers to on the filesystem. 3.7.2 SolutionOn Unix systems, use the function realpath( ) to resolve the canonical name of a file or path. On Windows, use the function GetFullPathName( ) to resolve the canonical name of a file or path. 3.7.3 DiscussionYou must be careful when making access decisions for a file. Taking relative pathnames and links into account, it is possible for multiple filenames to refer to the same file. Failure to take this into account when attempting to perform access checks based on filename can have severe consequences. On the surface, resolving the canonical name of a file or path may appear to be a reasonably simple task to undertake. However, many programmers fail to consider symbolic and hard links. On Windows, links are possible, but they are not as serious an issue as they are on Unix because they are much less frequently used. Fortunately, most modern Unix systems provide, as part of the standard C runtime, a function called realpath( ) that will properly resolve the canonical name of a file or path, taking relative paths and links into account. Be careful when using realpath( ) because the function is not thread-safe, and the resolved path is stored in a fixed-size buffer that must be at least MAXPATHLEN bytes in size.
The signature for realpath( ) is: char *realpath(const char *pathname, char resolved_path[MAXPATHLEN]); This function has the following arguments:
If the function fails for any reason, the return value will be NULL, and errno will contain an error code indicating the reason for the failure. If the function is successful, a pointer to resolved_path will be returned. On Windows, there is an equivalent function to realpath( ) called GetFullPathName( ). It will resolve relative paths, link information, and even UNC (Microsoft's Universal Naming Convention) names. The function is more flexible than its Unix counterpart in that it is thread-safe and provides an interface to allow you to dynamically allocate enough memory to hold the resolved canonical path. The signature for GetFullPathName( ) is: DWORD GetFullPathName(LPCTSTR lpFileName, DWORD nBufferLength, LPTSTR lpBuffer, LPTSTR *lpFilePath); This function has the following arguments:
When you initially call GetFullPathName( ), you should specifiy NULL for lpBuffer, and 0 for nBufferLength. When you do this, the return value from GetFullPathName( ) will be the number of characters required to hold the resolved path. After you allocate the necessary buffer space, call GetFullPathName( ) again with nBufferLength and lpBuffer filled in appropriately.
If an error occurs in resolving the path, GetFullPathName( ) will return 0, and you can call GetLastError( ) to determine the cause of the error; otherwise, it will return the number of characters written into lpBuffer. In the following example, SpcResolvePath( ) demonstrates how to use GetFullPathName( ) properly. If it is successful, it will return a dynamically allocated buffer that contains the resolved path; otherwise, it will return NULL. The allocated buffer must be freed by calling LocalFree( ). #include <windows.h> LPTSTR SpcResolvePath(LPCTSTR lpFileName) { DWORD dwLastError, nBufferLength; LPTSTR lpBuffer, lpFilePart; if (!(nBufferLength = GetFullPathName(lpFileName, 0, 0, &lpFilePart))) return 0; if (!(lpBuffer = (LPTSTR)LocalAlloc(LMEM_FIXED, sizeof(TCHAR) * nBufferLength))) return 0; if (!GetFullPathName(lpFileName, nBufferLength, lpBuffer, &lpFilePart)) { dwLastError = GetLastError( ); LocalFree(lpBuffer); SetLastError(dwLastError); return 0; } return lpBuffer; } |