Skip to content
This repository

Add nfs support - closes #3358 #192

Merged
merged 9 commits into from almost 3 years ago

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 June 18, 2011
Memphiz Memphiz closed this June 18, 2011
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
12  XBMC-ATV2.xcodeproj/project.pbxproj
@@ -23,6 +23,8 @@
23 23
 		7C99B7AA134072CD00FC2B16 /* GUIDialogPlayEject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C99B7A8134072CD00FC2B16 /* GUIDialogPlayEject.cpp */; };
24 24
 		C807119F135DB842002F601B /* InputOperations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C807119D135DB842002F601B /* InputOperations.cpp */; };
25 25
 		C8EC5D51136954E400CCC10D /* XBMC_keytable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8EC5D4F136954E400CCC10D /* XBMC_keytable.cpp */; };
  26
+		DF0DF16C13A3AF82008ED511 /* FileNFS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF0DF16813A3AF82008ED511 /* FileNFS.cpp */; };
  27
+		DF0DF16D13A3AF82008ED511 /* NFSDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF0DF16A13A3AF82008ED511 /* NFSDirectory.cpp */; };
26 28
 		F54D9E0712B65FFF006870F9 /* libc.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F54D9E0612B65FFF006870F9 /* libc.dylib */; };
27 29
 		F54D9E8E12B71457006870F9 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F54D9E8D12B71457006870F9 /* CoreAudio.framework */; };
28 30
 		F56B15FB12CD6922009B4C96 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F56B15FA12CD6922009B4C96 /* CoreVideo.framework */; };
@@ -958,6 +960,10 @@
958 960
 		C80711A0135DB848002F601B /* AnnouncementUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AnnouncementUtils.h; sourceTree = "<group>"; };
959 961
 		C8EC5D4F136954E400CCC10D /* XBMC_keytable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = XBMC_keytable.cpp; sourceTree = "<group>"; };
960 962
 		C8EC5D50136954E400CCC10D /* XBMC_keytable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBMC_keytable.h; sourceTree = "<group>"; };
  963
+		DF0DF16813A3AF82008ED511 /* FileNFS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileNFS.cpp; sourceTree = "<group>"; };
  964
+		DF0DF16913A3AF82008ED511 /* FileNFS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileNFS.h; sourceTree = "<group>"; };
  965
+		DF0DF16A13A3AF82008ED511 /* NFSDirectory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NFSDirectory.cpp; sourceTree = "<group>"; };
  966
+		DF0DF16B13A3AF82008ED511 /* NFSDirectory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NFSDirectory.h; sourceTree = "<group>"; };
961 967
 		F54D9E0612B65FFF006870F9 /* libc.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libc.dylib; path = usr/lib/libc.dylib; sourceTree = SDKROOT; };
962 968
 		F54D9E8D12B71457006870F9 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
963 969
 		F56B15FA12CD6922009B4C96 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; };
@@ -3922,6 +3928,10 @@
3922 3928
 		F56C7395131EC151000AD0F6 /* filesystem */ = {
3923 3929
 			isa = PBXGroup;
3924 3930
 			children = (
  3931
+				DF0DF16813A3AF82008ED511 /* FileNFS.cpp */,
  3932
+				DF0DF16913A3AF82008ED511 /* FileNFS.h */,
  3933
+				DF0DF16A13A3AF82008ED511 /* NFSDirectory.cpp */,
  3934
+				DF0DF16B13A3AF82008ED511 /* NFSDirectory.h */,
3925 3935
 				F57A1DBB1329FB0A00498CC7 /* SourcesDirectory.cpp */,
3926 3936
 				F57A1DBC1329FB0A00498CC7 /* SourcesDirectory.h */,
3927 3937
 				F56C7396131EC151000AD0F6 /* SpecialProtocol.cpp */,
@@ -6720,6 +6730,8 @@
6720 6730
 				7C0A7FC813A9E75400AFC2BD /* DirtyRegionSolvers.cpp in Sources */,
6721 6731
 				7C0A7FC913A9E75400AFC2BD /* DirtyRegionTracker.cpp in Sources */,
6722 6732
 				7C0A7FCC13A9E76E00AFC2BD /* GUIWindowDebugInfo.cpp in Sources */,
  6733
+				DF0DF16C13A3AF82008ED511 /* FileNFS.cpp in Sources */,
  6734
+				DF0DF16D13A3AF82008ED511 /* NFSDirectory.cpp in Sources */,
6723 6735
 			);
6724 6736
 			runOnlyForDeploymentPostprocessing = 0;
6725 6737
 		};
12  XBMC-IOS.xcodeproj/project.pbxproj
@@ -24,6 +24,8 @@
24 24
 		7C99B7BE1340730000FC2B16 /* GUIDialogPlayEject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C99B7BC1340730000FC2B16 /* GUIDialogPlayEject.cpp */; };
25 25
 		C80711AD135DB85F002F601B /* InputOperations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C80711AB135DB85F002F601B /* InputOperations.cpp */; };
26 26
 		C8EC5D26136953E100CCC10D /* XBMC_keytable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8EC5D24136953E100CCC10D /* XBMC_keytable.cpp */; };
  27
+		DF0DF17F13A3AF9F008ED511 /* FileNFS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF0DF17B13A3AF9F008ED511 /* FileNFS.cpp */; };
  28
+		DF0DF18013A3AF9F008ED511 /* NFSDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF0DF17D13A3AF9F008ED511 /* NFSDirectory.cpp */; };
27 29
 		F54D9E8112B713F8006870F9 /* libc.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F54D9E8012B713F8006870F9 /* libc.dylib */; };
28 30
 		F56B143412CAF279009B4C96 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F56B143312CAF279009B4C96 /* CoreVideo.framework */; };
29 31
 		F56B14A512CAF523009B4C96 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F56B14A412CAF523009B4C96 /* AudioToolbox.framework */; };
@@ -958,6 +960,10 @@
958 960
 		C80711AE135DB865002F601B /* AnnouncementUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AnnouncementUtils.h; sourceTree = "<group>"; };
959 961
 		C8EC5D24136953E100CCC10D /* XBMC_keytable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = XBMC_keytable.cpp; sourceTree = "<group>"; };
960 962
 		C8EC5D25136953E100CCC10D /* XBMC_keytable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBMC_keytable.h; sourceTree = "<group>"; };
  963
+		DF0DF17B13A3AF9F008ED511 /* FileNFS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileNFS.cpp; sourceTree = "<group>"; };
  964
+		DF0DF17C13A3AF9F008ED511 /* FileNFS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileNFS.h; sourceTree = "<group>"; };
  965
+		DF0DF17D13A3AF9F008ED511 /* NFSDirectory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NFSDirectory.cpp; sourceTree = "<group>"; };
  966
+		DF0DF17E13A3AF9F008ED511 /* NFSDirectory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NFSDirectory.h; sourceTree = "<group>"; };
961 967
 		F54D9E8012B713F8006870F9 /* libc.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libc.dylib; path = usr/lib/libc.dylib; sourceTree = SDKROOT; };
962 968
 		F56B143312CAF279009B4C96 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; };
963 969
 		F56B14A412CAF523009B4C96 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
@@ -4283,6 +4289,10 @@
4283 4289
 		F56C8378131F42E8000AD0F6 /* filesystem */ = {
4284 4290
 			isa = PBXGroup;
4285 4291
 			children = (
  4292
+				DF0DF17B13A3AF9F008ED511 /* FileNFS.cpp */,
  4293
+				DF0DF17C13A3AF9F008ED511 /* FileNFS.h */,
  4294
+				DF0DF17D13A3AF9F008ED511 /* NFSDirectory.cpp */,
  4295
+				DF0DF17E13A3AF9F008ED511 /* NFSDirectory.h */,
4286 4296
 				F57A1DB61329FAF700498CC7 /* SourcesDirectory.cpp */,
4287 4297
 				F57A1DB71329FAF700498CC7 /* SourcesDirectory.h */,
4288 4298
 				F56C8379131F42E8000AD0F6 /* SpecialProtocol.cpp */,
@@ -6737,6 +6747,8 @@
6737 6747
 				7C0A7F9D13A9E70800AFC2BD /* GUIWindowDebugInfo.cpp in Sources */,
6738 6748
 				7C0A7FB213A9E72E00AFC2BD /* DirtyRegionSolvers.cpp in Sources */,
6739 6749
 				7C0A7FB313A9E72E00AFC2BD /* DirtyRegionTracker.cpp in Sources */,
  6750
+				DF0DF17F13A3AF9F008ED511 /* FileNFS.cpp in Sources */,
  6751
+				DF0DF18013A3AF9F008ED511 /* NFSDirectory.cpp in Sources */,
6740 6752
 			);
6741 6753
 			runOnlyForDeploymentPostprocessing = 0;
6742 6754
 		};
15  XBMC.xcodeproj/project.pbxproj
@@ -602,6 +602,8 @@
602 602
 		C8D0B2AF1265A9A800F0C0AC /* SystemGlobals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8D0B2AE1265A9A800F0C0AC /* SystemGlobals.cpp */; };
603 603
 		C8D0B2B01265A9A800F0C0AC /* SystemGlobals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8D0B2AE1265A9A800F0C0AC /* SystemGlobals.cpp */; };
604 604
 		C8EC5D0E1369519D00CCC10D /* XBMC_keytable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8EC5D0C1369519D00CCC10D /* XBMC_keytable.cpp */; };
  605
+		DF0DF15B13A3ADA7008ED511 /* FileNFS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF0DF15713A3ADA7008ED511 /* FileNFS.cpp */; };
  606
+		DF0DF15C13A3ADA7008ED511 /* NFSDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF0DF15913A3ADA7008ED511 /* NFSDirectory.cpp */; };
605 607
 		E306D12E0DDF7B590052C2AD /* XBMCHelper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E306D12C0DDF7B590052C2AD /* XBMCHelper.cpp */; };
606 608
 		E33206380D5070AA00435CE3 /* DVDDemuxVobsub.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E33206370D5070AA00435CE3 /* DVDDemuxVobsub.cpp */; };
607 609
 		E33466A60D2E5103005A65EC /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E33466A50D2E5103005A65EC /* IOKit.framework */; };
@@ -2518,6 +2520,10 @@
2518 2520
 		C8D0B2AE1265A9A800F0C0AC /* SystemGlobals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SystemGlobals.cpp; sourceTree = "<group>"; };
2519 2521
 		C8EC5D0C1369519D00CCC10D /* XBMC_keytable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = XBMC_keytable.cpp; sourceTree = "<group>"; };
2520 2522
 		C8EC5D0D1369519D00CCC10D /* XBMC_keytable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBMC_keytable.h; sourceTree = "<group>"; };
  2523
+		DF0DF15713A3ADA7008ED511 /* FileNFS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileNFS.cpp; sourceTree = "<group>"; };
  2524
+		DF0DF15813A3ADA7008ED511 /* FileNFS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileNFS.h; sourceTree = "<group>"; };
  2525
+		DF0DF15913A3ADA7008ED511 /* NFSDirectory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NFSDirectory.cpp; sourceTree = "<group>"; };
  2526
+		DF0DF15A13A3ADA7008ED511 /* NFSDirectory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NFSDirectory.h; sourceTree = "<group>"; };
2521 2527
 		E306D12C0DDF7B590052C2AD /* XBMCHelper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = XBMCHelper.cpp; sourceTree = "<group>"; };
2522 2528
 		E306D12D0DDF7B590052C2AD /* XBMCHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBMCHelper.h; sourceTree = "<group>"; };
2523 2529
 		E33206370D5070AA00435CE3 /* DVDDemuxVobsub.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DVDDemuxVobsub.cpp; sourceTree = "<group>"; };
@@ -5521,6 +5527,10 @@
5521 5527
 		E38E16940D25F9FA00618676 /* filesystem */ = {
5522 5528
 			isa = PBXGroup;
5523 5529
 			children = (
  5530
+				DF0DF15713A3ADA7008ED511 /* FileNFS.cpp */,
  5531
+				DF0DF15813A3ADA7008ED511 /* FileNFS.h */,
  5532
+				DF0DF15913A3ADA7008ED511 /* NFSDirectory.cpp */,
  5533
+				DF0DF15A13A3ADA7008ED511 /* NFSDirectory.h */,
5524 5534
 				7C84A59C12FA3C1600CD1714 /* SourcesDirectory.cpp */,
5525 5535
 				7C84A59D12FA3C1600CD1714 /* SourcesDirectory.h */,
5526 5536
 				7C2D6AE20F35453E00DD2E85 /* SpecialProtocol.cpp */,
@@ -8063,6 +8073,11 @@
8063 8073
 				F558F27B13ABD56600631E12 /* DirtyRegionSolvers.cpp in Sources */,
8064 8074
 				F558F27F13ABD57400631E12 /* DirtyRegionTracker.cpp in Sources */,
8065 8075
 				F558F29613ABD7DF00631E12 /* GUIWindowDebugInfo.cpp in Sources */,
  8076
+				7C0A7F7C13A9E69A00AFC2BD /* DirtyRegionSolvers.cpp in Sources */,
  8077
+				7C0A7F7D13A9E69A00AFC2BD /* DirtyRegionTracker.cpp in Sources */,
  8078
+				7C0A7F8213A9E6C600AFC2BD /* GUIWindowDebugInfo.cpp in Sources */,
  8079
+				DF0DF15B13A3ADA7008ED511 /* FileNFS.cpp in Sources */,
  8080
+				DF0DF15C13A3ADA7008ED511 /* NFSDirectory.cpp in Sources */,
8066 8081
 			);
8067 8082
 			runOnlyForDeploymentPostprocessing = 0;
8068 8083
 		};
4  configure.in
@@ -830,6 +830,7 @@ if test "$use_libnfs" != "no"; then
830 830
   AC_CHECK_HEADERS([nfsc/libnfs.h],,
831 831
    [if test "$use_libnfs" = "yes"; then
832 832
       AC_MSG_ERROR($libnfs_not_found)
  833
+      USE_LIBNFS=0
833 834
     elif test "$use_libnfs" != "no"; then
834 835
       AC_MSG_NOTICE($libnfs_not_found)
835 836
       use_libnfs="no"
@@ -840,8 +841,10 @@ if test "$use_libnfs" != "no"; then
840 841
   fi
841 842
   if test "$use_libnfs" != "no"; then
842 843
     AC_DEFINE([HAVE_LIBNFS], [1], [Whether to use libnfs library.])
  844
+    USE_LIBNFS=1
843 845
   fi
844 846
 else
  847
+  USE_LIBNFS=0
845 848
   AC_MSG_NOTICE($libnfs_disabled)
846 849
 fi
847 850
 
@@ -1490,6 +1493,7 @@ AC_SUBST(USE_OPENGLES)
1490 1493
 AC_SUBST(USE_VDPAU)
1491 1494
 AC_SUBST(USE_VAAPI)
1492 1495
 AC_SUBST(USE_CRYSTALHD)
  1496
+AC_SUBST(USE_LIBNFS)
1493 1497
 AC_SUBST(USE_VDA)
1494 1498
 AC_SUBST(USE_OPENMAX)
1495 1499
 AC_SUBST(USE_PULSE)
7  xbmc/Application.cpp
@@ -99,6 +99,9 @@
99 99
 #if defined(_LINUX) && defined(HAS_FILESYSTEM_SMB)
100 100
 #include "filesystem/SMBDirectory.h"
101 101
 #endif
  102
+#ifdef HAS_FILESYSTEM_NFS
  103
+#include "filesystem/FileNFS.h"
  104
+#endif
102 105
 #ifdef HAS_FILESYSTEM_SFTP
103 106
 #include "filesystem/FileSFTP.h"
104 107
 #endif
@@ -4796,6 +4799,10 @@ void CApplication::ProcessSlow()
4796 4799
 #if defined(_LINUX) && defined(HAS_FILESYSTEM_SMB)
4797 4800
   smb.CheckIfIdle();
4798 4801
 #endif
  4802
+  
  4803
+#ifdef HAS_FILESYSTEM_NFS
  4804
+  gNfsConnection.CheckIfIdle();
  4805
+#endif
4799 4806
 
4800 4807
 #ifdef HAS_FILESYSTEM_SFTP
4801 4808
   CSFTPSessionManager::ClearOutIdleSessions();
6  xbmc/FileItem.cpp
@@ -759,6 +759,11 @@ bool CFileItem::IsOnDVD() const
759 759
   return URIUtils::IsOnDVD(m_strPath) || m_iDriveType == CMediaSource::SOURCE_TYPE_DVD;
760 760
 }
761 761
 
  762
+bool CFileItem::IsNfs() const
  763
+{
  764
+  return URIUtils::IsNfs(m_strPath);
  765
+}
  766
+
762 767
 bool CFileItem::IsOnLAN() const
763 768
 {
764 769
   return URIUtils::IsOnLAN(m_strPath);
@@ -1999,6 +2004,7 @@ void CFileItemList::Stack()
1999 2004
       // 1. rars and zips may be on slow sources? is this supposed to be allowed?
2000 2005
       if( !item->IsRemote()
2001 2006
         || item->IsSmb()
  2007
+        || item->IsNfs() 
2002 2008
         || URIUtils::IsInRAR(item->m_strPath)
2003 2009
         || URIUtils::IsInZIP(item->m_strPath)
2004 2010
         )
1  xbmc/FileItem.h
@@ -116,6 +116,7 @@ class CFileItem :
116 116
   bool IsOnDVD() const;
117 117
   bool IsOnLAN() const;
118 118
   bool IsHD() const;
  119
+  bool IsNfs() const;  
119 120
   bool IsRemote() const;
120 121
   bool IsSmb() const;
121 122
   bool IsURL() const;
8  xbmc/Util.cpp
@@ -1145,8 +1145,8 @@ bool CUtil::CreateDirectoryEx(const CStdString& strPath)
1145 1145
   // return true if directory already exist
1146 1146
   if (CDirectory::Exists(strPath)) return true;
1147 1147
 
1148  
-  // we currently only allow HD and smb paths
1149  
-  if (!URIUtils::IsHD(strPath) && !URIUtils::IsSmb(strPath))
  1148
+  // we currently only allow HD and smb and nfs paths
  1149
+  if (!URIUtils::IsHD(strPath) && !URIUtils::IsSmb(strPath) && !URIUtils::IsNfs(strPath))
1150 1150
   {
1151 1151
     CLog::Log(LOGERROR,"%s called with an unsupported path: %s", __FUNCTION__, strPath.c_str());
1152 1152
     return false;
@@ -1865,11 +1865,13 @@ bool CUtil::MakeShortenPath(CStdString StrInput, CStdString& StrOutput, int iTex
1865 1865
 
1866 1866
 bool CUtil::SupportsFileOperations(const CStdString& strPath)
1867 1867
 {
1868  
-  // currently only hd and smb support delete and rename
  1868
+  // currently only hd, smb and nfs support delete and rename
1869 1869
   if (URIUtils::IsHD(strPath))
1870 1870
     return true;
1871 1871
   if (URIUtils::IsSmb(strPath))
1872 1872
     return true;
  1873
+  if (URIUtils::IsNfs(strPath))
  1874
+    return true;
1873 1875
   if (URIUtils::IsMythTV(strPath))
1874 1876
   {
1875 1877
     /*
7  xbmc/filesystem/FactoryDirectory.cpp
@@ -93,6 +93,9 @@
93 93
 #ifdef HAS_FILESYSTEM_SFTP
94 94
 #include "SFTPDirectory.h"
95 95
 #endif
  96
+#ifdef HAS_FILESYSTEM_NFS
  97
+#include "NFSDirectory.h"
  98
+#endif
96 99
 
97 100
 using namespace XFILE;
98 101
 
@@ -184,6 +187,10 @@ IDirectory* CFactoryDirectory::Create(const CStdString& strPath)
184 187
 #ifdef HAS_ZEROCONF
185 188
     if (strProtocol == "zeroconf") return new CZeroconfDirectory();
186 189
 #endif
  190
+#ifdef HAS_FILESYSTEM_NFS
  191
+    if (strProtocol == "nfs") return new CNFSDirectory();
  192
+#endif
  193
+    
187 194
   }
188 195
 
189 196
   CLog::Log(LOGWARNING, "%s - Unsupported protocol(%s) in %s", __FUNCTION__, strProtocol.c_str(), url.Get().c_str() );
8  xbmc/filesystem/FileFactory.cpp
@@ -62,6 +62,10 @@
62 62
 #ifdef HAS_FILESYSTEM_SFTP
63 63
 #include "FileSFTP.h"
64 64
 #endif
  65
+#ifdef HAS_FILESYSTEM_NFS
  66
+#include "FileNFS.h"
  67
+#endif
  68
+
65 69
 #include "FileMusicDatabase.h"
66 70
 #include "FileSpecialProtocol.h"
67 71
 #include "MultiPathFile.h"
@@ -154,6 +158,10 @@ IFile* CFileFactory::CreateLoader(const CURL& url)
154 158
 #ifdef HAS_FILESYSTEM_VTP
155 159
     else if (strProtocol == "vtp") return new CVTPFile();
156 160
 #endif
  161
+#ifdef HAS_FILESYSTEM_NFS
  162
+    else if (strProtocol == "nfs") return new CFileNFS();
  163
+#endif
  164
+    
157 165
   }
158 166
 
159 167
   CLog::Log(LOGWARNING, "%s - Unsupported protocol(%s) in %s", __FUNCTION__, strProtocol.c_str(), url.Get().c_str() );
541  xbmc/filesystem/FileNFS.cpp
... ...
@@ -0,0 +1,541 @@
  1
+/*
  2
+ *      Copyright (C) 2011 Team XBMC
  3
+ *      http://www.xbmc.org
  4
+ *
  5
+ *  This Program is free software; you can redistribute it and/or modify
  6
+ *  it under the terms of the GNU General Public License as published by
  7
+ *  the Free Software Foundation; either version 2, or (at your option)
  8
+ *  any later version.
  9
+ *
  10
+ *  This Program is distributed in the hope that it will be useful,
  11
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13
+ *  GNU General Public License for more details.
  14
+ *
  15
+ *  You should have received a copy of the GNU General Public License
  16
+ *  along with XBMC; see the file COPYING.  If not, write to
  17
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  18
+ *  http://www.gnu.org/copyleft/gpl.html
  19
+ *
  20
+ */
  21
+
  22
+// FileNFS.cpp: implementation of the CFileNFS class.
  23
+//
  24
+//////////////////////////////////////////////////////////////////////
  25
+#include "system.h"
  26
+
  27
+#ifdef HAS_FILESYSTEM_NFS
  28
+#include "DllLibNfs.h"
  29
+#include "FileNFS.h"
  30
+#include "threads/SingleLock.h"
  31
+#include "utils/log.h"
  32
+#include "utils/URIUtils.h"
  33
+
  34
+using namespace XFILE;
  35
+
  36
+CNfsConnection::CNfsConnection()
  37
+: m_pNfsContext(NULL)
  38
+, m_shareName("")
  39
+, m_readChunkSize(0)
  40
+, m_writeChunkSize(0)
  41
+, m_OpenConnections(0)
  42
+, m_IdleTimeout(0)
  43
+, m_pLibNfs(new DllLibNfs())
  44
+{
  45
+}
  46
+
  47
+CNfsConnection::~CNfsConnection()
  48
+{
  49
+  delete m_pLibNfs;
  50
+}
  51
+
  52
+void CNfsConnection::resetContext()
  53
+{
  54
+  
  55
+  if(!m_pLibNfs->IsLoaded())
  56
+  {
  57
+    if(!m_pLibNfs->Load())
  58
+    {
  59
+      CLog::Log(LOGERROR,"NFS: Error loading libnfs (%s).",__FUNCTION__);    
  60
+      return;//FATAL!
  61
+    }    
  62
+  }
  63
+  
  64
+  if(m_pNfsContext)
  65
+  {
  66
+    m_pLibNfs->nfs_destroy_context(m_pNfsContext);
  67
+  }
  68
+  
  69
+  m_pNfsContext = m_pLibNfs->nfs_init_context();
  70
+  
  71
+  if (!m_pNfsContext) 
  72
+  {
  73
+		CLog::Log(LOGERROR,"NFS: Error initcontext in resetContext.");
  74
+  }
  75
+  m_writeChunkSize = 0;
  76
+  m_readChunkSize = 0;
  77
+  m_shareName.clear();
  78
+  m_hostName.clear();
  79
+}
  80
+
  81
+bool CNfsConnection::Connect(const CURL& url)
  82
+{
  83
+  CSingleLock lock(*this);
  84
+  int ret = 0;
  85
+  CStdString share; 
  86
+  URIUtils::GetDirectory(url.GetFileName(),share);
  87
+  share = "/" + share;
  88
+
  89
+  if(!share.Equals(m_shareName,true) || !url.GetHostName().Equals(m_hostName,false) )
  90
+  {
  91
+    resetContext();//we need a new context because sharename or hostname has changed - old context will be freed
  92
+    
  93
+    //we connect to the directory of the path. This will be the "root" path of this connection then.
  94
+    //So all fileoperations are relative to this mountpoint...
  95
+    ret = m_pLibNfs->nfs_mount_sync(m_pNfsContext, url.GetHostName().c_str(), share.c_str());
  96
+
  97
+    if  (ret != 0) 
  98
+    {
  99
+      CLog::Log(LOGERROR,"NFS: Failed to mount nfs share: %s\n", m_pLibNfs->nfs_get_error(m_pNfsContext));
  100
+      return false;
  101
+    }
  102
+    m_shareName = share;
  103
+    m_hostName = url.GetHostName();
  104
+    m_readChunkSize = m_pLibNfs->nfs_get_readmax(m_pNfsContext);
  105
+    m_writeChunkSize = m_pLibNfs->nfs_get_writemax(m_pNfsContext);   
  106
+    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);
  107
+  }
  108
+
  109
+  return true; 
  110
+}
  111
+
  112
+void CNfsConnection::Deinit()
  113
+{
  114
+  if(m_pNfsContext)
  115
+  {
  116
+    m_pLibNfs->nfs_destroy_context(m_pNfsContext);
  117
+  }        
  118
+  m_pNfsContext = NULL;
  119
+  m_shareName.clear();
  120
+  m_hostName.clear();
  121
+  m_pLibNfs->Unload();
  122
+}
  123
+
  124
+/* This is called from CApplication::ProcessSlow() and is used to tell if nfs have been idle for too long */
  125
+void CNfsConnection::CheckIfIdle()
  126
+{
  127
+  /* We check if there are open connections. This is done without a lock to not halt the mainthread. It should be thread safe as
  128
+   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.  */
  129
+  if (m_OpenConnections == 0 && m_pNfsContext != NULL)
  130
+  { /* I've set the the maxiumum IDLE time to be 1 min and 30 sec. */
  131
+    CSingleLock lock(*this);
  132
+    if (m_OpenConnections == 0 /* check again - when locked */)
  133
+    {
  134
+      if (m_IdleTimeout > 0)
  135
+      {
  136
+        m_IdleTimeout--;
  137
+      }
  138
+      else
  139
+      {
  140
+        CLog::Log(LOGNOTICE, "NFS is idle. Closing the remaining connections.");
  141
+        gNfsConnection.Deinit();
  142
+      }
  143
+    }
  144
+  }
  145
+}
  146
+
  147
+void CNfsConnection::SetActivityTime()
  148
+{
  149
+  /* Since we get called every 500ms from ProcessSlow we limit the tick count to 180 */
  150
+  /* That means we have 2 ticks per second which equals 180/2 == 90 seconds */
  151
+  m_IdleTimeout = 180;
  152
+}
  153
+
  154
+
  155
+/* The following two function is used to keep track on how many Opened files/directories there are.
  156
+needed for unloading the dylib*/
  157
+void CNfsConnection::AddActiveConnection()
  158
+{
  159
+  CSingleLock lock(*this);
  160
+  m_OpenConnections++;
  161
+}
  162
+
  163
+void CNfsConnection::AddIdleConnection()
  164
+{
  165
+  CSingleLock lock(*this);
  166
+  m_OpenConnections--;
  167
+  /* If we close a file we reset the idle timer so that we don't have any wierd behaviours if a user
  168
+   leaves the movie paused for a long while and then press stop */
  169
+  m_IdleTimeout = 180;
  170
+}
  171
+
  172
+
  173
+CNfsConnection gNfsConnection;
  174
+
  175
+CFileNFS::CFileNFS()
  176
+: m_fileSize(0)
  177
+, m_pFileHandle(NULL)
  178
+{
  179
+  gNfsConnection.AddActiveConnection();
  180
+}
  181
+
  182
+CFileNFS::~CFileNFS()
  183
+{
  184
+  Close();
  185
+  gNfsConnection.AddIdleConnection();
  186
+}
  187
+
  188
+int64_t CFileNFS::GetPosition()
  189
+{
  190
+  int ret = 0;
  191
+  off_t offset = 0;
  192
+  CSingleLock lock(gNfsConnection);
  193
+  
  194
+  if (gNfsConnection.GetNfsContext() == NULL || m_pFileHandle == NULL) return 0;
  195
+  
  196
+  ret = (int)gNfsConnection.GetImpl()->nfs_lseek_sync(gNfsConnection.GetNfsContext(), m_pFileHandle, 0, SEEK_CUR, &offset);
  197
+  
  198
+  if (ret < 0) 
  199
+  {
  200
+    CLog::Log(LOGERROR, "NFS: Failed to lseek(%s)",gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
  201
+  }
  202
+  return offset;
  203
+}
  204
+
  205
+int64_t CFileNFS::GetLength()
  206
+{
  207
+  if (m_pFileHandle == NULL) return 0;
  208
+  return m_fileSize;
  209
+}
  210
+
  211
+bool CFileNFS::Open(const CURL& url)
  212
+{
  213
+  int ret = 0;
  214
+  Close();
  215
+  // we can't open files like nfs://file.f or nfs://server/file.f
  216
+  // if a file matches the if below return false, it can't exist on a nfs share.
  217
+  if (!IsValidFile(url.GetFileName()))
  218
+  {
  219
+    CLog::Log(LOGNOTICE,"NFS: Bad URL : '%s'",url.GetFileName().c_str());
  220
+    return false;
  221
+  }
  222
+  
  223
+  CStdString filename = "//" + URIUtils::GetFileName(url.GetFileName());
  224
+   
  225
+  CSingleLock lock(gNfsConnection);
  226
+  
  227
+  if(!gNfsConnection.Connect(url))
  228
+    return false;
  229
+  
  230
+  ret = gNfsConnection.GetImpl()->nfs_open_sync(gNfsConnection.GetNfsContext(), filename.c_str(), O_RDONLY, &m_pFileHandle);
  231
+  
  232
+  if (ret != 0) 
  233
+  {
  234
+    CLog::Log(LOGINFO, "CFileNFS::Open: Unable to open file : '%s'  error : '%s'", url.GetFileName().c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
  235
+    return false;
  236
+  } 
  237
+  
  238
+  CLog::Log(LOGDEBUG,"CFileNFS::Open - opened %s",url.GetFileName().c_str());
  239
+  m_url=url;
  240
+  
  241
+#ifdef _LINUX
  242
+  struct __stat64 tmpBuffer;
  243
+#else
  244
+  struct stat tmpBuffer;
  245
+#endif
  246
+  if( Stat(&tmpBuffer) )
  247
+  {
  248
+    m_url.Reset();
  249
+    Close();
  250
+    return false;
  251
+  }
  252
+  
  253
+  m_fileSize = tmpBuffer.st_size;//cache the size of this file
  254
+  // We've successfully opened the file!
  255
+  return true;
  256
+}
  257
+
  258
+
  259
+bool CFileNFS::Exists(const CURL& url)
  260
+{
  261
+  return Stat(url,NULL) == 0;
  262
+}
  263
+
  264
+int CFileNFS::Stat(struct __stat64* buffer)
  265
+{
  266
+  return Stat(m_url,buffer);
  267
+}
  268
+
  269
+
  270
+int CFileNFS::Stat(const CURL& url, struct __stat64* buffer)
  271
+{
  272
+  int ret = 0;
  273
+  CSingleLock lock(gNfsConnection);
  274
+  
  275
+  if(!gNfsConnection.Connect(url))
  276
+    return -1;
  277
+   
  278
+  CStdString filename = "//" + URIUtils::GetFileName(url.GetFileName());
  279
+
  280
+  struct stat tmpBuffer = {0};
  281
+
  282
+  ret = gNfsConnection.GetImpl()->nfs_stat_sync(gNfsConnection.GetNfsContext(), filename.c_str(), &tmpBuffer);
  283
+  
  284
+  //if buffer == NULL we where called from Exists - in that case don't spam the log with errors
  285
+  if (ret != 0 && buffer != NULL) 
  286
+  {
  287
+    CLog::Log(LOGERROR, "NFS: Failed to stat(%s) %s\n", url.GetFileName().c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
  288
+    ret = -1;
  289
+  }
  290
+  else
  291
+  {  
  292
+    if(buffer)
  293
+    {
  294
+      memset(buffer, 0, sizeof(struct __stat64));
  295
+      buffer->st_dev = tmpBuffer.st_dev;
  296
+      buffer->st_ino = tmpBuffer.st_ino;
  297
+      buffer->st_mode = tmpBuffer.st_mode;
  298
+      buffer->st_nlink = tmpBuffer.st_nlink;
  299
+      buffer->st_uid = tmpBuffer.st_uid;
  300
+      buffer->st_gid = tmpBuffer.st_gid;
  301
+      buffer->st_rdev = tmpBuffer.st_rdev;
  302
+      buffer->st_size = tmpBuffer.st_size;
  303
+      buffer->st_atime = tmpBuffer.st_atime;
  304
+      buffer->st_mtime = tmpBuffer.st_mtime;
  305
+      buffer->st_ctime = tmpBuffer.st_ctime;
  306
+    }
  307
+  }
  308
+  return ret;
  309
+}
  310
+
  311
+unsigned int CFileNFS::Read(void *lpBuf, int64_t uiBufSize)
  312
+{
  313
+  int numberOfBytesRead = 0;
  314
+  int bytesLeft = uiBufSize;
  315
+  int bytesRead = 0;
  316
+  int chunkSize = gNfsConnection.GetMaxReadChunkSize();
  317
+  CSingleLock lock(gNfsConnection);
  318
+  
  319
+  if (m_pFileHandle == NULL || gNfsConnection.GetNfsContext()==NULL ) return 0;
  320
+
  321
+  //read chunked since nfs will only give server specific packet sizes at once
  322
+  while(bytesLeft)
  323
+  {
  324
+    //last chunk could be smaller then chunk size
  325
+    if(bytesLeft < chunkSize)
  326
+    {
  327
+      chunkSize = bytesLeft; 
  328
+    }
  329
+    
  330
+    bytesRead = gNfsConnection.GetImpl()->nfs_read_sync(gNfsConnection.GetNfsContext(), m_pFileHandle, chunkSize, (char *)lpBuf+numberOfBytesRead);
  331
+    bytesLeft -= bytesRead;
  332
+    numberOfBytesRead += bytesRead;
  333
+    
  334
+    if(bytesRead == 0)
  335
+    {
  336
+      break; //EOF
  337
+    }
  338
+    
  339
+    //something went wrong ...
  340
+    if (bytesRead < 0) 
  341
+    {
  342
+      CLog::Log(LOGERROR, "%s - Error( %d, %s )", __FUNCTION__, bytesRead, gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
  343
+      return 0;
  344
+    }
  345
+    
  346
+  }
  347
+  return (unsigned int)numberOfBytesRead;
  348
+}
  349
+
  350
+int64_t CFileNFS::Seek(int64_t iFilePosition, int iWhence)
  351
+{
  352
+  int ret = 0;
  353
+  off_t offset = 0;
  354
+
  355
+  CSingleLock lock(gNfsConnection);  
  356
+  if (m_pFileHandle == NULL || gNfsConnection.GetNfsContext()==NULL) return -1;
  357
+  
  358
+ 
  359
+  ret = (int)gNfsConnection.GetImpl()->nfs_lseek_sync(gNfsConnection.GetNfsContext(), m_pFileHandle, iFilePosition, iWhence, &offset);
  360
+  
  361
+  if (ret < 0) 
  362
+  {
  363
+    CLog::Log(LOGERROR, "%s - Error( seekpos: %"PRId64", whence: %i, fsize: %"PRId64", %s)", __FUNCTION__, iFilePosition, iWhence, m_fileSize, gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
  364
+    return -1;
  365
+  }
  366
+  return (int64_t)offset;
  367
+}
  368
+
  369
+void CFileNFS::Close()
  370
+{
  371
+  CSingleLock lock(gNfsConnection);
  372
+  
  373
+  if (m_pFileHandle != NULL && gNfsConnection.GetNfsContext()!=NULL)
  374
+  {
  375
+    int ret = 0;
  376
+    CLog::Log(LOGDEBUG,"CFileNFS::Close closing file %s", m_url.GetFileName().c_str());
  377
+    ret = gNfsConnection.GetImpl()->nfs_close_sync(gNfsConnection.GetNfsContext(), m_pFileHandle);
  378
+    
  379
+	  if (ret < 0) 
  380
+    {
  381
+      CLog::Log(LOGERROR, "Failed to close(%s) - %s\n", m_url.GetFileName().c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
  382
+    }
  383
+    m_pFileHandle=NULL;
  384
+    m_fileSize = 0;
  385
+  }
  386
+}
  387
+
  388
+//this was a bitch!
  389
+//for nfs write to work we have to write chunked
  390
+//otherwise this could crash on big files
  391
+int CFileNFS::Write(const void* lpBuf, int64_t uiBufSize)
  392
+{
  393
+  int numberOfBytesWritten = 0;
  394
+  int writtenBytes = 0;
  395
+  int leftBytes = uiBufSize;
  396
+  int chunkSize = gNfsConnection.GetMaxWriteChunkSize();
  397
+  
  398
+  CSingleLock lock(gNfsConnection);
  399
+  
  400
+  if (m_pFileHandle == NULL || gNfsConnection.GetNfsContext() == NULL) return -1;
  401
+  
  402
+  //write as long as some bytes are left to be written
  403
+  while( leftBytes )
  404
+  {
  405
+    //the last chunk could be smalle than chunksize
  406
+    if(leftBytes < chunkSize)
  407
+    {
  408
+      chunkSize = leftBytes;//write last chunk with correct size
  409
+    }
  410
+    //write chunk
  411
+    writtenBytes = gNfsConnection.GetImpl()->nfs_write_sync(gNfsConnection.GetNfsContext(), 
  412
+                                  m_pFileHandle, 
  413
+                                  (size_t)chunkSize, 
  414
+                                  (char *)lpBuf + numberOfBytesWritten);
  415
+    //decrease left bytes
  416
+    leftBytes-= writtenBytes;
  417
+    //increase overall written bytes
  418
+    numberOfBytesWritten += writtenBytes;
  419
+        
  420
+    //danger - something went wrong
  421
+    if (writtenBytes < 0) 
  422
+    {
  423
+      CLog::Log(LOGERROR, "Failed to pwrite(%s) %s\n", m_url.GetFileName().c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));        
  424
+      break;
  425
+    }     
  426
+  }
  427
+  //return total number of written bytes
  428
+  return numberOfBytesWritten;
  429
+}
  430
+
  431
+bool CFileNFS::Delete(const CURL& url)
  432
+{
  433
+  int ret = 0;
  434
+  CSingleLock lock(gNfsConnection);
  435
+  
  436
+  if(!gNfsConnection.Connect(url))
  437
+    return false;
  438
+  
  439
+  CStdString filename = "//" + URIUtils::GetFileName(url.GetFileName());
  440
+  
  441
+  ret = gNfsConnection.GetImpl()->nfs_unlink_sync(gNfsConnection.GetNfsContext(), filename.c_str());
  442
+  
  443
+  if(ret != 0)
  444
+  {
  445
+    CLog::Log(LOGERROR, "%s - Error( %s )", __FUNCTION__, gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
  446
+  }
  447
+  return (ret == 0);
  448
+}
  449
+
  450
+bool CFileNFS::Rename(const CURL& url, const CURL& urlnew)
  451
+{
  452
+  int ret = 0;
  453
+  CSingleLock lock(gNfsConnection);
  454
+  
  455
+  if(!gNfsConnection.Connect(url))
  456
+    return false;
  457
+  
  458
+  CStdString strFile = "//" + URIUtils::GetFileName(url.GetFileName());
  459
+  CStdString strFileNew = "//" + URIUtils::GetFileName(urlnew.GetFileName());
  460
+  
  461
+  ret = gNfsConnection.GetImpl()->nfs_rename_sync(gNfsConnection.GetNfsContext() , strFile.c_str(), strFileNew.c_str());
  462
+  
  463
+  if(ret != 0)
  464
+  {
  465
+    CLog::Log(LOGERROR, "%s - Error( %s )", __FUNCTION__, gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
  466
+  } 
  467
+  return (ret == 0);
  468
+}
  469
+
  470
+bool CFileNFS::OpenForWrite(const CURL& url, bool bOverWrite)
  471
+{ 
  472
+  int ret = 0;
  473
+  
  474
+  Close();
  475
+  CSingleLock lock(gNfsConnection);
  476
+  
  477
+  if(!gNfsConnection.Connect(url))
  478
+    return false;
  479
+  
  480
+  // we can't open files like nfs://file.f or nfs://server/file.f
  481
+  // if a file matches the if below return false, it can't exist on a nfs share.
  482
+  if (!IsValidFile(url.GetFileName())) return false;
  483
+  
  484
+  CStdString filename = "//" + URIUtils::GetFileName(url.GetFileName());
  485
+  
  486
+  if (bOverWrite)
  487
+  {
  488
+    CLog::Log(LOGWARNING, "FileNFS::OpenForWrite() called with overwriting enabled! - %s", filename.c_str());
  489
+    //create file with proper permissions
  490
+    ret = gNfsConnection.GetImpl()->nfs_creat_sync(gNfsConnection.GetNfsContext(), filename.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, &m_pFileHandle);    
  491
+    //if file was created the file handle isn't valid ... so close it and open later
  492
+    if(ret == 0)
  493
+    {
  494
+      gNfsConnection.GetImpl()->nfs_close_sync(gNfsConnection.GetNfsContext(),m_pFileHandle);
  495
+    }
  496
+  }
  497
+
  498
+  ret = gNfsConnection.GetImpl()->nfs_open_sync(gNfsConnection.GetNfsContext(), filename.c_str(), O_RDWR, &m_pFileHandle);
  499
+  
  500
+  if (ret || m_pFileHandle == NULL)
  501
+  {
  502
+    // write error to logfile
  503
+    CLog::Log(LOGERROR, "CFileNFS::Open: Unable to open file : '%s' error : '%s'", filename.c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
  504
+    return false;
  505
+  }
  506
+  m_url=url;
  507
+  
  508
+#ifdef _LINUX
  509
+  struct __stat64 tmpBuffer = {0};
  510
+#else
  511
+  struct stat tmpBuffer = {0};
  512
+#endif
  513
+  //only stat if file was not created
  514
+  if(!bOverWrite) 
  515
+  {
  516
+    if(Stat(&tmpBuffer))
  517
+    {
  518
+      m_url.Reset();
  519
+      Close();
  520
+      return false;
  521
+    }
  522
+    m_fileSize = tmpBuffer.st_size;//cache filesize of this file    
  523
+  }
  524
+  else//file was created - filesize is zero
  525
+  {
  526
+    m_fileSize = 0;    
  527
+  }
  528
+  
  529
+  // We've successfully opened the file!
  530
+  return true;
  531
+}
  532
+
  533
+bool CFileNFS::IsValidFile(const CStdString& strFileName)
  534
+{
  535
+  if (strFileName.Find('/') == -1 || /* doesn't have sharename */
  536
+      strFileName.Right(2) == "/." || /* not current folder */
  537
+      strFileName.Right(3) == "/..")  /* not parent folder */
  538
+    return false;
  539
+  return true;
  540
+}
  541
+#endif//HAS_FILESYSTEM_NFS
77  xbmc/filesystem/FileNFS.h
... ...
@@ -0,0 +1,77 @@
  1
+
  2
+// FileNFS.h: interface for the CFileNFS class.
  3
+#ifndef FILENFS_H_
  4
+#define FILENFS_H_
  5
+
  6
+#include "IFile.h"
  7
+#include "URL.h"
  8
+#include "threads/CriticalSection.h"
  9
+
  10
+class DllLibNfs;
  11
+
  12
+class CNfsConnection : public CCriticalSection
  13
+{     
  14
+public:
  15
+  
  16
+  CNfsConnection();
  17
+  ~CNfsConnection();
  18
+  bool Connect(const CURL &url);
  19
+  struct nfs_context *GetNfsContext(){return m_pNfsContext;}
  20
+  size_t            GetMaxReadChunkSize(){return m_readChunkSize;}
  21
+  size_t            GetMaxWriteChunkSize(){return m_writeChunkSize;} 
  22
+  DllLibNfs        *GetImpl(){return m_pLibNfs;}
  23
+  
  24
+  void AddActiveConnection();
  25
+  void AddIdleConnection();
  26
+  void CheckIfIdle();
  27
+  void SetActivityTime();
  28
+  void Deinit();
  29
+  
  30
+private:
  31
+  struct nfs_context *m_pNfsContext;    
  32
+  CStdString m_shareName;
  33
+  CStdString m_hostName;
  34
+  size_t m_readChunkSize;
  35
+  size_t m_writeChunkSize;
  36
+  int m_OpenConnections;
  37
+  unsigned int m_IdleTimeout;
  38
+  DllLibNfs *m_pLibNfs;
  39
+  void resetContext();  
  40
+};
  41
+
  42
+extern CNfsConnection gNfsConnection;
  43
+
  44
+namespace XFILE
  45
+{
  46
+  class CFileNFS : public IFile
  47
+  {
  48
+  public:
  49
+    CFileNFS();
  50
+    virtual ~CFileNFS();
  51
+    virtual void Close();
  52
+    virtual int64_t Seek(int64_t iFilePosition, int iWhence = SEEK_SET);
  53
+    virtual unsigned int Read(void* lpBuf, int64_t uiBufSize);
  54
+    virtual bool Open(const CURL& url);
  55
+    virtual bool Exists(const CURL& url);
  56
+    virtual int Stat(const CURL& url, struct __stat64* buffer);
  57
+    virtual int Stat(struct __stat64* buffer);
  58
+    virtual int64_t GetLength();
  59
+    virtual int64_t GetPosition();
  60
+    virtual int Write(const void* lpBuf, int64_t uiBufSize);
  61
+    //implement iocontrol for seek_possible for preventing the stat in File class for
  62
+    //getting this info ...
  63
+    virtual int IoControl(EIoControl request, void* param){ if(request == IOCTRL_SEEK_POSSIBLE) return 1;return -1;};    
  64
+    virtual int  GetChunkSize() {return 1;}
  65
+    
  66
+    virtual bool OpenForWrite(const CURL& url, bool bOverWrite = false);
  67
+    virtual bool Delete(const CURL& url);
  68
+    virtual bool Rename(const CURL& url, const CURL& urlnew);    
  69
+  protected:
  70
+    CURL m_url;
  71
+    bool IsValidFile(const CStdString& strFileName);
  72
+    int64_t m_fileSize;
  73
+    struct nfsfh  *m_pFileHandle;
  74
+  };
  75
+}
  76
+#endif // FILENFS_H_
  77
+
5  xbmc/filesystem/Makefile.in
@@ -93,6 +93,11 @@ SRCS+=FileRar.cpp \
93 93
       RarManager.cpp \
94 94
 
95 95
 endif
  96
+ifeq (@USE_LIBNFS@,1)
  97
+SRCS+=FileNFS.cpp \
  98
+      NFSDirectory.cpp \
  99
+
  100
+endif
96 101
 
97 102
 INCLUDES+=-I@abs_top_srcdir@/lib/libUPnP/Platinum/Source/Core \
98 103
           -I@abs_top_srcdir@/lib/libUPnP/Platinum/Source/Platinum \
206  xbmc/filesystem/NFSDirectory.cpp
... ...
@@ -0,0 +1,206 @@
  1
+/*
  2
+ *      Copyright (C) 2011 Team XBMC
  3
+ *      http://www.xbmc.org
  4
+ *
  5
+ *  This Program is free software; you can redistribute it and/or modify
  6
+ *  it under the terms of the GNU General Public License as published by
  7
+ *  the Free Software Foundation; either version 2, or (at your option)
  8
+ *  any later version.
  9
+ *
  10
+ *  This Program is distributed in the hope that it will be useful,
  11
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13
+ *  GNU General Public License for more details.
  14
+ *
  15
+ *  You should have received a copy of the GNU General Public License
  16
+ *  along with XBMC; see the file COPYING.  If not, write to
  17
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  18
+ *  http://www.gnu.org/copyleft/gpl.html
  19
+ *
  20
+ */
  21
+
  22
+#include "system.h"
  23
+
  24
+#ifdef HAS_FILESYSTEM_NFS
  25
+#include "DllLibNfs.h"
  26
+#include "NFSDirectory.h"
  27
+#include "FileItem.h"
  28
+#include "utils/log.h"
  29
+#include "utils/URIUtils.h"
  30
+#include "threads/SingleLock.h"
  31
+
  32
+using namespace XFILE;
  33
+using namespace std;
  34
+
  35
+CNFSDirectory::CNFSDirectory(void)
  36
+{
  37
+  gNfsConnection.AddActiveConnection();
  38
+}
  39
+
  40
+CNFSDirectory::~CNFSDirectory(void)
  41
+{
  42
+  gNfsConnection.AddIdleConnection();
  43
+}
  44
+
  45
+bool CNFSDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items)
  46
+{
  47
+  // We accept nfs://server/path[/file]]]]
  48
+  int ret = 0;
  49
+  FILETIME fileTime, localTime;    
  50
+  CSingleLock lock(gNfsConnection);
  51
+  
  52
+  CURL url(strPath);
  53
+  
  54
+  if(!gNfsConnection.Connect(url))
  55
+  {
  56
+    return false;
  57
+  }
  58
+  
  59
+  CStdString strDirName="//";//relative to the strPath we connected - we want to get the "/" directory then
  60
+    
  61
+  vector<CStdString> vecEntries;
  62
+  struct nfsdir *nfsdir = NULL;
  63
+  struct nfsdirent *nfsdirent = NULL;
  64
+
  65
+  ret = gNfsConnection.GetImpl()->nfs_opendir_sync(gNfsConnection.GetNfsContext(), strDirName.c_str(), &nfsdir);
  66
+	
  67
+  if(ret != 0)
  68
+  {
  69
+    CLog::Log(LOGERROR, "Failed to open(%s) %s\n", strDirName.c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
  70
+    return false;
  71
+  }
  72
+  lock.Leave();
  73
+  
  74
+  while((nfsdirent = gNfsConnection.GetImpl()->nfs_readdir(gNfsConnection.GetNfsContext(), nfsdir)) != NULL) 
  75
+  {
  76
+    vecEntries.push_back(nfsdirent->name);
  77
+  }
  78
+  
  79
+  lock.Enter();
  80
+  gNfsConnection.GetImpl()->nfs_closedir(gNfsConnection.GetNfsContext(), nfsdir);//close the dir
  81
+  lock.Leave();
  82
+      
  83
+  for (size_t i=0; i<vecEntries.size(); i++)
  84
+  {
  85
+    CStdString strName = vecEntries[i];
  86
+   
  87
+    if (!strName.Equals(".") && !strName.Equals("..")
  88
+      && !strName.Equals("lost+found"))
  89
+    {
  90
+      int64_t iSize = 0;
  91
+      bool bIsDir = false;
  92
+      int64_t lTimeDate = 0;
  93
+      struct stat info = {0};
  94
+
  95
+      CStdString strFullName = strDirName + strName;          
  96
+
  97
+      lock.Enter();
  98
+      ret = gNfsConnection.GetImpl()->nfs_stat_sync(gNfsConnection.GetNfsContext(), strFullName.c_str(), &info);
  99
+      lock.Leave();
  100
+      
  101
+      if( ret == 0 )
  102
+      {