Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Add nfs support - closes #3358 #192

Merged
merged 9 commits into from

3 participants

Memphiz David Robins Ronnie Sahlberg
Memphiz
Owner

Hi folks,

like title says this would add nfs support for linux, osx and ios. The libnfs dependency is already prepared in master and will be built automagically for osx/ios once the darwin build sys is activated. Linux devs should build lib/libnfs and install it before running configure for getting it pickup the nfs support.

Not much to say about it. putting on his helmet for accepting blames

PS: sorry for the WIN32 guys. I did one try to port this over to win32 - but the libnfs rpc stuff broke my neck.

David Robins

Excuse what may seem an ignorant question, but why add NFS support through a library rather than having the user mount the remote share using their mechanism of choice and treat it as local? Permissions and access?

Memphiz
Owner

There are plattforms which can't mount (ios).

Ronnie Sahlberg

Why not mount on the underlying host system?

Several reasons, some better than others :-)
1, On some platforms you cant mount at all.
2, Sometimes you might want to not expose the NFS share outside of the application and not make it visible to the underlying host. this allows the "mount" to be private to the application.
3, Only root can mount a share on the host. This allows non-root users to access the data on the NFS share.
4, When/if ported to Win32, this allows win32 users to access nfs shares out-of-the-box without the hassle of dealing with SFU.

Memphiz
Owner

If nobody of the linux devs spotted any blocker for this, i would like to rebase once more and hit the green button in 2 days.

Memphiz Memphiz merged commit 5db9543 into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jun 18, 2011
  1. Memphiz

    [nfs] - add nfs support (incl. write allowed like smb)

    Memphiz authored
    - using dyloaded libnfs - incl. connection tracking for delayed unload feature
  2. Memphiz
  3. Memphiz
  4. Memphiz
  5. Memphiz
  6. Memphiz

    [cleanup] - after first review

    Memphiz authored
    - removed unneeded header includes
    - removed unused function
    - some cosmetics
    - removed usage of URIUtils::GetParentPath
  7. Memphiz

    [cosmetics] - fixed comments

    Memphiz authored
  8. Memphiz

    silence compile warning

    spiff authored Memphiz committed
  9. Memphiz

    conditionally compile nfs code

    spiff authored Memphiz committed
This page is out of date. Refresh to see the latest.
12 XBMC-ATV2.xcodeproj/project.pbxproj
View
@@ -23,6 +23,8 @@
7C99B7AA134072CD00FC2B16 /* GUIDialogPlayEject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C99B7A8134072CD00FC2B16 /* GUIDialogPlayEject.cpp */; };
C807119F135DB842002F601B /* InputOperations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C807119D135DB842002F601B /* InputOperations.cpp */; };
C8EC5D51136954E400CCC10D /* XBMC_keytable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8EC5D4F136954E400CCC10D /* XBMC_keytable.cpp */; };
+ DF0DF16C13A3AF82008ED511 /* FileNFS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF0DF16813A3AF82008ED511 /* FileNFS.cpp */; };
+ DF0DF16D13A3AF82008ED511 /* NFSDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF0DF16A13A3AF82008ED511 /* NFSDirectory.cpp */; };
F54D9E0712B65FFF006870F9 /* libc.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F54D9E0612B65FFF006870F9 /* libc.dylib */; };
F54D9E8E12B71457006870F9 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F54D9E8D12B71457006870F9 /* CoreAudio.framework */; };
F56B15FB12CD6922009B4C96 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F56B15FA12CD6922009B4C96 /* CoreVideo.framework */; };
@@ -958,6 +960,10 @@
C80711A0135DB848002F601B /* AnnouncementUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AnnouncementUtils.h; sourceTree = "<group>"; };
C8EC5D4F136954E400CCC10D /* XBMC_keytable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = XBMC_keytable.cpp; sourceTree = "<group>"; };
C8EC5D50136954E400CCC10D /* XBMC_keytable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBMC_keytable.h; sourceTree = "<group>"; };
+ DF0DF16813A3AF82008ED511 /* FileNFS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileNFS.cpp; sourceTree = "<group>"; };
+ DF0DF16913A3AF82008ED511 /* FileNFS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileNFS.h; sourceTree = "<group>"; };
+ DF0DF16A13A3AF82008ED511 /* NFSDirectory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NFSDirectory.cpp; sourceTree = "<group>"; };
+ DF0DF16B13A3AF82008ED511 /* NFSDirectory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NFSDirectory.h; sourceTree = "<group>"; };
F54D9E0612B65FFF006870F9 /* libc.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libc.dylib; path = usr/lib/libc.dylib; sourceTree = SDKROOT; };
F54D9E8D12B71457006870F9 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
F56B15FA12CD6922009B4C96 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; };
@@ -3922,6 +3928,10 @@
F56C7395131EC151000AD0F6 /* filesystem */ = {
isa = PBXGroup;
children = (
+ DF0DF16813A3AF82008ED511 /* FileNFS.cpp */,
+ DF0DF16913A3AF82008ED511 /* FileNFS.h */,
+ DF0DF16A13A3AF82008ED511 /* NFSDirectory.cpp */,
+ DF0DF16B13A3AF82008ED511 /* NFSDirectory.h */,
F57A1DBB1329FB0A00498CC7 /* SourcesDirectory.cpp */,
F57A1DBC1329FB0A00498CC7 /* SourcesDirectory.h */,
F56C7396131EC151000AD0F6 /* SpecialProtocol.cpp */,
@@ -6720,6 +6730,8 @@
7C0A7FC813A9E75400AFC2BD /* DirtyRegionSolvers.cpp in Sources */,
7C0A7FC913A9E75400AFC2BD /* DirtyRegionTracker.cpp in Sources */,
7C0A7FCC13A9E76E00AFC2BD /* GUIWindowDebugInfo.cpp in Sources */,
+ DF0DF16C13A3AF82008ED511 /* FileNFS.cpp in Sources */,
+ DF0DF16D13A3AF82008ED511 /* NFSDirectory.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
12 XBMC-IOS.xcodeproj/project.pbxproj
View
@@ -24,6 +24,8 @@
7C99B7BE1340730000FC2B16 /* GUIDialogPlayEject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C99B7BC1340730000FC2B16 /* GUIDialogPlayEject.cpp */; };
C80711AD135DB85F002F601B /* InputOperations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C80711AB135DB85F002F601B /* InputOperations.cpp */; };
C8EC5D26136953E100CCC10D /* XBMC_keytable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8EC5D24136953E100CCC10D /* XBMC_keytable.cpp */; };
+ DF0DF17F13A3AF9F008ED511 /* FileNFS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF0DF17B13A3AF9F008ED511 /* FileNFS.cpp */; };
+ DF0DF18013A3AF9F008ED511 /* NFSDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF0DF17D13A3AF9F008ED511 /* NFSDirectory.cpp */; };
F54D9E8112B713F8006870F9 /* libc.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F54D9E8012B713F8006870F9 /* libc.dylib */; };
F56B143412CAF279009B4C96 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F56B143312CAF279009B4C96 /* CoreVideo.framework */; };
F56B14A512CAF523009B4C96 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F56B14A412CAF523009B4C96 /* AudioToolbox.framework */; };
@@ -958,6 +960,10 @@
C80711AE135DB865002F601B /* AnnouncementUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AnnouncementUtils.h; sourceTree = "<group>"; };
C8EC5D24136953E100CCC10D /* XBMC_keytable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = XBMC_keytable.cpp; sourceTree = "<group>"; };
C8EC5D25136953E100CCC10D /* XBMC_keytable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBMC_keytable.h; sourceTree = "<group>"; };
+ DF0DF17B13A3AF9F008ED511 /* FileNFS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileNFS.cpp; sourceTree = "<group>"; };
+ DF0DF17C13A3AF9F008ED511 /* FileNFS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileNFS.h; sourceTree = "<group>"; };
+ DF0DF17D13A3AF9F008ED511 /* NFSDirectory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NFSDirectory.cpp; sourceTree = "<group>"; };
+ DF0DF17E13A3AF9F008ED511 /* NFSDirectory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NFSDirectory.h; sourceTree = "<group>"; };
F54D9E8012B713F8006870F9 /* libc.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libc.dylib; path = usr/lib/libc.dylib; sourceTree = SDKROOT; };
F56B143312CAF279009B4C96 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; };
F56B14A412CAF523009B4C96 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
@@ -4283,6 +4289,10 @@
F56C8378131F42E8000AD0F6 /* filesystem */ = {
isa = PBXGroup;
children = (
+ DF0DF17B13A3AF9F008ED511 /* FileNFS.cpp */,
+ DF0DF17C13A3AF9F008ED511 /* FileNFS.h */,
+ DF0DF17D13A3AF9F008ED511 /* NFSDirectory.cpp */,
+ DF0DF17E13A3AF9F008ED511 /* NFSDirectory.h */,
F57A1DB61329FAF700498CC7 /* SourcesDirectory.cpp */,
F57A1DB71329FAF700498CC7 /* SourcesDirectory.h */,
F56C8379131F42E8000AD0F6 /* SpecialProtocol.cpp */,
@@ -6737,6 +6747,8 @@
7C0A7F9D13A9E70800AFC2BD /* GUIWindowDebugInfo.cpp in Sources */,
7C0A7FB213A9E72E00AFC2BD /* DirtyRegionSolvers.cpp in Sources */,
7C0A7FB313A9E72E00AFC2BD /* DirtyRegionTracker.cpp in Sources */,
+ DF0DF17F13A3AF9F008ED511 /* FileNFS.cpp in Sources */,
+ DF0DF18013A3AF9F008ED511 /* NFSDirectory.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
15 XBMC.xcodeproj/project.pbxproj
View
@@ -602,6 +602,8 @@
C8D0B2AF1265A9A800F0C0AC /* SystemGlobals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8D0B2AE1265A9A800F0C0AC /* SystemGlobals.cpp */; };
C8D0B2B01265A9A800F0C0AC /* SystemGlobals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8D0B2AE1265A9A800F0C0AC /* SystemGlobals.cpp */; };
C8EC5D0E1369519D00CCC10D /* XBMC_keytable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8EC5D0C1369519D00CCC10D /* XBMC_keytable.cpp */; };
+ DF0DF15B13A3ADA7008ED511 /* FileNFS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF0DF15713A3ADA7008ED511 /* FileNFS.cpp */; };
+ DF0DF15C13A3ADA7008ED511 /* NFSDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF0DF15913A3ADA7008ED511 /* NFSDirectory.cpp */; };
E306D12E0DDF7B590052C2AD /* XBMCHelper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E306D12C0DDF7B590052C2AD /* XBMCHelper.cpp */; };
E33206380D5070AA00435CE3 /* DVDDemuxVobsub.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E33206370D5070AA00435CE3 /* DVDDemuxVobsub.cpp */; };
E33466A60D2E5103005A65EC /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E33466A50D2E5103005A65EC /* IOKit.framework */; };
@@ -2518,6 +2520,10 @@
C8D0B2AE1265A9A800F0C0AC /* SystemGlobals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SystemGlobals.cpp; sourceTree = "<group>"; };
C8EC5D0C1369519D00CCC10D /* XBMC_keytable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = XBMC_keytable.cpp; sourceTree = "<group>"; };
C8EC5D0D1369519D00CCC10D /* XBMC_keytable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBMC_keytable.h; sourceTree = "<group>"; };
+ DF0DF15713A3ADA7008ED511 /* FileNFS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileNFS.cpp; sourceTree = "<group>"; };
+ DF0DF15813A3ADA7008ED511 /* FileNFS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileNFS.h; sourceTree = "<group>"; };
+ DF0DF15913A3ADA7008ED511 /* NFSDirectory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NFSDirectory.cpp; sourceTree = "<group>"; };
+ DF0DF15A13A3ADA7008ED511 /* NFSDirectory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NFSDirectory.h; sourceTree = "<group>"; };
E306D12C0DDF7B590052C2AD /* XBMCHelper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = XBMCHelper.cpp; sourceTree = "<group>"; };
E306D12D0DDF7B590052C2AD /* XBMCHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBMCHelper.h; sourceTree = "<group>"; };
E33206370D5070AA00435CE3 /* DVDDemuxVobsub.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DVDDemuxVobsub.cpp; sourceTree = "<group>"; };
@@ -5521,6 +5527,10 @@
E38E16940D25F9FA00618676 /* filesystem */ = {
isa = PBXGroup;
children = (
+ DF0DF15713A3ADA7008ED511 /* FileNFS.cpp */,
+ DF0DF15813A3ADA7008ED511 /* FileNFS.h */,
+ DF0DF15913A3ADA7008ED511 /* NFSDirectory.cpp */,
+ DF0DF15A13A3ADA7008ED511 /* NFSDirectory.h */,
7C84A59C12FA3C1600CD1714 /* SourcesDirectory.cpp */,
7C84A59D12FA3C1600CD1714 /* SourcesDirectory.h */,
7C2D6AE20F35453E00DD2E85 /* SpecialProtocol.cpp */,
@@ -8063,6 +8073,11 @@
F558F27B13ABD56600631E12 /* DirtyRegionSolvers.cpp in Sources */,
F558F27F13ABD57400631E12 /* DirtyRegionTracker.cpp in Sources */,
F558F29613ABD7DF00631E12 /* GUIWindowDebugInfo.cpp in Sources */,
+ 7C0A7F7C13A9E69A00AFC2BD /* DirtyRegionSolvers.cpp in Sources */,
+ 7C0A7F7D13A9E69A00AFC2BD /* DirtyRegionTracker.cpp in Sources */,
+ 7C0A7F8213A9E6C600AFC2BD /* GUIWindowDebugInfo.cpp in Sources */,
+ DF0DF15B13A3ADA7008ED511 /* FileNFS.cpp in Sources */,
+ DF0DF15C13A3ADA7008ED511 /* NFSDirectory.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
4 configure.in
View
@@ -830,6 +830,7 @@ if test "$use_libnfs" != "no"; then
AC_CHECK_HEADERS([nfsc/libnfs.h],,
[if test "$use_libnfs" = "yes"; then
AC_MSG_ERROR($libnfs_not_found)
+ USE_LIBNFS=0
elif test "$use_libnfs" != "no"; then
AC_MSG_NOTICE($libnfs_not_found)
use_libnfs="no"
@@ -840,8 +841,10 @@ if test "$use_libnfs" != "no"; then
fi
if test "$use_libnfs" != "no"; then
AC_DEFINE([HAVE_LIBNFS], [1], [Whether to use libnfs library.])
+ USE_LIBNFS=1
fi
else
+ USE_LIBNFS=0
AC_MSG_NOTICE($libnfs_disabled)
fi
@@ -1490,6 +1493,7 @@ AC_SUBST(USE_OPENGLES)
AC_SUBST(USE_VDPAU)
AC_SUBST(USE_VAAPI)
AC_SUBST(USE_CRYSTALHD)
+AC_SUBST(USE_LIBNFS)
AC_SUBST(USE_VDA)
AC_SUBST(USE_OPENMAX)
AC_SUBST(USE_PULSE)
7 xbmc/Application.cpp
View
@@ -99,6 +99,9 @@
#if defined(_LINUX) && defined(HAS_FILESYSTEM_SMB)
#include "filesystem/SMBDirectory.h"
#endif
+#ifdef HAS_FILESYSTEM_NFS
+#include "filesystem/FileNFS.h"
+#endif
#ifdef HAS_FILESYSTEM_SFTP
#include "filesystem/FileSFTP.h"
#endif
@@ -4796,6 +4799,10 @@ void CApplication::ProcessSlow()
#if defined(_LINUX) && defined(HAS_FILESYSTEM_SMB)
smb.CheckIfIdle();
#endif
+
+#ifdef HAS_FILESYSTEM_NFS
+ gNfsConnection.CheckIfIdle();
+#endif
#ifdef HAS_FILESYSTEM_SFTP
CSFTPSessionManager::ClearOutIdleSessions();
6 xbmc/FileItem.cpp
View
@@ -759,6 +759,11 @@ bool CFileItem::IsOnDVD() const
return URIUtils::IsOnDVD(m_strPath) || m_iDriveType == CMediaSource::SOURCE_TYPE_DVD;
}
+bool CFileItem::IsNfs() const
+{
+ return URIUtils::IsNfs(m_strPath);
+}
+
bool CFileItem::IsOnLAN() const
{
return URIUtils::IsOnLAN(m_strPath);
@@ -1999,6 +2004,7 @@ void CFileItemList::Stack()
// 1. rars and zips may be on slow sources? is this supposed to be allowed?
if( !item->IsRemote()
|| item->IsSmb()
+ || item->IsNfs()
|| URIUtils::IsInRAR(item->m_strPath)
|| URIUtils::IsInZIP(item->m_strPath)
)
1  xbmc/FileItem.h
View
@@ -116,6 +116,7 @@ class CFileItem :
bool IsOnDVD() const;
bool IsOnLAN() const;
bool IsHD() const;
+ bool IsNfs() const;
bool IsRemote() const;
bool IsSmb() const;
bool IsURL() const;
8 xbmc/Util.cpp
View
@@ -1145,8 +1145,8 @@ bool CUtil::CreateDirectoryEx(const CStdString& strPath)
// return true if directory already exist
if (CDirectory::Exists(strPath)) return true;
- // we currently only allow HD and smb paths
- if (!URIUtils::IsHD(strPath) && !URIUtils::IsSmb(strPath))
+ // we currently only allow HD and smb and nfs paths
+ if (!URIUtils::IsHD(strPath) && !URIUtils::IsSmb(strPath) && !URIUtils::IsNfs(strPath))
{
CLog::Log(LOGERROR,"%s called with an unsupported path: %s", __FUNCTION__, strPath.c_str());
return false;
@@ -1865,11 +1865,13 @@ bool CUtil::MakeShortenPath(CStdString StrInput, CStdString& StrOutput, int iTex
bool CUtil::SupportsFileOperations(const CStdString& strPath)
{
- // currently only hd and smb support delete and rename
+ // currently only hd, smb and nfs support delete and rename
if (URIUtils::IsHD(strPath))
return true;
if (URIUtils::IsSmb(strPath))
return true;
+ if (URIUtils::IsNfs(strPath))
+ return true;
if (URIUtils::IsMythTV(strPath))
{
/*
7 xbmc/filesystem/FactoryDirectory.cpp
View
@@ -93,6 +93,9 @@
#ifdef HAS_FILESYSTEM_SFTP
#include "SFTPDirectory.h"
#endif
+#ifdef HAS_FILESYSTEM_NFS
+#include "NFSDirectory.h"
+#endif
using namespace XFILE;
@@ -184,6 +187,10 @@ IDirectory* CFactoryDirectory::Create(const CStdString& strPath)
#ifdef HAS_ZEROCONF
if (strProtocol == "zeroconf") return new CZeroconfDirectory();
#endif
+#ifdef HAS_FILESYSTEM_NFS
+ if (strProtocol == "nfs") return new CNFSDirectory();
+#endif
+
}
CLog::Log(LOGWARNING, "%s - Unsupported protocol(%s) in %s", __FUNCTION__, strProtocol.c_str(), url.Get().c_str() );
8 xbmc/filesystem/FileFactory.cpp
View
@@ -62,6 +62,10 @@
#ifdef HAS_FILESYSTEM_SFTP
#include "FileSFTP.h"
#endif
+#ifdef HAS_FILESYSTEM_NFS
+#include "FileNFS.h"
+#endif
+
#include "FileMusicDatabase.h"
#include "FileSpecialProtocol.h"
#include "MultiPathFile.h"
@@ -154,6 +158,10 @@ IFile* CFileFactory::CreateLoader(const CURL& url)
#ifdef HAS_FILESYSTEM_VTP
else if (strProtocol == "vtp") return new CVTPFile();
#endif
+#ifdef HAS_FILESYSTEM_NFS
+ else if (strProtocol == "nfs") return new CFileNFS();
+#endif
+
}
CLog::Log(LOGWARNING, "%s - Unsupported protocol(%s) in %s", __FUNCTION__, strProtocol.c_str(), url.Get().c_str() );
541 xbmc/filesystem/FileNFS.cpp
View
@@ -0,0 +1,541 @@
+/*
+ * Copyright (C) 2011 Team XBMC
+ * http://www.xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+// FileNFS.cpp: implementation of the CFileNFS class.
+//
+//////////////////////////////////////////////////////////////////////
+#include "system.h"
+
+#ifdef HAS_FILESYSTEM_NFS
+#include "DllLibNfs.h"
+#include "FileNFS.h"
+#include "threads/SingleLock.h"
+#include "utils/log.h"
+#include "utils/URIUtils.h"
+
+using namespace XFILE;
+
+CNfsConnection::CNfsConnection()
+: m_pNfsContext(NULL)
+, m_shareName("")
+, m_readChunkSize(0)
+, m_writeChunkSize(0)
+, m_OpenConnections(0)
+, m_IdleTimeout(0)
+, m_pLibNfs(new DllLibNfs())
+{
+}
+
+CNfsConnection::~CNfsConnection()
+{
+ delete m_pLibNfs;
+}
+
+void CNfsConnection::resetContext()
+{
+
+ if(!m_pLibNfs->IsLoaded())
+ {
+ if(!m_pLibNfs->Load())
+ {
+ CLog::Log(LOGERROR,"NFS: Error loading libnfs (%s).",__FUNCTION__);
+ return;//FATAL!
+ }
+ }
+
+ if(m_pNfsContext)
+ {
+ m_pLibNfs->nfs_destroy_context(m_pNfsContext);
+ }
+
+ m_pNfsContext = m_pLibNfs->nfs_init_context();
+
+ if (!m_pNfsContext)
+ {
+ CLog::Log(LOGERROR,"NFS: Error initcontext in resetContext.");
+ }
+ m_writeChunkSize = 0;
+ m_readChunkSize = 0;
+ m_shareName.clear();
+ m_hostName.clear();
+}
+
+bool CNfsConnection::Connect(const CURL& url)
+{
+ CSingleLock lock(*this);
+ int ret = 0;
+ CStdString share;
+ URIUtils::GetDirectory(url.GetFileName(),share);
+ share = "/" + share;
+
+ if(!share.Equals(m_shareName,true) || !url.GetHostName().Equals(m_hostName,false) )
+ {
+ resetContext();//we need a new context because sharename or hostname has changed - old context will be freed
+
+ //we connect to the directory of the path. This will be the "root" path of this connection then.
+ //So all fileoperations are relative to this mountpoint...
+ ret = m_pLibNfs->nfs_mount_sync(m_pNfsContext, url.GetHostName().c_str(), share.c_str());
+
+ if (ret != 0)
+ {
+ CLog::Log(LOGERROR,"NFS: Failed to mount nfs share: %s\n", m_pLibNfs->nfs_get_error(m_pNfsContext));
+ return false;
+ }
+ m_shareName = share;
+ m_hostName = url.GetHostName();
+ m_readChunkSize = m_pLibNfs->nfs_get_readmax(m_pNfsContext);
+ m_writeChunkSize = m_pLibNfs->nfs_get_writemax(m_pNfsContext);
+ CLog::Log(LOGDEBUG,"NFS: Connected to server %s and export %s (chunks: r/w %i/%i)\n", url.GetHostName().c_str(), url.GetShareName().c_str(),(int)m_readChunkSize,(int)m_writeChunkSize);
+ }
+
+ return true;
+}
+
+void CNfsConnection::Deinit()
+{
+ if(m_pNfsContext)
+ {
+ m_pLibNfs->nfs_destroy_context(m_pNfsContext);
+ }
+ m_pNfsContext = NULL;
+ m_shareName.clear();
+ m_hostName.clear();
+ m_pLibNfs->Unload();
+}
+
+/* This is called from CApplication::ProcessSlow() and is used to tell if nfs have been idle for too long */
+void CNfsConnection::CheckIfIdle()
+{
+ /* We check if there are open connections. This is done without a lock to not halt the mainthread. It should be thread safe as
+ worst case scenario is that m_OpenConnections could read 0 and then changed to 1 if this happens it will enter the if wich will lead to another check, wich is locked. */
+ if (m_OpenConnections == 0 && m_pNfsContext != NULL)
+ { /* I've set the the maxiumum IDLE time to be 1 min and 30 sec. */
+ CSingleLock lock(*this);
+ if (m_OpenConnections == 0 /* check again - when locked */)
+ {
+ if (m_IdleTimeout > 0)
+ {
+ m_IdleTimeout--;
+ }
+ else
+ {
+ CLog::Log(LOGNOTICE, "NFS is idle. Closing the remaining connections.");
+ gNfsConnection.Deinit();
+ }
+ }
+ }
+}
+
+void CNfsConnection::SetActivityTime()
+{
+ /* Since we get called every 500ms from ProcessSlow we limit the tick count to 180 */
+ /* That means we have 2 ticks per second which equals 180/2 == 90 seconds */
+ m_IdleTimeout = 180;
+}
+
+
+/* The following two function is used to keep track on how many Opened files/directories there are.
+needed for unloading the dylib*/
+void CNfsConnection::AddActiveConnection()
+{
+ CSingleLock lock(*this);
+ m_OpenConnections++;
+}
+
+void CNfsConnection::AddIdleConnection()
+{
+ CSingleLock lock(*this);
+ m_OpenConnections--;
+ /* If we close a file we reset the idle timer so that we don't have any wierd behaviours if a user
+ leaves the movie paused for a long while and then press stop */
+ m_IdleTimeout = 180;
+}
+
+
+CNfsConnection gNfsConnection;
+
+CFileNFS::CFileNFS()
+: m_fileSize(0)
+, m_pFileHandle(NULL)
+{
+ gNfsConnection.AddActiveConnection();
+}
+
+CFileNFS::~CFileNFS()
+{
+ Close();
+ gNfsConnection.AddIdleConnection();
+}
+
+int64_t CFileNFS::GetPosition()
+{
+ int ret = 0;
+ off_t offset = 0;
+ CSingleLock lock(gNfsConnection);
+
+ if (gNfsConnection.GetNfsContext() == NULL || m_pFileHandle == NULL) return 0;
+
+ ret = (int)gNfsConnection.GetImpl()->nfs_lseek_sync(gNfsConnection.GetNfsContext(), m_pFileHandle, 0, SEEK_CUR, &offset);
+
+ if (ret < 0)
+ {
+ CLog::Log(LOGERROR, "NFS: Failed to lseek(%s)",gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ }
+ return offset;
+}
+
+int64_t CFileNFS::GetLength()
+{
+ if (m_pFileHandle == NULL) return 0;
+ return m_fileSize;
+}
+
+bool CFileNFS::Open(const CURL& url)
+{
+ int ret = 0;
+ Close();
+ // we can't open files like nfs://file.f or nfs://server/file.f
+ // if a file matches the if below return false, it can't exist on a nfs share.
+ if (!IsValidFile(url.GetFileName()))
+ {
+ CLog::Log(LOGNOTICE,"NFS: Bad URL : '%s'",url.GetFileName().c_str());
+ return false;
+ }
+
+ CStdString filename = "//" + URIUtils::GetFileName(url.GetFileName());
+
+ CSingleLock lock(gNfsConnection);
+
+ if(!gNfsConnection.Connect(url))
+ return false;
+
+ ret = gNfsConnection.GetImpl()->nfs_open_sync(gNfsConnection.GetNfsContext(), filename.c_str(), O_RDONLY, &m_pFileHandle);
+
+ if (ret != 0)
+ {
+ CLog::Log(LOGINFO, "CFileNFS::Open: Unable to open file : '%s' error : '%s'", url.GetFileName().c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ return false;
+ }
+
+ CLog::Log(LOGDEBUG,"CFileNFS::Open - opened %s",url.GetFileName().c_str());
+ m_url=url;
+
+#ifdef _LINUX
+ struct __stat64 tmpBuffer;
+#else
+ struct stat tmpBuffer;
+#endif
+ if( Stat(&tmpBuffer) )
+ {
+ m_url.Reset();
+ Close();
+ return false;
+ }
+
+ m_fileSize = tmpBuffer.st_size;//cache the size of this file
+ // We've successfully opened the file!
+ return true;
+}
+
+
+bool CFileNFS::Exists(const CURL& url)
+{
+ return Stat(url,NULL) == 0;
+}
+
+int CFileNFS::Stat(struct __stat64* buffer)
+{
+ return Stat(m_url,buffer);
+}
+
+
+int CFileNFS::Stat(const CURL& url, struct __stat64* buffer)
+{
+ int ret = 0;
+ CSingleLock lock(gNfsConnection);
+
+ if(!gNfsConnection.Connect(url))
+ return -1;
+
+ CStdString filename = "//" + URIUtils::GetFileName(url.GetFileName());
+
+ struct stat tmpBuffer = {0};
+
+ ret = gNfsConnection.GetImpl()->nfs_stat_sync(gNfsConnection.GetNfsContext(), filename.c_str(), &tmpBuffer);
+
+ //if buffer == NULL we where called from Exists - in that case don't spam the log with errors
+ if (ret != 0 && buffer != NULL)
+ {
+ CLog::Log(LOGERROR, "NFS: Failed to stat(%s) %s\n", url.GetFileName().c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ ret = -1;
+ }
+ else
+ {
+ if(buffer)
+ {
+ memset(buffer, 0, sizeof(struct __stat64));
+ buffer->st_dev = tmpBuffer.st_dev;
+ buffer->st_ino = tmpBuffer.st_ino;
+ buffer->st_mode = tmpBuffer.st_mode;
+ buffer->st_nlink = tmpBuffer.st_nlink;
+ buffer->st_uid = tmpBuffer.st_uid;
+ buffer->st_gid = tmpBuffer.st_gid;
+ buffer->st_rdev = tmpBuffer.st_rdev;
+ buffer->st_size = tmpBuffer.st_size;
+ buffer->st_atime = tmpBuffer.st_atime;
+ buffer->st_mtime = tmpBuffer.st_mtime;
+ buffer->st_ctime = tmpBuffer.st_ctime;
+ }
+ }
+ return ret;
+}
+
+unsigned int CFileNFS::Read(void *lpBuf, int64_t uiBufSize)
+{
+ int numberOfBytesRead = 0;
+ int bytesLeft = uiBufSize;
+ int bytesRead = 0;
+ int chunkSize = gNfsConnection.GetMaxReadChunkSize();
+ CSingleLock lock(gNfsConnection);
+
+ if (m_pFileHandle == NULL || gNfsConnection.GetNfsContext()==NULL ) return 0;
+
+ //read chunked since nfs will only give server specific packet sizes at once
+ while(bytesLeft)
+ {
+ //last chunk could be smaller then chunk size
+ if(bytesLeft < chunkSize)
+ {
+ chunkSize = bytesLeft;
+ }
+
+ bytesRead = gNfsConnection.GetImpl()->nfs_read_sync(gNfsConnection.GetNfsContext(), m_pFileHandle, chunkSize, (char *)lpBuf+numberOfBytesRead);
+ bytesLeft -= bytesRead;
+ numberOfBytesRead += bytesRead;
+
+ if(bytesRead == 0)
+ {
+ break; //EOF
+ }
+
+ //something went wrong ...
+ if (bytesRead < 0)
+ {
+ CLog::Log(LOGERROR, "%s - Error( %d, %s )", __FUNCTION__, bytesRead, gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ return 0;
+ }
+
+ }
+ return (unsigned int)numberOfBytesRead;
+}
+
+int64_t CFileNFS::Seek(int64_t iFilePosition, int iWhence)
+{
+ int ret = 0;
+ off_t offset = 0;
+
+ CSingleLock lock(gNfsConnection);
+ if (m_pFileHandle == NULL || gNfsConnection.GetNfsContext()==NULL) return -1;
+
+
+ ret = (int)gNfsConnection.GetImpl()->nfs_lseek_sync(gNfsConnection.GetNfsContext(), m_pFileHandle, iFilePosition, iWhence, &offset);
+
+ if (ret < 0)
+ {
+ CLog::Log(LOGERROR, "%s - Error( seekpos: %"PRId64", whence: %i, fsize: %"PRId64", %s)", __FUNCTION__, iFilePosition, iWhence, m_fileSize, gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ return -1;
+ }
+ return (int64_t)offset;
+}
+
+void CFileNFS::Close()
+{
+ CSingleLock lock(gNfsConnection);
+
+ if (m_pFileHandle != NULL && gNfsConnection.GetNfsContext()!=NULL)
+ {
+ int ret = 0;
+ CLog::Log(LOGDEBUG,"CFileNFS::Close closing file %s", m_url.GetFileName().c_str());
+ ret = gNfsConnection.GetImpl()->nfs_close_sync(gNfsConnection.GetNfsContext(), m_pFileHandle);
+
+ if (ret < 0)
+ {
+ CLog::Log(LOGERROR, "Failed to close(%s) - %s\n", m_url.GetFileName().c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ }
+ m_pFileHandle=NULL;
+ m_fileSize = 0;
+ }
+}
+
+//this was a bitch!
+//for nfs write to work we have to write chunked
+//otherwise this could crash on big files
+int CFileNFS::Write(const void* lpBuf, int64_t uiBufSize)
+{
+ int numberOfBytesWritten = 0;
+ int writtenBytes = 0;
+ int leftBytes = uiBufSize;
+ int chunkSize = gNfsConnection.GetMaxWriteChunkSize();
+
+ CSingleLock lock(gNfsConnection);
+
+ if (m_pFileHandle == NULL || gNfsConnection.GetNfsContext() == NULL) return -1;
+
+ //write as long as some bytes are left to be written
+ while( leftBytes )
+ {
+ //the last chunk could be smalle than chunksize
+ if(leftBytes < chunkSize)
+ {
+ chunkSize = leftBytes;//write last chunk with correct size
+ }
+ //write chunk
+ writtenBytes = gNfsConnection.GetImpl()->nfs_write_sync(gNfsConnection.GetNfsContext(),
+ m_pFileHandle,
+ (size_t)chunkSize,
+ (char *)lpBuf + numberOfBytesWritten);
+ //decrease left bytes
+ leftBytes-= writtenBytes;
+ //increase overall written bytes
+ numberOfBytesWritten += writtenBytes;
+
+ //danger - something went wrong
+ if (writtenBytes < 0)
+ {
+ CLog::Log(LOGERROR, "Failed to pwrite(%s) %s\n", m_url.GetFileName().c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ break;
+ }
+ }
+ //return total number of written bytes
+ return numberOfBytesWritten;
+}
+
+bool CFileNFS::Delete(const CURL& url)
+{
+ int ret = 0;
+ CSingleLock lock(gNfsConnection);
+
+ if(!gNfsConnection.Connect(url))
+ return false;
+
+ CStdString filename = "//" + URIUtils::GetFileName(url.GetFileName());
+
+ ret = gNfsConnection.GetImpl()->nfs_unlink_sync(gNfsConnection.GetNfsContext(), filename.c_str());
+
+ if(ret != 0)
+ {
+ CLog::Log(LOGERROR, "%s - Error( %s )", __FUNCTION__, gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ }
+ return (ret == 0);
+}
+
+bool CFileNFS::Rename(const CURL& url, const CURL& urlnew)
+{
+ int ret = 0;
+ CSingleLock lock(gNfsConnection);
+
+ if(!gNfsConnection.Connect(url))
+ return false;
+
+ CStdString strFile = "//" + URIUtils::GetFileName(url.GetFileName());
+ CStdString strFileNew = "//" + URIUtils::GetFileName(urlnew.GetFileName());
+
+ ret = gNfsConnection.GetImpl()->nfs_rename_sync(gNfsConnection.GetNfsContext() , strFile.c_str(), strFileNew.c_str());
+
+ if(ret != 0)
+ {
+ CLog::Log(LOGERROR, "%s - Error( %s )", __FUNCTION__, gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ }
+ return (ret == 0);
+}
+
+bool CFileNFS::OpenForWrite(const CURL& url, bool bOverWrite)
+{
+ int ret = 0;
+
+ Close();
+ CSingleLock lock(gNfsConnection);
+
+ if(!gNfsConnection.Connect(url))
+ return false;
+
+ // we can't open files like nfs://file.f or nfs://server/file.f
+ // if a file matches the if below return false, it can't exist on a nfs share.
+ if (!IsValidFile(url.GetFileName())) return false;
+
+ CStdString filename = "//" + URIUtils::GetFileName(url.GetFileName());
+
+ if (bOverWrite)
+ {
+ CLog::Log(LOGWARNING, "FileNFS::OpenForWrite() called with overwriting enabled! - %s", filename.c_str());
+ //create file with proper permissions
+ ret = gNfsConnection.GetImpl()->nfs_creat_sync(gNfsConnection.GetNfsContext(), filename.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, &m_pFileHandle);
+ //if file was created the file handle isn't valid ... so close it and open later
+ if(ret == 0)
+ {
+ gNfsConnection.GetImpl()->nfs_close_sync(gNfsConnection.GetNfsContext(),m_pFileHandle);
+ }
+ }
+
+ ret = gNfsConnection.GetImpl()->nfs_open_sync(gNfsConnection.GetNfsContext(), filename.c_str(), O_RDWR, &m_pFileHandle);
+
+ if (ret || m_pFileHandle == NULL)
+ {
+ // write error to logfile
+ CLog::Log(LOGERROR, "CFileNFS::Open: Unable to open file : '%s' error : '%s'", filename.c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ return false;
+ }
+ m_url=url;
+
+#ifdef _LINUX
+ struct __stat64 tmpBuffer = {0};
+#else
+ struct stat tmpBuffer = {0};
+#endif
+ //only stat if file was not created
+ if(!bOverWrite)
+ {
+ if(Stat(&tmpBuffer))
+ {
+ m_url.Reset();
+ Close();
+ return false;
+ }
+ m_fileSize = tmpBuffer.st_size;//cache filesize of this file
+ }
+ else//file was created - filesize is zero
+ {
+ m_fileSize = 0;
+ }
+
+ // We've successfully opened the file!
+ return true;
+}
+
+bool CFileNFS::IsValidFile(const CStdString& strFileName)
+{
+ if (strFileName.Find('/') == -1 || /* doesn't have sharename */
+ strFileName.Right(2) == "/." || /* not current folder */
+ strFileName.Right(3) == "/..") /* not parent folder */
+ return false;
+ return true;
+}
+#endif//HAS_FILESYSTEM_NFS
77 xbmc/filesystem/FileNFS.h
View
@@ -0,0 +1,77 @@
+
+// FileNFS.h: interface for the CFileNFS class.
+#ifndef FILENFS_H_
+#define FILENFS_H_
+
+#include "IFile.h"
+#include "URL.h"
+#include "threads/CriticalSection.h"
+
+class DllLibNfs;
+
+class CNfsConnection : public CCriticalSection
+{
+public:
+
+ CNfsConnection();
+ ~CNfsConnection();
+ bool Connect(const CURL &url);
+ struct nfs_context *GetNfsContext(){return m_pNfsContext;}
+ size_t GetMaxReadChunkSize(){return m_readChunkSize;}
+ size_t GetMaxWriteChunkSize(){return m_writeChunkSize;}
+ DllLibNfs *GetImpl(){return m_pLibNfs;}
+
+ void AddActiveConnection();
+ void AddIdleConnection();
+ void CheckIfIdle();
+ void SetActivityTime();
+ void Deinit();
+
+private:
+ struct nfs_context *m_pNfsContext;
+ CStdString m_shareName;
+ CStdString m_hostName;
+ size_t m_readChunkSize;
+ size_t m_writeChunkSize;
+ int m_OpenConnections;
+ unsigned int m_IdleTimeout;
+ DllLibNfs *m_pLibNfs;
+ void resetContext();
+};
+
+extern CNfsConnection gNfsConnection;
+
+namespace XFILE
+{
+ class CFileNFS : public IFile
+ {
+ public:
+ CFileNFS();
+ virtual ~CFileNFS();
+ virtual void Close();
+ virtual int64_t Seek(int64_t iFilePosition, int iWhence = SEEK_SET);
+ virtual unsigned int Read(void* lpBuf, int64_t uiBufSize);
+ virtual bool Open(const CURL& url);
+ virtual bool Exists(const CURL& url);
+ virtual int Stat(const CURL& url, struct __stat64* buffer);
+ virtual int Stat(struct __stat64* buffer);
+ virtual int64_t GetLength();
+ virtual int64_t GetPosition();
+ virtual int Write(const void* lpBuf, int64_t uiBufSize);
+ //implement iocontrol for seek_possible for preventing the stat in File class for
+ //getting this info ...
+ virtual int IoControl(EIoControl request, void* param){ if(request == IOCTRL_SEEK_POSSIBLE) return 1;return -1;};
+ virtual int GetChunkSize() {return 1;}
+
+ virtual bool OpenForWrite(const CURL& url, bool bOverWrite = false);
+ virtual bool Delete(const CURL& url);
+ virtual bool Rename(const CURL& url, const CURL& urlnew);
+ protected:
+ CURL m_url;
+ bool IsValidFile(const CStdString& strFileName);
+ int64_t m_fileSize;
+ struct nfsfh *m_pFileHandle;
+ };
+}
+#endif // FILENFS_H_
+
5 xbmc/filesystem/Makefile.in
View
@@ -93,6 +93,11 @@ SRCS+=FileRar.cpp \
RarManager.cpp \
endif
+ifeq (@USE_LIBNFS@,1)
+SRCS+=FileNFS.cpp \
+ NFSDirectory.cpp \
+
+endif
INCLUDES+=-I@abs_top_srcdir@/lib/libUPnP/Platinum/Source/Core \
-I@abs_top_srcdir@/lib/libUPnP/Platinum/Source/Platinum \
206 xbmc/filesystem/NFSDirectory.cpp
View
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2011 Team XBMC
+ * http://www.xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "system.h"
+
+#ifdef HAS_FILESYSTEM_NFS
+#include "DllLibNfs.h"
+#include "NFSDirectory.h"
+#include "FileItem.h"
+#include "utils/log.h"
+#include "utils/URIUtils.h"
+#include "threads/SingleLock.h"
+
+using namespace XFILE;
+using namespace std;
+
+CNFSDirectory::CNFSDirectory(void)
+{
+ gNfsConnection.AddActiveConnection();
+}
+
+CNFSDirectory::~CNFSDirectory(void)
+{
+ gNfsConnection.AddIdleConnection();
+}
+
+bool CNFSDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items)
+{
+ // We accept nfs://server/path[/file]]]]
+ int ret = 0;
+ FILETIME fileTime, localTime;
+ CSingleLock lock(gNfsConnection);
+
+ CURL url(strPath);
+
+ if(!gNfsConnection.Connect(url))
+ {
+ return false;
+ }
+
+ CStdString strDirName="//";//relative to the strPath we connected - we want to get the "/" directory then
+
+ vector<CStdString> vecEntries;
+ struct nfsdir *nfsdir = NULL;
+ struct nfsdirent *nfsdirent = NULL;
+
+ ret = gNfsConnection.GetImpl()->nfs_opendir_sync(gNfsConnection.GetNfsContext(), strDirName.c_str(), &nfsdir);
+
+ if(ret != 0)
+ {
+ CLog::Log(LOGERROR, "Failed to open(%s) %s\n", strDirName.c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ return false;
+ }
+ lock.Leave();
+
+ while((nfsdirent = gNfsConnection.GetImpl()->nfs_readdir(gNfsConnection.GetNfsContext(), nfsdir)) != NULL)
+ {
+ vecEntries.push_back(nfsdirent->name);
+ }
+
+ lock.Enter();
+ gNfsConnection.GetImpl()->nfs_closedir(gNfsConnection.GetNfsContext(), nfsdir);//close the dir
+ lock.Leave();
+
+ for (size_t i=0; i<vecEntries.size(); i++)
+ {
+ CStdString strName = vecEntries[i];
+
+ if (!strName.Equals(".") && !strName.Equals("..")
+ && !strName.Equals("lost+found"))
+ {
+ int64_t iSize = 0;
+ bool bIsDir = false;
+ int64_t lTimeDate = 0;
+ struct stat info = {0};
+
+ CStdString strFullName = strDirName + strName;
+
+ lock.Enter();
+ ret = gNfsConnection.GetImpl()->nfs_stat_sync(gNfsConnection.GetNfsContext(), strFullName.c_str(), &info);
+ lock.Leave();
+
+ if( ret == 0 )
+ {
+ bIsDir = (info.st_mode & S_IFDIR) ? true : false;
+ lTimeDate = info.st_mtime;
+ if(lTimeDate == 0) // if modification date is missing, use create date
+ lTimeDate = info.st_ctime;
+ iSize = info.st_size;
+ }
+ else
+ CLog::Log(LOGERROR, "NFS; Failed to stat(%s) %s\n", strFullName.c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+
+ LONGLONG ll = Int32x32To64(lTimeDate & 0xffffffff, 10000000) + 116444736000000000ll;
+ fileTime.dwLowDateTime = (DWORD) (ll & 0xffffffff);
+ fileTime.dwHighDateTime = (DWORD)(ll >> 32);
+ FileTimeToLocalFileTime(&fileTime, &localTime);
+
+ CFileItemPtr pItem(new CFileItem(strName));
+ pItem->m_strPath = strPath + strName;
+ pItem->m_dateTime=localTime;
+
+ if (bIsDir)
+ {
+ URIUtils::AddSlashAtEnd(pItem->m_strPath);
+ pItem->m_bIsFolder = true;
+ }
+ else
+ {
+ pItem->m_bIsFolder = false;
+ pItem->m_dwSize = iSize;
+ }
+ items.Add(pItem);
+ }
+ }
+ return true;
+}
+
+bool CNFSDirectory::Create(const char* strPath)
+{
+ int ret = 0;
+
+ CSingleLock lock(gNfsConnection);
+ CStdString folderName(strPath);
+ URIUtils::RemoveSlashAtEnd(folderName);//mkdir fails if a slash is at the end!!!
+
+ CURL url(folderName);
+ folderName = "//" + URIUtils::GetFileName(folderName);
+
+ if(!gNfsConnection.Connect(url))
+ return false;
+
+ ret = gNfsConnection.GetImpl()->nfs_mkdir_sync(gNfsConnection.GetNfsContext(), folderName.c_str());
+
+ if(ret != 0)
+ CLog::Log(LOGERROR, "NFS: Failed to create(%s) %s\n", folderName.c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ return (ret == 0 || EEXIST == ret);
+}
+
+bool CNFSDirectory::Remove(const char* strPath)
+{
+ int ret = 0;
+
+ CSingleLock lock(gNfsConnection);
+ CStdString folderName(strPath);
+ URIUtils::RemoveSlashAtEnd(folderName);//rmdir fails if a slash is at the end!!!
+
+ CURL url(folderName);
+ folderName = "//" + URIUtils::GetFileName(folderName);
+
+ if(!gNfsConnection.Connect(url))
+ return false;
+
+ ret = gNfsConnection.GetImpl()->nfs_rmdir_sync(gNfsConnection.GetNfsContext(), folderName.c_str());
+
+ if(ret != 0 && errno != ENOENT)
+ {
+ CLog::Log(LOGERROR, "%s - Error( %s )", __FUNCTION__, gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
+ return false;
+ }
+ return true;
+}
+
+bool CNFSDirectory::Exists(const char* strPath)
+{
+ int ret = 0;
+
+ CSingleLock lock(gNfsConnection);
+ CStdString folderName(strPath);
+ URIUtils::RemoveSlashAtEnd(folderName);//remove slash at end or URIUtils::GetFileName won't return what we want...
+
+ CURL url(folderName);
+ folderName = "//" + URIUtils::GetFileName(folderName);
+
+ if(!gNfsConnection.Connect(url))
+ return false;
+
+ struct stat info;
+ ret = gNfsConnection.GetImpl()->nfs_stat_sync(gNfsConnection.GetNfsContext(), folderName.c_str(), &info);
+
+ if (ret != 0)
+ {
+ return false;
+ }
+ return (info.st_mode & S_IFDIR) ? true : false;
+}
+
+#endif
40 xbmc/filesystem/NFSDirectory.h
View
@@ -0,0 +1,40 @@
+#pragma once
+/*
+ * Copyright (C) 2011 Team XBMC
+ * http://www.xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "IDirectory.h"
+#include "FileNFS.h"
+
+namespace XFILE
+{
+ class CNFSDirectory : public IDirectory
+ {
+ public:
+ CNFSDirectory(void);
+ virtual ~CNFSDirectory(void);
+ virtual bool GetDirectory(const CStdString& strPath, CFileItemList &items);
+ virtual DIR_CACHE_TYPE GetCacheType(const CStdString &strPath) const { return DIR_CACHE_ONCE; };
+ virtual bool Create(const char* strPath);
+ virtual bool Exists(const char* strPath);
+ virtual bool Remove(const char* strPath);
+ };
+}
+
5 xbmc/system.h
View
@@ -64,6 +64,11 @@
#define HAS_FILESYSTEM_VTP
#define HAS_FILESYSTEM_HTSP
+#ifdef HAVE_LIBNFS
+ #define HAS_FILESYSTEM_NFS
+#endif
+
+
/**********************
* Non-free Components
**********************/
11 xbmc/utils/URIUtils.cpp
View
@@ -683,6 +683,17 @@ bool URIUtils::IsMusicDb(const CStdString& strFile)
return strFile.Left(8).Equals("musicdb:");
}
+bool URIUtils::IsNfs(const CStdString& strFile)
+{
+ CStdString strFile2(strFile);
+
+ if (IsStack(strFile))
+ strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
+
+ return strFile2.Left(4).Equals("nfs:");
+}
+
+
bool URIUtils::IsVideoDb(const CStdString& strFile)
{
return strFile.Left(8).Equals("videodb:");
1  xbmc/utils/URIUtils.h
View
@@ -68,6 +68,7 @@ class URIUtils
static bool IsMultiPath(const CStdString& strPath);
static bool IsMusicDb(const CStdString& strFile);
static bool IsMythTV(const CStdString& strFile);
+ static bool IsNfs(const CStdString& strFile);
static bool IsOnDVD(const CStdString& strFile);
static bool IsOnLAN(const CStdString& strFile);
static bool IsPlugin(const CStdString& strFile);
Something went wrong with that request. Please try again.