2 * Copyright (C) 2011-2013 Team XBMC
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)
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.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
23 #ifdef HAS_FILESYSTEM_NFS
24 #include "DllLibNfs.h"
31 #include "NFSDirectory.h"
33 #include "utils/log.h"
34 #include "utils/URIUtils.h"
35 #include "utils/StringUtils.h"
36 #include "threads/SingleLock.h"
37 using namespace XFILE;
40 #include <nfsc/libnfs-raw-mount.h>
41 #include <nfsc/libnfs-raw-nfs.h>
43 CNFSDirectory::CNFSDirectory(void)
45 gNfsConnection.AddActiveConnection();
48 CNFSDirectory::~CNFSDirectory(void)
50 gNfsConnection.AddIdleConnection();
53 bool CNFSDirectory::GetDirectoryFromExportList(const std::string& strPath, CFileItemList &items)
56 std::string nonConstStrPath(strPath);
57 std::list<std::string> exportList=gNfsConnection.GetExportList(url);
58 std::list<std::string>::iterator it;
60 for(it=exportList.begin();it!=exportList.end();it++)
62 std::string currentExport(*it);
63 URIUtils::RemoveSlashAtEnd(nonConstStrPath);
65 CFileItemPtr pItem(new CFileItem(currentExport));
66 std::string path(nonConstStrPath + currentExport);
67 URIUtils::AddSlashAtEnd(path);
71 pItem->m_bIsFolder = true;
75 return exportList.empty()? false : true;
78 bool CNFSDirectory::GetServerList(CFileItemList &items)
80 struct nfs_server_list *srvrs;
81 struct nfs_server_list *srv;
84 if(!gNfsConnection.HandleDyLoad())
89 srvrs = gNfsConnection.GetImpl()->nfs_find_local_servers();
91 for (srv=srvrs; srv; srv = srv->next)
93 CStdString currentExport(srv->addr);
95 CFileItemPtr pItem(new CFileItem(currentExport));
96 CStdString path("nfs://" + currentExport);
97 URIUtils::AddSlashAtEnd(path);
100 pItem->SetPath(path);
101 pItem->m_bIsFolder = true;
103 ret = true; //added at least one entry
105 gNfsConnection.GetImpl()->free_nfs_srvr_list(srvrs);
110 bool CNFSDirectory::ResolveSymlink( const CStdString &dirName, struct nfsdirent *dirent, CURL &resolvedUrl)
112 CSingleLock lock(gNfsConnection);
115 CStdString fullpath = dirName;
116 char resolvedLink[MAX_PATH];
118 URIUtils::AddSlashAtEnd(fullpath);
119 fullpath.append(dirent->name);
122 resolvedUrl.SetPort(2049);
123 resolvedUrl.SetProtocol("nfs");
124 resolvedUrl.SetHostName(gNfsConnection.GetConnectedIp());
126 ret = gNfsConnection.GetImpl()->nfs_readlink(gNfsConnection.GetNfsContext(), fullpath.c_str(), resolvedLink, MAX_PATH);
130 NFSSTAT tmpBuffer = {0};
132 URIUtils::AddSlashAtEnd(fullpath);
133 fullpath.append(resolvedLink);
135 //special case - if link target is absolute it could be even another export
136 //intervolume symlinks baby ...
137 if(resolvedLink[0] == '/')
139 //use the special stat function for using an extra context
140 //because we are inside of a dir traversation
141 //and just can't change the global nfs context here
142 //without destroying something...
143 fullpath = resolvedLink;
144 resolvedUrl.SetFileName(fullpath);
145 ret = gNfsConnection.stat(resolvedUrl, &tmpBuffer);
149 ret = gNfsConnection.GetImpl()->nfs_stat(gNfsConnection.GetNfsContext(), fullpath.c_str(), &tmpBuffer);
150 resolvedUrl.SetFileName(gNfsConnection.GetConnectedExport() + fullpath);
155 CLog::Log(LOGERROR, "NFS: Failed to stat(%s) on link resolve %s\n", fullpath.c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
160 dirent->inode = tmpBuffer.st_ino;
161 dirent->mode = tmpBuffer.st_mode;
162 dirent->size = tmpBuffer.st_size;
163 dirent->atime.tv_sec = tmpBuffer.st_atime;
164 dirent->mtime.tv_sec = tmpBuffer.st_mtime;
165 dirent->ctime.tv_sec = tmpBuffer.st_ctime;
167 //map stat mode to nf3type
168 if(S_ISBLK(tmpBuffer.st_mode)){ dirent->type = NF3BLK; }
169 else if(S_ISCHR(tmpBuffer.st_mode)){ dirent->type = NF3CHR; }
170 else if(S_ISDIR(tmpBuffer.st_mode)){ dirent->type = NF3DIR; }
171 else if(S_ISFIFO(tmpBuffer.st_mode)){ dirent->type = NF3FIFO; }
172 else if(S_ISREG(tmpBuffer.st_mode)){ dirent->type = NF3REG; }
173 else if(S_ISLNK(tmpBuffer.st_mode)){ dirent->type = NF3LNK; }
174 else if(S_ISSOCK(tmpBuffer.st_mode)){ dirent->type = NF3SOCK; }
179 CLog::Log(LOGERROR, "Failed to readlink(%s) %s\n", fullpath.c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
185 bool CNFSDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items)
187 // We accept nfs://server/path[/file]]]]
189 FILETIME fileTime, localTime;
190 CSingleLock lock(gNfsConnection);
192 CStdString strDirName="";
193 std::string myStrPath(strPath);
194 URIUtils::AddSlashAtEnd(myStrPath); //be sure the dir ends with a slash
196 if(!gNfsConnection.Connect(url,strDirName))
198 //connect has failed - so try to get the exported filesystms if no path is given to the url
199 if(url.GetShareName().Equals(""))
201 if(url.GetHostName().Equals(""))
203 return GetServerList(items);
207 return GetDirectoryFromExportList(myStrPath, items);
216 struct nfsdir *nfsdir = NULL;
217 struct nfsdirent *nfsdirent = NULL;
219 ret = gNfsConnection.GetImpl()->nfs_opendir(gNfsConnection.GetNfsContext(), strDirName.c_str(), &nfsdir);
223 CLog::Log(LOGERROR, "Failed to open(%s) %s\n", strDirName.c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
228 while((nfsdirent = gNfsConnection.GetImpl()->nfs_readdir(gNfsConnection.GetNfsContext(), nfsdir)) != NULL)
230 std::string strName = nfsdirent->name;
231 std::string path(myStrPath + strName);
234 int64_t lTimeDate = 0;
237 if(nfsdirent->type == NF3LNK)
240 //resolve symlink changes nfsdirent and strName
241 if(!ResolveSymlink(strDirName,nfsdirent,linkUrl))
246 path = linkUrl.Get();
249 iSize = nfsdirent->size;
250 bIsDir = nfsdirent->type == NF3DIR;
251 lTimeDate = nfsdirent->mtime.tv_sec;
253 if (!StringUtils::EqualsNoCase(strName,".") && !StringUtils::EqualsNoCase(strName,"..")
254 && !StringUtils::EqualsNoCase(strName,"lost+found"))
256 if(lTimeDate == 0) // if modification date is missing, use create date
258 lTimeDate = nfsdirent->ctime.tv_sec;
261 LONGLONG ll = Int32x32To64(lTimeDate & 0xffffffff, 10000000) + 116444736000000000ll;
262 fileTime.dwLowDateTime = (DWORD) (ll & 0xffffffff);
263 fileTime.dwHighDateTime = (DWORD)(ll >> 32);
264 FileTimeToLocalFileTime(&fileTime, &localTime);
266 CFileItemPtr pItem(new CFileItem(nfsdirent->name));
267 pItem->m_dateTime=localTime;
268 pItem->m_dwSize = iSize;
272 URIUtils::AddSlashAtEnd(path);
273 pItem->m_bIsFolder = true;
277 pItem->m_bIsFolder = false;
280 if (strName[0] == '.')
282 pItem->SetProperty("file:hidden", true);
284 pItem->SetPath(path);
290 gNfsConnection.GetImpl()->nfs_closedir(gNfsConnection.GetNfsContext(), nfsdir);//close the dir
295 bool CNFSDirectory::Create(const char* strPath)
300 CSingleLock lock(gNfsConnection);
301 CStdString folderName(strPath);
302 URIUtils::RemoveSlashAtEnd(folderName);//mkdir fails if a slash is at the end!!!
303 CURL url(folderName);
306 if(!gNfsConnection.Connect(url,folderName))
309 ret = gNfsConnection.GetImpl()->nfs_mkdir(gNfsConnection.GetNfsContext(), folderName.c_str());
311 success = (ret == 0 || -EEXIST == ret);
313 CLog::Log(LOGERROR, "NFS: Failed to create(%s) %s\n", folderName.c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
317 bool CNFSDirectory::Remove(const char* strPath)
321 CSingleLock lock(gNfsConnection);
322 CStdString folderName(strPath);
323 URIUtils::RemoveSlashAtEnd(folderName);//rmdir fails if a slash is at the end!!!
324 CURL url(folderName);
327 if(!gNfsConnection.Connect(url,folderName))
330 ret = gNfsConnection.GetImpl()->nfs_rmdir(gNfsConnection.GetNfsContext(), folderName.c_str());
332 if(ret != 0 && errno != ENOENT)
334 CLog::Log(LOGERROR, "%s - Error( %s )", __FUNCTION__, gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
340 bool CNFSDirectory::Exists(const char* strPath)
344 CSingleLock lock(gNfsConnection);
345 CStdString folderName(strPath);
346 URIUtils::RemoveSlashAtEnd(folderName);//remove slash at end or URIUtils::GetFileName won't return what we want...
347 CURL url(folderName);
350 if(!gNfsConnection.Connect(url,folderName))
354 ret = gNfsConnection.GetImpl()->nfs_stat(gNfsConnection.GetNfsContext(), folderName.c_str(), &info);
360 return S_ISDIR(info.st_mode) ? true : false;