Vault  4.1
vfsnode_platform.cpp
Go to the documentation of this file.
00001 /*
00002 Copyright c1997-2014 Trygve Isaacson. All rights reserved.
00003 This file is part of the Code Vault version 4.1
00004 http://www.bombaydigital.com/
00005 License: MIT. See LICENSE.md in the Vault top level directory.
00006 */
00007 
00010 #include "vfsnode.h"
00011 #include "vtypes_internal.h"
00012 
00013 #include "vexception.h"
00014 #include "vthread.h"
00015 #include <dirent.h>
00016 
00017 #ifdef VPLATFORM_MAC
00018     #ifndef VPLATFORM_MAC_IOS
00019         // There is a conflict between standard and OpenTransport definitions such as TCP_NODELAY. We don't use OT nor want it. This
00020         // only cropped up when turning on precompiled headers. Specifying MAC_OS_X_VERSION_MIN_REQUIRED to MAC_OS_X_VERSION_10_4
00021         // should suppress the OT includes at the end of OSServices.h, but I can't make that work. These three
00022         // defines have the same effect. CoreServices.h is the thing that includes OSServices.h which conditionally
00023         // includes the three OpenTransport*.h files. We need CoreServices.h to use FSFindFolder et al in our directory operations here.
00024         // We also need Carbon.h for GetCurrentProcess and GetProcessBundleLocation in _platform_getExecutable.
00025         #define __OPENTRANSPORT__
00026         #define __OPENTRANSPORTPROVIDERS__
00027         #define __OPENTRANSPORTPROTOCOL__
00028 
00029         // Workaround problem on iOS SDK. Prevent inclusion of WebServicesCore, which presumes inclusion of CFXMLNode.h which does not exist.
00030         #define __WEBSERVICESCORE__
00031 
00032         #include <CoreServices/CoreServices.h>
00033     #endif /* not VPLATFORM_MAC_IOS */
00034 #endif /* VPLATFORM_MAC */
00035 
00036 // static
00037 VString VFSNode::_platform_normalizePath(const VString& path) {
00038     // Paths are already in our "normalized" form on Unix.
00039     return path;
00040 }
00041 
00042 // static
00043 VString VFSNode::_platform_denormalizePath(const VString& path) {
00044     // Paths are already in our "normalized" form on Unix.
00045     return path;
00046 }
00047 
00048 #ifdef VPLATFORM_MAC
00049 
00050 extern VString _V_NSHomeDirectory(); // Implemented in private vtypes_platform.mm Objective-C++ code.
00051 
00052 // static
00053 VFSNode VFSNode::_platform_getKnownDirectoryNode(KnownDirectoryIdentifier id, const VString& companyName, const VString& appName) {
00054     if (id == CURRENT_WORKING_DIRECTORY) {
00055         return VFSNode(VPlatformAPI::getcwd());
00056     }
00057 
00058     if (id == EXECUTABLE_DIRECTORY) {
00059         /*
00060         This depends on the structure of the application or tool.
00061         If it's an iOS application, it's a bundle where we have:
00062             /...../wanted-dir/AppName.app/executable
00063             (2 levels up, wanted-dir is a randomized serial number at some path)
00064         If it's built as a Mac OS X application bundle we have:
00065             /...../wanted-dir/AppName.app/Contents/MacOS/executable
00066             (4 levels up, typically wanted-dir is /Applications if installed, but doesn't have to be)
00067         If it's built as a simple Unix-y tool we have:
00068             /...../wanted-dir/executable
00069             (1 level up, wanted-dir is wherever the tool has been placed)
00070         */
00071 #ifdef VPLATFORM_MAC_IOS
00072         const int NUM_LEVELS_UP = 2;
00073 #else
00074 #ifdef VAULT_MACOSX_APP_IS_BUNDLE
00075         const int NUM_LEVELS_UP = 4;
00076 #else
00077         const int NUM_LEVELS_UP = 1;
00078 #endif
00079 #endif
00080         VFSNode node = VFSNode::getExecutable();
00081         for (int i = 0; i < NUM_LEVELS_UP; ++i) {
00082             VFSNode parentNode;
00083             node.getParentNode(parentNode);
00084             node = parentNode;
00085         }
00086 
00087         return node;
00088     }
00089 
00090     VFSNode currentUserFolder(_V_NSHomeDirectory());
00091 
00092     if (id == USER_HOME_DIRECTORY) {
00093         return currentUserFolder;
00094     }
00095 
00096     VFSNode libraryFolder;
00097     currentUserFolder.getChildNode("Library", libraryFolder);
00098     libraryFolder.mkdir();
00099 
00100     VFSNode subFolder;
00101 
00102     switch (id) {
00103         case USER_HOME_DIRECTORY:
00104             // handled earlier; we returned above
00105             break;
00106 
00107         case LOG_FILES_DIRECTORY:
00108             libraryFolder.getChildNode("Logs", subFolder);
00109             break;
00110 
00111         case USER_PREFERENCES_DIRECTORY:
00112             libraryFolder.getChildNode("Preferences", subFolder);
00113             break;
00114 
00115         case CACHED_DATA_DIRECTORY:
00116             libraryFolder.getChildNode("Caches", subFolder);
00117             break;
00118 
00119         case APPLICATION_DATA_DIRECTORY:
00120             subFolder = libraryFolder;
00121             break;
00122 
00123         case CURRENT_WORKING_DIRECTORY:
00124             // handled earlier; we returned above
00125             break;
00126 
00127         case EXECUTABLE_DIRECTORY:
00128             // handled earlier; we returned above
00129             break;
00130 
00131         default:
00132             throw VStackTraceException(VSTRING_FORMAT("VFSNode::_platform_getKnownDirectoryNode: Requested invalid directory ID %d.", (int) id));
00133             break;
00134     }
00135 
00136     subFolder.mkdir();
00137 
00138     VFSNode companyFolder;
00139     if (companyName.isEmpty()) {
00140         companyFolder = subFolder;
00141     } else {
00142         subFolder.getChildNode(companyName, companyFolder);
00143         companyFolder.mkdir();
00144     }
00145 
00146     VFSNode resultNode;
00147     if (appName.isEmpty()) {
00148         resultNode = companyFolder;
00149     } else {
00150         companyFolder.getChildNode(appName, resultNode);
00151         resultNode.mkdir();
00152     }
00153 
00154     return resultNode;
00155 }
00156 
00157 #include <mach-o/dyld.h> // for _NSGetExecutablePath()
00158 
00159 // static
00160 VFSNode VFSNode::_platform_getExecutable() {
00161     uint32_t bufsize = 3000; // can in theory be bigger than MAXPATH=1024
00162     VString executablePath;
00163     executablePath.preflight((int) bufsize);
00164     char* buffer = executablePath.buffer();
00165     int result = _NSGetExecutablePath(buffer, &bufsize);
00166     if (result == -1) {
00167         throw VStackTraceException(VSTRING_FORMAT("VFSNode::_platform_getExecutable: Failed to get path. _NSGetExecutablePath returned %d.", result));
00168     }
00169 
00170     executablePath.postflight((int) ::strlen(buffer));
00171 
00172     // todo: could then convert to a "real path" in case returned path has sym links
00173     return VFSNode(VFSNode::normalizePath(executablePath)); // must supply normalized form to VFSNode below
00174 }
00175 
00176 #else /* end of Mac OS X implementation of VFSNode::_platform_getKnownDirectoryNode and VFSNode::_platform_getExecutable */
00177 
00178 /* The generic Unix implementation of VFSNode::_platform_getKnownDirectoryNode and VFSNode::_platform_getExecutable follows */
00179 
00180 #include <pwd.h>
00181 
00182 // static
00183 VFSNode VFSNode::_platform_getKnownDirectoryNode(KnownDirectoryIdentifier id, const VString& companyName, const VString& appName) {
00184     if (id == CURRENT_WORKING_DIRECTORY) {
00185         return VFSNode(VSystemAPI::getcwd());
00186     }
00187 
00188     if (id == EXECUTABLE_DIRECTORY) {
00189         VFSNode executable = VFSNode::getExecutable();
00190         VFSNode executableDirectory;
00191         executable.getParentNode(executableDirectory);
00192         return executableDirectory;
00193     }
00194 
00195     struct passwd* pwInfo = ::getpwuid(::getuid()); // Get info about the current user.
00196     if (pwInfo == NULL) {
00197         throw VStackTraceException(
00198             // Oddity: errno 0 can occur and means "no such user".
00199             (errno == 0 ? VSystemError(0, "No such user") : VSystemError()),
00200             "VFSNode::_platform_getKnownDirectoryNode failed to get current user info from getpwuid()."
00201         );
00202     }
00203 
00204     const VString homePath(pwInfo->pw_dir);
00205 
00206     if (id == USER_HOME_DIRECTORY) {
00207         return VFSNode(homePath);
00208     }
00209 
00210     VString basePath;
00211     VString companyFolderName(companyName);
00212 
00213     switch (id) {
00214         case USER_HOME_DIRECTORY:
00215             // handled earlier; we returned above
00216             break;
00217 
00218         case LOG_FILES_DIRECTORY:
00219             basePath = homePath + "/log";
00220             break;
00221 
00222         case USER_PREFERENCES_DIRECTORY:
00223             basePath = homePath;
00224             if (companyName.isNotEmpty()) {
00225                 companyFolderName.format(".%s", companyName.chars());
00226             }
00227             break;
00228 
00229         case CACHED_DATA_DIRECTORY:
00230             basePath = homePath + "/cache";
00231             break;
00232 
00233         case APPLICATION_DATA_DIRECTORY:
00234             basePath = homePath + "/data";
00235             break;
00236 
00237         case CURRENT_WORKING_DIRECTORY:
00238             // handled earlier; we returned above
00239             break;
00240 
00241         case EXECUTABLE_DIRECTORY:
00242             // handled earlier; we returned above
00243             break;
00244 
00245         default:
00246             throw VStackTraceException(VSTRING_FORMAT("VFSNode::_platform_getKnownDirectoryNode: Requested invalid directory ID %d.", (int) id));
00247             break;
00248     }
00249 
00250     VFSNode baseDir(basePath);
00251     baseDir.mkdir();
00252 
00253     VFSNode companyFolder;
00254     if (companyFolderName.isEmpty()) {
00255         companyFolder = baseDir;
00256     } else {
00257         baseDir.getChildNode(companyFolderName, companyFolder);
00258         companyFolder.mkdir();
00259     }
00260 
00261     VFSNode resultNode;
00262     if (appName.isEmpty()) {
00263         resultNode = companyFolder;
00264     } else {
00265         companyFolder.getChildNode(appName, resultNode);
00266         resultNode.mkdir();
00267     }
00268 
00269     return resultNode;
00270 }
00271 
00272 // Assume Linux; conditionalize others:
00273 #ifdef VPLATFORM_UNIX_BSD
00274 static const VString PROCESS_LINKPATH("/proc/curproc/file");
00275 #else
00276 static const VString PROCESS_LINKPATH("/proc/self/exe");
00277 #endif
00278 
00279 // static
00280 VFSNode VFSNode::_platform_getExecutable() {
00281     const int PATH_BUFFER_SIZE = 1024;
00282     VString executablePath;
00283     executablePath.preflight(PATH_BUFFER_SIZE);
00284     ssize_t len = ::readlink(PROCESS_LINKPATH, executablePath.buffer(), PATH_BUFFER_SIZE - 1);
00285     if (len == -1) {
00286         throw VStackTraceException(VSystemError(), "VFSNode::_platform_getExecutable: Unable to determine executable path.");
00287     }
00288 
00289     executablePath.postflight(len);
00290     return VFSNode(VFSNode::normalizePath(executablePath)); // must supply normalized form to VFSNode below
00291 }
00292 
00293 #endif /* end of generic Unix implementation of VFSNode::_platform_getKnownDirectoryNode and VFSNode::_platform_getExecutable */
00294 
00295 bool VFSNode::_platform_getNodeInfo(VFSNodeInfo& info) const {
00296     struct stat statData;
00297     int result = VFileSystem::stat(mPath, &statData);
00298 
00299     if (result >= 0) {
00300         info.mCreationDate = CONST_S64(1000) * static_cast<Vs64>(statData.st_ctime);
00301         info.mModificationDate = CONST_S64(1000) * static_cast<Vs64>(statData.st_mtime);
00302         info.mFileSize = statData.st_size;
00303         info.mIsFile = (! S_ISDIR(statData.st_mode)) && (! S_ISLNK(statData.st_mode));
00304         info.mIsDirectory = (S_ISDIR(statData.st_mode)) || (S_ISLNK(statData.st_mode));
00305         info.mErrNo = 0;
00306     } else {
00307         info.mErrNo = errno;
00308     }
00309 
00310     return (result >= 0);
00311 }
00312 
00313 void VFSNode::_platform_createDirectory() const {
00314     int result = VFileSystem::mkdir(mPath, (S_IFDIR | S_IRWXO | S_IRWXG | S_IRWXU));
00315 
00316     if (result != 0)
00317         throw VException(VSystemError(), VSTRING_FORMAT("VFSNode::_platform_createDirectory failed with result %d for '%s'.", result, mPath.chars()));
00318 }
00319 
00320 bool VFSNode::_platform_removeDirectory() const {
00321     int result = VFileSystem::rmdir(mPath);
00322     return (result == 0);
00323 }
00324 
00325 bool VFSNode::_platform_removeFile() const {
00326     int result = VFileSystem::unlink(mPath);
00327     return (result == 0);
00328 }
00329 
00330 void VFSNode::_platform_renameNode(const VString& newPath) const {
00331     int result = VFileSystem::rename(mPath, newPath);
00332 
00333     if (result != 0)
00334         throw VException(VSystemError(), VSTRING_FORMAT("VFSNode::_platform_renameNode failed with result %d renaming '%s' to '%s'.", result, mPath.chars(), newPath.chars()));
00335 }
00336 
00337 // This is the Unix implementation of directory iteration using
00338 // opendir(), readdir(), closedir() functions.
00339 
00340 void VFSNode::_platform_directoryIterate(VDirectoryIterationCallback& callback) const {
00341     VString nodeName;
00342 
00343     DIR* dir = ::opendir(mPath);
00344 
00345     if (dir == NULL) {
00346         throw VException(VSTRING_FORMAT("VFSNode::_platform_getDirectoryList failed for directory '%s'.", mPath.chars()));
00347     }
00348 
00349     try {
00350         bool keepGoing = true;
00351         struct dirent* entry = ::readdir(dir);
00352 
00353         while (keepGoing && (entry != NULL)) {
00354             VThread::yield(); // be nice if we're iterating over a huge directory
00355 
00356             nodeName.copyFromCString(entry->d_name);
00357 
00358             // Skip current and parent pseudo-entries. Otherwise client must
00359             // know too much detail in order to avoid traversal problems.
00360             if ((nodeName != ".") &&
00361                     (nodeName != "..")) {
00362                 VFSNode childNode;
00363                 this->getChildNode(nodeName, childNode);
00364                 keepGoing = callback.handleNextNode(childNode);
00365             }
00366 
00367             entry = ::readdir(dir);
00368         }
00369     } catch (...) {
00370         ::closedir(dir);
00371         throw;
00372     }
00373 
00374     ::closedir(dir);
00375 }
00376 

Copyright ©1997-2014 Trygve Isaacson. All rights reserved. This documentation was generated with Doxygen.