VError XMacFileSystemNotification::StartWatchingForChanges( const VFolder &inFolder, VFileSystemNotifier::EventKind inKindFilter, VFileSystemNotifier::IEventHandler *inHandler, sLONG inLatency ) { // We need to get the folder's path into an array for us to pass along to the OS call. VString posixPathString; inFolder.GetPath( posixPathString, FPS_POSIX); VStringConvertBuffer buffer( posixPathString, VTC_UTF_8); std::string posixPath( buffer.GetCPointer()); CFStringRef pathRef = posixPathString.MAC_RetainCFStringCopy(); CFArrayRef pathArray = CFArrayCreate( NULL, (const void **)&pathRef, 1, NULL ); FSEventStreamContext context = { 0 }; // The latency the user passed us is expressed in milliseconds, but the OS call requires the latency to be // expressed in seconds. CFTimeInterval latency = (inLatency / 1000.0); // Now we can make our change data object XMacChangeData *data = new XMacChangeData( inFolder.GetPath(), posixPath, inKindFilter, VTask::GetCurrent(), inHandler, this); context.info = data; data->fStreamRef = FSEventStreamCreate( NULL, &FSEventCallback, &context, pathArray, kFSEventStreamEventIdSinceNow, latency, kFSEventStreamCreateFlagNone ); if (data->fStreamRef == NULL) { CFRelease( pathArray ); CFRelease( pathRef ); ReleaseRefCountable( &data); return MAKE_NATIVE_VERROR( errno ); } // We also need to take an initial snapshot of the directory for us to compare again CreateDirectorySnapshot( posixPath, data->fSnapshot, true ); // Now we can add the data object we just made to our list fOwner->PushChangeData( data); // Next, we can schedule this to run on the main event loop FSEventStreamScheduleWithRunLoop( data->fStreamRef, CFRunLoopGetMain(), kCFRunLoopDefaultMode ); CFRelease( pathArray ); CFRelease( pathRef ); // Now that we've scheduled the stream to run on a helper thread, all that is left is to // start executing the stream VError err; if (FSEventStreamStart( data->fStreamRef )) { err = VE_OK; } else { err = MAKE_NATIVE_VERROR( errno ); } ReleaseRefCountable( &data); return err; }
//static VError XLinuxFileDesc::CreateFile(const char* inPath, FileCreateOptions inCreateOpts) { //////////////////////////////////////////////////////////////////////////////// // // I'm pretty sure it doesn't behave in the same way than the windows platform. // (the share and overwrite behavior might be different) // //////////////////////////////////////////////////////////////////////////////// //utiliser open helper int openFlags=0; if(inCreateOpts & FCR_Overwrite) openFlags|=O_RDONLY|O_CREAT; //CREATE_ALWAYS ? else openFlags|=O_RDONLY|O_CREAT|O_EXCL; //CREATE_NEW mode_t modeFlags=644; //jmo - todo : virer ca ! int fd=open(const_cast<char*>(inPath), openFlags, modeFlags); if(fd<0) return MAKE_NATIVE_VERROR(errno); close(fd); return VE_OK; }
VError XWinFolder::Move( const VFolder& inNewParent, VFolder** outFolder ) const { XBOX::VString name; VFilePath newPath( inNewParent.GetPath() ); DWORD winErr; if (newPath.IsValid()) { VString name; fOwner->GetName(name); newPath.ToSubFolder(name); XWinFullPath oldWinPath( fOwner->GetPath() ); XWinFullPath newWinPath( newPath ); winErr = ::MoveFileW( oldWinPath, newWinPath) ? 0 : ::GetLastError(); } else { winErr = ERROR_INVALID_NAME; } if (outFolder) { *outFolder = (winErr == 0) ? new VFolder( newPath) : NULL; } return MAKE_NATIVE_VERROR( winErr); }
VError XWinFolder::Rename( const VString& inName, VFolder** outFile ) const { VFilePath newPath( fOwner->GetPath() ); newPath.SetName( inName ); DWORD winErr; if (newPath.IsValid()) { XWinFullPath oldWinPath( fOwner->GetPath() ); XWinFullPath newWinPath( newPath ); winErr = ::MoveFileW( oldWinPath, newWinPath ) ? 0 : ::GetLastError(); } else { winErr = ERROR_INVALID_NAME; } if (outFile) { *outFile = (winErr == 0) ? new VFolder( newPath) : NULL; } return MAKE_NATIVE_VERROR( winErr); }
VError XLinuxFileDesc::GetSize(sLONG8 *outSize) const { //There are two easy ways to implement this function : lseek and fstat. //Both are easy and should work. I choose lseek in an arbitrary way. //Utiliser stat helper ! if(!IsValid()) return VE_INVALID_PARAMETER; sLONG8 pos=0; sLONG8 end=0; VError verr=GetPos(&pos); if(verr==VE_OK) verr=SetPos(0, END); else return MAKE_NATIVE_VERROR(errno); if(verr==VE_OK) verr=GetPos(&end); if(verr==VE_OK) { *outSize=end; verr=SetPos(pos, SET); } xbox_assert(verr==VE_OK); //We should always be able to restore seek ptr return verr; }
VError PathBuffer::InitWithCwd() { if(getcwd(fPath, sizeof(fPath))==NULL) return MAKE_NATIVE_VERROR(errno); return VE_OK; }
VError XWinFolder::GetTimeAttributes( VTime* outLastModification, VTime* outCreationTime, VTime* outLastAccess ) const { WIN32_FILE_ATTRIBUTE_DATA info; SYSTEMTIME stime; XWinFullPath path( fOwner->GetPath()); DWORD winErr = ::GetFileAttributesExW( path, GetFileExInfoStandard, &info) ? 0 : ::GetLastError(); if (winErr == 0) { if ( outLastModification ) { ::FileTimeToSystemTime( &info.ftLastWriteTime, &stime); outLastModification->FromUTCTime( stime.wYear, stime.wMonth, stime.wDay, stime.wHour, stime.wMinute, stime.wSecond, stime.wMilliseconds ); } if ( outCreationTime ) { ::FileTimeToSystemTime( &info.ftCreationTime, &stime); outCreationTime->FromUTCTime( stime.wYear, stime.wMonth, stime.wDay, stime.wHour, stime.wMinute, stime.wSecond, stime.wMilliseconds ); } if ( outLastAccess ) { ::FileTimeToSystemTime( &info.ftLastAccessTime, &stime); outLastAccess->FromUTCTime( stime.wYear, stime.wMonth, stime.wDay, stime.wHour, stime.wMinute, stime.wSecond, stime.wMilliseconds ); } } return MAKE_NATIVE_VERROR( winErr); }
VError MapHelper::Map(FileDescSystemRef inFd, VSize inSize) { fSize=inSize; fAddr=mmap(fPreferedAddr, fSize, fProt, fFlags, inFd, fOffset); return (fAddr!=MAP_FAILED) ? VE_OK : MAKE_NATIVE_VERROR(errno); }
VError XLinuxFileDesc::Flush() const { if(!IsValid()) return VE_INVALID_PARAMETER; VTaskLock locker(&fMutex); return (::fsync(fFd) == 0) ? VE_OK : MAKE_NATIVE_VERROR(errno); }
VError XWinFolder::MoveToTrash() const { // don't use XWinFullPath because SHFileOperation fails on any path prefixed with "\\?\". // the path parameter is actually a paths list and must be double null terminated. // so one must copy the path in a temp buffer. UniChar *path = (UniChar*) calloc( fOwner->GetPath().GetPath().GetLength() + 2, sizeof( UniChar)); if (path == NULL) return MAKE_NATIVE_VERROR( ERROR_NOT_ENOUGH_MEMORY); wcscpy( path, fOwner->GetPath().GetPath().GetCPointer()); SHFILEOPSTRUCTW info; memset( &info, 0, sizeof(info)); info.fFlags |= FOF_SILENT; // don't report progress info.fFlags |= FOF_NOERRORUI; // don't report errors info.fFlags |= FOF_NOCONFIRMATION; // don't confirm delete info.fFlags |= FOF_ALLOWUNDO; // move to recycle bin info.fFlags |= FOF_NOCONFIRMMKDIR; info.fFlags |= FOF_RENAMEONCOLLISION; info.wFunc = FO_DELETE; // required: delete operation info.pTo = NULL; // must be NULL info.pFrom = path; VError err; int result = SHFileOperationW( &info); if (result != 0) { // unfortunately, GetLastError() is unusable here. // and the result code is poorly documented. switch( result) { case 0x7C /*DE_INVALIDFILES*/: err = MAKE_NATIVE_VERROR( ERROR_PATH_NOT_FOUND); break; default: err = MAKE_NATIVE_VERROR( ERROR_INVALID_PARAMETER); break; } } else { err = VE_OK; } free( path); return err; }
VError XWinFolder::Delete() const { XWinFullPath path( fOwner->GetPath() ); DWORD winErr = ::RemoveDirectoryW( path) ? 0 : ::GetLastError(); return MAKE_NATIVE_VERROR( winErr); }
VError XWinFolder::SetAttributes( DWORD inAttrb) const { XWinFullPath path( fOwner->GetPath() ); DWORD winErr = ::SetFileAttributesW( path, inAttrb) ? 0 : ::GetLastError(); return MAKE_NATIVE_VERROR( winErr); }
VError MkdirHelper::MakeDir(const PathBuffer& inPath) const { mode_t modeFlags=PERM_755; int res=mkdir(inPath.GetPath(), modeFlags); return (res==0) ? VE_OK : MAKE_NATIVE_VERROR(errno); }
VError XWinFolder::Create() const { XWinFullPath path( fOwner->GetPath() ); // NULL= no security attributes DWORD winErr = ::CreateDirectoryW( path, NULL) ? 0 : ::GetLastError(); return MAKE_NATIVE_VERROR( winErr); }
VError XWinFolder::GetAttributes( DWORD *outAttrb ) const { XWinFullPath path( fOwner->GetPath() ); *outAttrb = ::GetFileAttributesW(path); DWORD winErr = (*outAttrb != INVALID_FILE_ATTRIBUTES ) ? 0 : ::GetLastError(); return MAKE_NATIVE_VERROR( winErr); }
VError PathBuffer::InitWithUniqTmpDir() { int n=snprintf(fPath, sizeof(fPath), "/tmp/Wakanda_XXXXXX"); //A path template with 6 'X' - no more, no less ! xbox_assert(n>0); if(mkdtemp(fPath)==NULL) //Create the directory and replace the 'X' with the actual path return MAKE_NATIVE_VERROR(errno); return VE_OK; }
VError StatHelper::Stat(FileDescSystemRef fd) { int res=fstat(fd, &fStat); if(res!=0) return MAKE_NATIVE_VERROR(errno); fDoesExist=true; //We are sure that the file exists return VE_OK; }
VError XLinuxFileDesc::Flush() const { if(!IsValid()) return VE_INVALID_PARAMETER; int res=fsync(fFd); if(res<0) return MAKE_NATIVE_VERROR(errno); return VE_OK; }
VError XWinFolder::GetVolumeFreeSpace(sLONG8 *outFreeBytes, bool inWithQuotas ) const { VFilePath filePath( fOwner->GetPath() ); XWinFullPath path( filePath); uLONG8 freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes; DWORD winErr = ::GetDiskFreeSpaceExW( path, (PULARGE_INTEGER) &freeBytesAvailable, (PULARGE_INTEGER) &totalNumberOfBytes, (PULARGE_INTEGER) &totalNumberOfFreeBytes) ? 0 : ::GetLastError(); if (winErr == 0) *outFreeBytes = inWithQuotas ? freeBytesAvailable : totalNumberOfFreeBytes; return MAKE_NATIVE_VERROR( winErr); }
VError StatHelper::Stat(const PathBuffer& inPath) { int res=(fFlags&followLinks) ? stat(inPath.GetPath(), &fStat) : lstat(inPath.GetPath(), &fStat); if(res==ENOENT || res==ENOTDIR) fDoesNotExist=true; //We are sure that the file doesn't exists if(res!=0) return MAKE_NATIVE_VERROR(errno); fDoesExist=true; //We are sure that the file exists if(fFlags&withFileSystemStats) { //I put stats on file systems here because they are obtained through a file of the fs. res=statfs(inPath.GetPath(), &fFsStat); if(res!=0) return MAKE_NATIVE_VERROR(errno); } return VE_OK; }
VError PathBuffer::RealPath(PathBuffer* outBuf) const { if(outBuf==NULL) return VE_INVALID_PARAMETER; //2nd arg MUST refer to a buffer capable of storing at least PATH_MAX characters char* res=realpath(fPath, outBuf->fPath); if(res==NULL) return MAKE_NATIVE_VERROR(errno); return VE_OK; }
VError PathBuffer::ReadLink(PathBuffer* outBuf) const { if(outBuf==NULL) return VE_INVALID_PARAMETER; ssize_t n=readlink(fPath, outBuf->fPath, sizeof(outBuf->fPath)); //Doesn't put a trailing 0 if(n==-1) return MAKE_NATIVE_VERROR(errno); if(n==sizeof(outBuf->fPath)) return VE_INVALID_PARAMETER; //The target path is longer than PATH_MAX ? It shouldn't be possible ! return VE_OK; }
VError TouchHelper::Touch(const PathBuffer& inPath, VTime inAccessTime, VTime inModificationTime) const { struct utimbuf buf; memset(&buf, 0, sizeof(buf)); buf.actime=XBoxToUnixTime(inAccessTime); buf.modtime=XBoxToUnixTime(inModificationTime); int res=utime(inPath.GetPath(), &buf); if(res<0) return MAKE_NATIVE_VERROR(errno); return VE_OK; }
VError XWinFolder::GetVolumeCapacity (sLONG8 *outTotalBytes ) const { VFilePath filePath( fOwner->GetPath() ); filePath.ToParent(); XWinFullPath path( filePath); uLONG8 freeBytesAvailable = 0, totalNumberOfBytes = 0, totalNumberOfFreeBytes = 0; DWORD winErr = ::GetDiskFreeSpaceExW( path, (PULARGE_INTEGER) &freeBytesAvailable, (PULARGE_INTEGER) &totalNumberOfBytes, (PULARGE_INTEGER) &totalNumberOfFreeBytes) ? 0 : ::GetLastError(); if (winErr == 0) *outTotalBytes = totalNumberOfBytes; return MAKE_NATIVE_VERROR( winErr); }
VError XLinuxFileDesc::GetData(void *outData, VSize &ioCount, sLONG8 inOffset, bool inFromStart) const { if(!IsValid()) return VE_INVALID_PARAMETER; VTaskLock locker(&fMutex); VError verr = SetPosFromStartWL(fFd, inOffset, inFromStart); if (verr!=VE_OK) { ioCount=0; //According to win implementation return verr; } ssize_t n=0; ssize_t count=0; ssize_t bytes=ioCount; do { if((n=read(fFd, (char*)(outData)+count, bytes-count))<0) { if(errno==EINTR) continue; else { verr=MAKE_NATIVE_VERROR(errno); break; } } count+=n; } while(n!=0); //Callers expect impl. to fail if it can not fill the data buffer... //As a result callers fail if impl. succeed ;) So let's simulate an error ! if(bytes>count) verr=VE_STREAM_EOF; ioCount=count; return verr; }
VError XLinuxFile::Move(const VFilePath& inDestinationPath, VFileSystem *inDestinationFileSystem, VFile** outFile, FileCopyOptions /*inOptions*/) const { VFilePath dstPath(inDestinationPath); if (dstPath.IsFolder()) { VStr255 name; //jmo - todo : NAME_MAX ? fOwner->GetName(name); dstPath.SetFileName(name); } PathBuffer pathBuffer; pathBuffer.Init(dstPath); //First we try to rename the file... VError verr; { RenameHelper rnmHlp; verr=rnmHlp.Rename(fPath, pathBuffer); } //If Rename() fails because src and dst are not on the same fs, we try a Copy() if(verr!=VE_OK && IS_NATIVE_VERROR(verr) && NATIVE_ERRCODE_FROM_VERROR(verr)==EXDEV) { CopyHelper cpHlp; verr = cpHlp.Copy(fPath, pathBuffer); // it's a move not a copy, so one must delete the source after a sucessful copy if (verr == VE_OK) { int res=unlink(fPath.GetPath()); verr = (res==0) ? VE_OK : MAKE_NATIVE_VERROR(errno); } } if (outFile != NULL) { *outFile = (verr == VE_OK) ? new VFile( dstPath, inDestinationFileSystem) : NULL; } return verr; }
VError XWinFolder::SetTimeAttributes( const VTime *inLastModification, const VTime *inCreationTime, const VTime *inLastAccess ) const { WIN32_FILE_ATTRIBUTE_DATA info; SYSTEMTIME lastModification; SYSTEMTIME creationTime; SYSTEMTIME lastAccess; XWinFullPath path( fOwner->GetPath()); DWORD winErr = ::GetFileAttributesExW( path, GetFileExInfoStandard, &info) ? 0 : ::GetLastError(); if (winErr == 0) { if ( inLastModification ) { inLastModification->GetUTCTime( (sWORD&)lastModification.wYear, (sWORD&)lastModification.wMonth, (sWORD&)lastModification.wDay, (sWORD&)lastModification.wHour, (sWORD&)lastModification.wMinute, (sWORD&)lastModification.wSecond, (sWORD&)lastModification.wMilliseconds); ::SystemTimeToFileTime( &lastModification, &info.ftLastWriteTime ); } if ( inCreationTime ) { inCreationTime->GetUTCTime( (sWORD&)creationTime.wYear, (sWORD&)creationTime.wMonth, (sWORD&)creationTime.wDay, (sWORD&)creationTime.wHour, (sWORD&)creationTime.wMinute, (sWORD&)creationTime.wSecond, (sWORD&)creationTime.wMilliseconds); ::SystemTimeToFileTime( &creationTime, &info.ftCreationTime ); } if ( inLastAccess ) { inLastAccess->GetUTCTime( (sWORD&)lastAccess.wYear, (sWORD&)lastAccess.wMonth, (sWORD&)lastAccess.wDay, (sWORD&)lastAccess.wHour, (sWORD&)lastAccess.wMinute, (sWORD&)lastAccess.wSecond, (sWORD&)lastAccess.wMilliseconds); ::SystemTimeToFileTime( &lastAccess, &info.ftLastAccessTime ); } HANDLE fileHandle = ::CreateFileW(path, FILE_WRITE_ATTRIBUTES , 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if ( fileHandle != INVALID_HANDLE_VALUE ) { winErr = ::SetFileTime( fileHandle, &info.ftCreationTime, &info.ftLastAccessTime, &info.ftLastWriteTime ) ? 0 : ::GetLastError(); ::CloseHandle( fileHandle); } else { winErr = ::GetLastError(); } } return MAKE_NATIVE_VERROR( winErr); }
VError XLinuxFileDesc::SetPos(sLONG8 inOffset, Whence inWhence, sLONG8* outLastPos) const { //Warning : If we set the cursor past the end of the file and then write data, we may create // hole in the file... This is ok but it might be confusing : the size reported by // ls may be greater than the sum of allocated blocks. Bytes in holes are read as 0. if(!IsValid()) return VE_INVALID_PARAMETER; int whence; switch(inWhence) { case SET : whence=SEEK_SET; break; case CUR : whence=SEEK_CUR; break; case END: whence=SEEK_END; break; default : xbox_assert(0); return VE_INVALID_PARAMETER; } off_t res=lseek(fFd, inOffset, whence); if(res<0) return MAKE_NATIVE_VERROR(errno); if(outLastPos!=NULL) *outLastPos=res; return VE_OK; }
VError XLinuxFileDesc::PutData(const void *inData, VSize& ioCount, sLONG8 inOffset, bool inFromStart) const { if (!IsValid()) return VE_INVALID_PARAMETER; VTaskLock locker(&fMutex); VError verr = SetPosFromStartWL(fFd, inOffset, inFromStart); if (verr!=VE_OK) { ioCount=0; //According to win implementation return verr; } ssize_t n=0; ssize_t count=0; ssize_t bytes=ioCount; do { if((n=write(fFd, (char*)(inData)+count, bytes-count))<0) { if(errno==EINTR) continue; else { verr=MAKE_NATIVE_VERROR(errno); break; } } count+=n; } while(bytes-count>0); ioCount=count; return verr; }
VError CreateHelper::Create(const PathBuffer& inPath) const { //I'm pretty sure it doesn't behave in the same way than the windows platform. //(the share and overwrite behavior might be different) int openFlags=0; if(fCreateOpts & FCR_Overwrite) openFlags|=O_RDONLY|O_CREAT; //CREATE_ALWAYS ? else openFlags|=O_RDONLY|O_CREAT|O_EXCL; //CREATE_NEW mode_t modeFlags=PERM_644; int fd=open(const_cast<char*>(inPath.GetPath()), openFlags, modeFlags); if(fd<0) return MAKE_NATIVE_VERROR(errno); close(fd); return VE_OK; }