filemanager/src/inc/fmutils_s60.cpp
changeset 35 060d0b1ab845
parent 30 6e96d2143d46
child 41 fc4654ce4fcb
--- a/filemanager/src/inc/fmutils_s60.cpp	Tue Jul 06 14:06:28 2010 +0300
+++ b/filemanager/src/inc/fmutils_s60.cpp	Wed Aug 18 09:39:39 2010 +0300
@@ -36,7 +36,8 @@
 #include <QFileInfoList>
 #include <QDir>
 #include <QFile>
-
+#include <QIODevice>
+#include <XQConversions>
 #include <hbglobal.h>
 
 #include <xqaiwrequest.h>
@@ -46,6 +47,34 @@
 
 #define BURCONFIGFILE  "z:/private/2002BCC0/burconfig.xml"
 
+
+/*!
+    Used to get drive type for convenience.
+*/
+FmDriverInfo::DriveType FmDriverInfo::driveType()
+{
+    FmDriverInfo::DriveType driveType;
+    if( mDriveState & FmDriverInfo::EDriveRemovable ) {
+        if( mDriveState & FmDriverInfo::EDriveMassStorage ) {
+            driveType = FmDriverInfo::EDriveTypeMassStorage;
+        } else if( mDriveState & FmDriverInfo::EDriveUsbMemory ) {
+            driveType = FmDriverInfo::EDriveTypeUsbMemory;
+        } else if( mDriveState & FmDriverInfo::EDriveRemote ){
+            driveType = FmDriverInfo::EDriveTypeRemote;
+        } else {
+            driveType = FmDriverInfo::EDriveTypeMemoryCard;
+        }
+    } else if( mDriveState & FmDriverInfo::EDriveRom ) {
+        driveType = FmDriverInfo::EDriveTypeRom;
+    } else if( mDriveState & FmDriverInfo::EDriveRam ) {
+        driveType = FmDriverInfo::EDriveTypeRam;
+    } else {
+        driveType = FmDriverInfo::EDriveTypePhoneMemory;
+    }
+    
+    return driveType;
+}
+
 QString FmUtils::getDriveNameFromPath( const QString &path )
 {
     // fillPathWithSplash make sure path length will be at least 3 if it is not empty.
@@ -94,7 +123,7 @@
         }
     }
     
-    if( volumeName == KErrNone || driveInfoErr == KErrNone ) {
+    if( volumeInfoErr == KErrNone || driveInfoErr == KErrNone ) {
         //TDriveInfo driveInfo = volumeInfo.iDrive;
     
         quint32 drvStatus( 0 );
@@ -112,7 +141,12 @@
                 {
                 state |= FmDriverInfo::EDriveUsbMemory;
                 }
-
+            
+            if ( drvStatus & DriveInfo::EDriveRemote )
+                {
+                state |= FmDriverInfo::EDriveRemote;
+                }
+            
             if ( drvStatus & DriveInfo::EDriveRom ){
                 state |= FmDriverInfo::EDriveRom;  
             }
@@ -195,7 +229,7 @@
             "_driveInfoErr:" + QString::number( driveInfoErr ) +
             "_errorCode:" + QString::number( errorCode ) + 
             "_driveSatus:" + QString::number( state ) );
-    FmLogger::log( logString );
+    FM_LOG( logString );
     return FmDriverInfo( volumeInfo.iSize, volumeInfo.iFree, driverName, volumeName, state );
 }
 
@@ -214,20 +248,20 @@
 
 int FmUtils::removeDrivePwd( const QString &driverName,  const QString &Pwd )
 {
-    if( driverName.isEmpty() ) {
+    if( driverName.isEmpty() || Pwd.length() > FmMaxLengthofDrivePassword ) {
         return FmErrWrongParam;
     }
     QString logString = "Drive name:" + driverName;
-    FmLogger::log( logString );
+    FM_LOG( logString );
     logString = "Password:" + Pwd;
-    FmLogger::log( logString );
+    FM_LOG( logString );
 
     CCoeEnv *env = CCoeEnv::Static();
 	RFs& fs = env->FsSession();
 
     TInt drive = 0;
 
-	drive = driverName[0].toUpper().toAscii() - 'A' + EDriveA;
+    drive = driverName[0].toUpper().toAscii() - 'A' + EDriveA;
 
     HBufC* password16 = XQConversions::qStringToS60Desc( Pwd );
     TMediaPassword password;   
@@ -237,10 +271,12 @@
     int err( fs.ClearPassword( drive, password ) );
 
     logString = "Drive:" + QString::number( drive );
-    FmLogger::log( logString );
+    FM_LOG( logString );
 
     logString = "Clear password error:" + QString::number( err );
-    FmLogger::log( logString );
+    FM_LOG( logString );
+
+    delete password16;
 
     if( err == KErrNone ){
         return FmErrNone;   
@@ -255,13 +291,13 @@
 
 int FmUtils::unlockDrive( const QString &driverName,  const QString &Pwd )
 {
-    if( driverName.isEmpty() ) {
+    if( driverName.isEmpty() || Pwd.length() > FmMaxLengthofDrivePassword ) {
         return FmErrWrongParam;
     }
     QString logString = "Drive name:" + driverName;
-    FmLogger::log( logString );
+    FM_LOG( logString );
     logString = "Password:" + Pwd;
-    FmLogger::log( logString );
+    FM_LOG( logString );
 
     CCoeEnv *env = CCoeEnv::Static();
 	RFs& fs = env->FsSession();
@@ -277,9 +313,11 @@
     int err( fs.UnlockDrive( drive, password, ETrue) );
 
     logString = "Drive:" + QString::number( drive );
-    FmLogger::log( logString );
+    FM_LOG( logString );
     logString = "Unlock drive error:" + QString::number( err );
-    FmLogger::log( logString );
+    FM_LOG( logString );
+
+    delete password16;
 
     if( err == KErrNone ){
         return FmErrNone;   
@@ -300,25 +338,26 @@
 
 int FmUtils::checkDrivePwd( const QString &driverName, const QString &pwd )
 {
-    if( driverName.isEmpty() ) {
+    if( driverName.isEmpty() || pwd.length() > FmMaxLengthofDrivePassword ) {
         return FmErrWrongParam;
     }
     QString logString = "checkDrivePwd Drive name:" + driverName;
     logString += " password:" + pwd;
-    FmLogger::log( logString );
+    FM_LOG( logString );
 
     return setDrivePwd( driverName, pwd, pwd );
 }
 
 int FmUtils::setDrivePwd( const QString &driverName, const QString &oldPwd, const QString &newPwd)
 {
-    if( driverName.isEmpty() ) {
+    if( driverName.isEmpty() || 
+        oldPwd.length() > FmMaxLengthofDrivePassword || newPwd.length() > FmMaxLengthofDrivePassword  ) {
         return FmErrWrongParam;
     }
     QString logString = "setDrivePwd Drive name:" + driverName ;
     logString += " Old password:" + oldPwd;
     logString += " New password:" + newPwd;
-    FmLogger::log( logString );
+    FM_LOG( logString );
 
     CCoeEnv *env = CCoeEnv::Static();
 	RFs& fs = env->FsSession();
@@ -341,10 +380,12 @@
     int err( fs.LockDrive( drive, oldPassword, newPassword, ETrue ) );
 
     logString = "Drive:" + QString::number( drive );
-    FmLogger::log( logString );
+    FM_LOG( logString );
     logString = "Password set error:" + QString::number( err );
-    FmLogger::log( logString );
+    FM_LOG( logString );
 
+    delete newPassword16;
+    delete oldPassword16;
     if( err == KErrNone ){
         return FmErrNone;   
     }
@@ -358,10 +399,10 @@
 
 void FmUtils::emptyPwd( QString &pwd )
 {
-    TPtr des ( ( XQConversions::qStringToS60Desc( pwd ) )->Des() );
-    des.FillZ( des.MaxLength() );
-    des.Zero();
-    pwd = XQConversions::s60DescToQString( des );
+    TBuf< FmMaxLengthofDrivePassword > nullPwd;
+    nullPwd.FillZ( nullPwd.MaxLength() );
+    nullPwd.Zero();
+    pwd = XQConversions::s60DescToQString( nullPwd );
 }
 
 int FmUtils::renameDrive( const QString &driverName, const QString &newVolumeName)
@@ -391,7 +432,7 @@
     int err( fs.SetVolumeLabel( newName, drive ));
     
     QString logString = "Rename error:" + QString::number( err );
-    FmLogger::log( logString );
+    FM_LOG( logString );
 
     if( err == KErrNone ){
         return FmErrNone;   
@@ -410,7 +451,7 @@
         return FmErrWrongParam;
     }
     QString logString = "FmUtils::ejectDrive start";
-    FmLogger::log( logString );
+    FM_LOG( logString );
 
     TInt drive = 0;
 	drive = driverName[0].toUpper().toAscii() - 'A' + EDriveA;
@@ -456,7 +497,7 @@
     RFs fs;
     err = fs.Connect();
     
-    QString string( formatPath( folderPath ) );
+    QString string( fillPathWithSplash( folderPath ) );
 
     TPtrC desFolderPath( XQConversions::qStringToS60Desc( string )->Des() );
     TPtrC ptrExtension( XQConversions::qStringToS60Desc( extension )->Des() );
@@ -601,6 +642,11 @@
     CleanupStack::PopAndDestroy( array );
 }
 
+/*!
+    fill splash in the end of \a filePath if the path is not a file
+    All "/" and "\" will be changed to QDir::separator
+    \sa formatPath only changed "/" and "\" to QDir::separator
+*/
 QString FmUtils::fillPathWithSplash( const QString &filePath )
 {
 	QString newFilePath;
@@ -608,18 +654,11 @@
         return newFilePath;
     }
 
-    foreach( QChar ch, filePath ) {
-        if( ch == QChar('\\') || ch == QChar('/') ) {
-			newFilePath.append( QDir::separator() );
-        } else {
-            newFilePath.append( ch );
-        }
-    }
+    newFilePath = formatPath( filePath );
     
     if( newFilePath.right( 1 )!= QDir::separator() ){
         newFilePath.append( QDir::separator() );
     }
-    
     return newFilePath;
 }
 
@@ -667,14 +706,14 @@
 {
     QString logString;
     logString = QString( "checkFolderToDriveFilter: " ) + path;
-    FmLogger::log( logString );
+    FM_LOG( logString );
     QString checkedPath = fillPathWithSplash( path );
 
     logString = QString( "checkFolderToDriveFilter_fillPathWithSplash: " ) + checkedPath;
-    FmLogger::log( logString );
+    FM_LOG( logString );
     
     if( checkedPath.compare( Folder_C_Data, Qt::CaseInsensitive ) == 0 ) {
-        FmLogger::log( QString( " change from c:/data/ to C:/" ) );
+        FM_LOG( QString( " change from c:/data/ to C:/" ) );
         return Drive_C;
     }
     return path;
@@ -684,54 +723,58 @@
 int FmUtils::isPathAccessabel( const QString &path )
 {
     // Used to check if path is accessable, very important feature
-	// and will return filemanager error.
-    FmLogger::log( QString( "isPathAccessabel:" ) + path );
+    // and will return filemanager error.
+    FM_LOG( QString( "isPathAccessabel:" ) + path );
     if( path.isEmpty() ) {
         return FmErrPathNotExist;
     }
-    if( path.length() <= 3 && !isDriveAvailable( path ) ) { //used to filter locked drive
-        FmLogger::log( QString( "isPathAccessabel false: path is drive and not available" ) );
+
+    // used to filter locked/ejected/corrupted drive
+    // check if drive is available, no matter if it is a drive, a folder, or a file.
+    if( !isDriveAvailable( path ) ) {
+        FM_LOG( QString( "isPathAccessabel false: path is drive and not available" ) );
         return FmErrDriveNotAvailable;
     }
+
     QFileInfo fileInfo( path );
     if( fileInfo.absoluteFilePath().contains( Drive_C, Qt::CaseInsensitive ) &&
         !fileInfo.absoluteFilePath().contains( Folder_C_Data, Qt::CaseInsensitive ) ) {
-        FmLogger::log( QString( "isPathAccessabel false: path contain C and not in data folder" ) );
+        FM_LOG( QString( "isPathAccessabel false: path contain C and not in data folder" ) );
         return FmErrPathDenied;
     }
     if( !checkDriveAccessFilter( FmUtils::getDriveNameFromPath( fileInfo.absoluteFilePath() ) ) ){
         return FmErrDriveDenied;
     }
     if( !fileInfo.exists() ) {
-        FmLogger::log( QString( "isPathAccessabel false: path not exist" ) );
+        FM_LOG( QString( "isPathAccessabel false: path not exist" ) );
         return FmErrPathNotExist;
     }
-    FmLogger::log( QString( "isPathAccessabel FmErrNone" ) );
+    FM_LOG( QString( "isPathAccessabel FmErrNone" ) );
     return FmErrNone;
 }
 
 // only used to check drive, when MMC is not inserted, also return false
 bool FmUtils::isDriveAvailable( const QString &path )
 {
-    FmLogger::log( QString( "isDriveAvailable:" ) + path );
+    FM_LOG( QString( "isDriveAvailable:" ) + path );
     if( path.isEmpty() ) {
         return false;
     }
     FmDriverInfo::DriveState driveState = queryDriverInfo( path ).driveState();
     if( ( driveState & FmDriverInfo::EDriveAvailable ) ) {
-        FmLogger::log( QString( "isDriveAvailable true" ) );
+        FM_LOG( QString( "isDriveAvailable true" ) );
         return true;
     }
-    FmLogger::log( QString( "isDriveAvailable false" ) );
+    FM_LOG( QString( "isDriveAvailable false" ) );
     return false;
 }
 
 void FmUtils::getDriveList( QStringList &driveList, bool isHideUnAvailableDrive )
 {
     if( isHideUnAvailableDrive ) {
-        FmLogger::log( QString( "getDriveList HideUnAvailableDrive_true" ) );
+        FM_LOG( QString( "getDriveList HideUnAvailableDrive_true" ) );
     } else {
-        FmLogger::log( QString( "getDriveList HideUnAvailableDrive_false" ) );
+        FM_LOG( QString( "getDriveList HideUnAvailableDrive_false" ) );
     }
     QFileInfoList infoList = QDir::drives();
 
@@ -749,6 +792,10 @@
     return;
 }
 
+/*!
+    fill volume name for \a driveName, with drive letter at the front, for example, C: Phone memory
+    if \a isFillWithDefaultVolume is true, default volume is provided for non-volume drive.
+*/
 QString FmUtils::fillDriveVolume( QString driveName, bool isFillWithDefaultVolume )
 {
     QString ret;
@@ -761,36 +808,73 @@
     
     FmDriverInfo driverInfo = FmUtils::queryDriverInfo( tempDriveName );
     QString volumeName = driverInfo.volumeName();
-
+    
     if( volumeName.isEmpty() && isFillWithDefaultVolume ){
-    FmDriverInfo::DriveState driveState = queryDriverInfo( tempDriveName ).driveState();
-        if( driveState & FmDriverInfo::EDriveAvailable ){
-            if( driveState & FmDriverInfo::EDriveRemovable ) {
-                if( driveState & FmDriverInfo::EDriveMassStorage ) {
-                    // Mass Storage
-                    ret = hbTrId( "txt_fmgr_dblist_1_mass_storage" ).arg( checkedDriveName );  
-                } else if( driveState & FmDriverInfo::EDriveUsbMemory ) {
-                    // USB Memory
-                    ret = hbTrId( "txt_fmgr_dblist_1_usb_memory" ).arg( checkedDriveName );
-                } else{
-                    // Memory Card
-                    ret = hbTrId( "txt_fmgr_dblist_1_memory_card" ).arg( checkedDriveName );
-                }
-            } else{
-                // phone memory
+        switch ( driverInfo.driveType() )
+            {
+            case FmDriverInfo::EDriveTypeMassStorage:
+                ret = hbTrId( "txt_fmgr_dblist_1_mass_storage" ).arg( checkedDriveName );
+                break;
+            case FmDriverInfo::EDriveTypeUsbMemory:
+                ret = hbTrId( "txt_fmgr_dblist_1_usb_memory" ).arg( checkedDriveName );
+                break;
+            case FmDriverInfo::EDriveTypeMemoryCard:
+                ret = hbTrId( "txt_fmgr_dblist_1_memory_card" ).arg( checkedDriveName );
+                break;
+            case FmDriverInfo::EDriveTypePhoneMemory:
                 ret = hbTrId( "txt_fmgr_dblist_1_device_memory" ).arg( checkedDriveName );
-            }
-        }  
+                break;
+            default:
+                Q_ASSERT_X( false, "FmUtils::fillDriveVolume", "please handle drive type");
+                break;
+            }    
     }
     
     if( ret.isEmpty() ) {
-        // set ret as volumeName
+        // ret is not got. fill ret as default method
         // txt_fmgr_dblist_1_2 is not correct, can not use.
         ret = hbTrId( "%1 %2" ).arg( checkedDriveName ).arg( volumeName );
     }
     return ret;
 }
 
+
+/*!
+    return volume name for \a driveName. without drive letter at the front.
+    \a defaultName is set true if default volume name is return for volume name
+*/
+QString FmUtils::getVolumeNameWithDefaultNameIfNull( const QString &diskName, bool &defaultName )
+{
+    FmDriverInfo driverInfo = FmUtils::queryDriverInfo( diskName );
+          
+    QString volumeName = driverInfo.volumeName();    
+    //save the volume status, whether it is default name
+    defaultName = false;
+    //volume name may be null if not set, it will be set at least for one time in the following while cycling.
+    if ( volumeName.isEmpty() ) {
+        defaultName = true;
+        switch ( driverInfo.driveType() )
+            {
+            case FmDriverInfo::EDriveTypeMassStorage:
+                volumeName = hbTrId("Mass storage"); 
+                break;
+            case FmDriverInfo::EDriveTypeUsbMemory:
+                volumeName = hbTrId("USB memory"); 
+                break;
+            case FmDriverInfo::EDriveTypeMemoryCard:
+                volumeName = hbTrId("Memory card");
+                break;
+            case FmDriverInfo::EDriveTypePhoneMemory:
+                volumeName = hbTrId("Device memory");
+                break;
+            default:
+                Q_ASSERT_X( false, "FmUtils::getVolumeNameWithDefaultNameIfNull", "please handle drive type" );
+                break;
+            }   
+    }
+    return volumeName;
+}
+
 int FmUtils::launchFile( const QString &filePath )
 {
     QFile file( filePath );
@@ -875,7 +959,7 @@
 
 QString FmUtils::Localize( const QString &path )
 {
-    QString locPath = formatPath( path );
+    QString locPath = fillPathWithSplash( path );
 
     TPtrC desPath( XQConversions::qStringToS60Desc( locPath )->Des() );
     CDirectoryLocalizer *localizer = CDirectoryLocalizer::NewL();
@@ -888,9 +972,17 @@
     return QString();
 }
 
+/*!
+    All "/" and "\" in \a path will be changed to QDir::separator
+    \sa fillPathWithSplash, fillPathWithSplash will append QDir::separator in the end if path is no a file
+*/
 QString FmUtils::formatPath( const QString &path  )
 {
     QString formatPath;
+    if( path.isEmpty() ) {
+        return formatPath;
+    }
+    
 	foreach( QChar ch, path ) {
 		if( ch == QChar('\\') || ch == QChar('/') ) {
 			formatPath.append( QDir::separator() );
@@ -899,9 +991,6 @@
 		}
     }
 
-    if( formatPath.right( 1 ) != QDir::separator() ){
-        formatPath.append( QDir::separator() );
-    }
     return formatPath;
 }
 
@@ -917,33 +1006,41 @@
     }
     return true;
 }
+
 bool FmUtils::checkFolderFileName( const QString& name )
 {
-    if( name.endsWith( QChar('.'),  Qt::CaseInsensitive ) ) {
+    // trim space firest, because there may be some spaces after "." ,  it is also not valid
+    QString trimmedName( name.trimmed() );
+	if( trimmedName.isEmpty() ) {
+		return false;
+	}
+    if( trimmedName.endsWith( QChar('.'),  Qt::CaseInsensitive ) ) {
         return false;
     }
-    if( name.contains( QChar('\\'), Qt::CaseInsensitive ) ||
-        name.contains( QChar('/'),  Qt::CaseInsensitive ) ||
-        name.contains( QChar(':'),  Qt::CaseInsensitive ) ||
-        name.contains( QChar('*'),  Qt::CaseInsensitive ) ||
-        name.contains( QChar('?'),  Qt::CaseInsensitive ) ||
-        name.contains( QChar('\"'), Qt::CaseInsensitive ) ||
-        name.contains( QChar('<'),  Qt::CaseInsensitive ) ||
-        name.contains( QChar('>'),  Qt::CaseInsensitive ) ||
-        name.contains( QChar('|'),  Qt::CaseInsensitive ) ){
+    if( trimmedName.contains( QChar('\\'), Qt::CaseInsensitive ) ||
+        trimmedName.contains( QChar('/'),  Qt::CaseInsensitive ) ||
+        trimmedName.contains( QChar(':'),  Qt::CaseInsensitive ) ||
+        trimmedName.contains( QChar('*'),  Qt::CaseInsensitive ) ||
+        trimmedName.contains( QChar('?'),  Qt::CaseInsensitive ) ||
+        trimmedName.contains( QChar('\"'), Qt::CaseInsensitive ) ||
+        trimmedName.contains( QChar('<'),  Qt::CaseInsensitive ) ||
+        trimmedName.contains( QChar('>'),  Qt::CaseInsensitive ) ||
+        trimmedName.contains( QChar('|'),  Qt::CaseInsensitive ) ){
         return false;
     }
+    // use orignal name to exam max size of file name
     if( name.length() > KMaxFileName ) {
         return false;
     }
     return true;
 }
 
-bool FmUtils::checkNewFolderOrFile( const QString &path, QString &errString )
+bool FmUtils::checkNewFolderOrFile( const QString &fileName, const QString &path, QString &errString )
 {
+    // first check if fileName is valid, then check if path length is valid, and check if file/foler is existed at last
     QFileInfo fileInfo( path );
     bool ret( true );   
-    if (!FmUtils::checkFolderFileName( fileInfo.fileName() ) ) {
+    if (!FmUtils::checkFolderFileName( fileName ) ) {
         errString = hbTrId( "Invalid file or folder name!" );
         ret = false;
     } else if( !FmUtils::checkMaxPathLength( path ) ) {
@@ -955,3 +1052,62 @@
     }
     return ret;
 }
+
+/*!
+    Check if \a dest is sub level path of \a src
+    Used to check True/False when copy a folder to itself or its subfolder
+    For example, c:\data\test is sub path of c:\data.
+    But c:\data123\test is not sub path of c:\data.
+    So after got right part of path, the first char must be \ or /
+*/
+bool FmUtils::isSubLevelPath( const QString &src, const QString &dest )
+{
+    FM_LOG("FmUtils::isSubFolder: src=" + src + " dest=" + dest);
+    QString checkedSrc( FmUtils::fillPathWithSplash( src ) );
+    QString checkedDest( FmUtils::fillPathWithSplash( dest ) );
+    
+    if( checkedDest.contains( checkedSrc, Qt::CaseInsensitive) &&
+            checkedDest.length() > checkedSrc.length() ) {
+        // for example c:\data\ vs c:\data\123\ 
+        FM_LOG("FmUtils::isSubFolder: true");
+        return true;
+    }
+    // for example c:\data\ vs c:\data\ 
+    // for example c:\data\ vs c:\data123\ 
+
+    FM_LOG("FmUtils::isSubFolder: false");
+    return false;
+}
+
+/*!
+    set the \a desFile attributes as the same with \a srcFile
+*/
+int FmUtils::setFileAttributes( const QString &srcFile, const QString &desFile )
+{
+    RFs fsSession;
+    User::LeaveIfError( fsSession.Connect() ); 
+    CleanupClosePushL( fsSession );
+    RFile64 src;
+    RFile64 des;
+    HBufC *buf1 = XQConversions::qStringToS60Desc( removePathSplash( formatPath( srcFile ) ) );
+    HBufC *buf2 = XQConversions::qStringToS60Desc( removePathSplash( formatPath( desFile ) ) );
+    User::LeaveIfError( src.Open( fsSession, *buf1, EFileRead | EFileShareReadersOnly ) );
+    User::LeaveIfError( des.Open( fsSession, *buf2, EFileWrite | EFileShareExclusive ) );
+    TTime mod;
+    int err = src.Modified( mod );;
+    if ( err == FmErrNone ) {
+        err = des.SetModified( mod );    
+    }
+    TUint att( 0 );
+    if ( err == FmErrNone ) {
+        err = src.Att( att );        
+    }
+    if ( err == FmErrNone ) {
+        des.SetAtt( att, ( ~att ) & KEntryAttMaskSupported );
+    }    
+    src.Close();
+    des.Close();
+    fsSession.Close();
+    CleanupStack::PopAndDestroy(); // fsSession
+    return err;
+}