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 "threads/SingleLock.h"
36 using namespace XFILE;
39 #include <nfsc/libnfs-raw-mount.h>
40 #include <nfsc/libnfs-raw-nfs.h>
42 CNFSDirectory::CNFSDirectory(void)
44 gNfsConnection.AddActiveConnection();
47 CNFSDirectory::~CNFSDirectory(void)
49 gNfsConnection.AddIdleConnection();
52 bool CNFSDirectory::GetDirectoryFromExportList(const CStdString& strPath, CFileItemList &items)
55 CStdString nonConstStrPath(strPath);
56 std::list<CStdString> exportList=gNfsConnection.GetExportList(url);
57 std::list<CStdString>::iterator it;
59 for(it=exportList.begin();it!=exportList.end();it++)
61 CStdString currentExport(*it);
62 URIUtils::RemoveSlashAtEnd(nonConstStrPath);
64 CFileItemPtr pItem(new CFileItem(currentExport));
65 CStdString path(nonConstStrPath + currentExport);
66 URIUtils::AddSlashAtEnd(path);
70 pItem->m_bIsFolder = true;
74 return exportList.empty()? false : true;
77 bool CNFSDirectory::GetServerList(CFileItemList &items)
79 struct nfs_server_list *srvrs;
80 struct nfs_server_list *srv;
83 if(!gNfsConnection.HandleDyLoad())
88 srvrs = gNfsConnection.GetImpl()->nfs_find_local_servers();
90 for (srv=srvrs; srv; srv = srv->next)
92 CStdString currentExport(srv->addr);
94 CFileItemPtr pItem(new CFileItem(currentExport));
95 CStdString path("nfs://" + currentExport);
96 URIUtils::AddSlashAtEnd(path);
100 pItem->m_bIsFolder = true;
102 ret = true; //added at least one entry
104 gNfsConnection.GetImpl()->free_nfs_srvr_list(srvrs);
109 bool CNFSDirectory::ResolveSymlink( const CStdString &dirName, struct nfsdirent *dirent, CURL &resolvedUrl)
111 CSingleLock lock(gNfsConnection);
114 CStdString fullpath = dirName;
115 char resolvedLink[MAX_PATH];
117 URIUtils::AddSlashAtEnd(fullpath);
118 fullpath.append(dirent->name);
121 resolvedUrl.SetPort(2049);
122 resolvedUrl.SetProtocol("nfs");
123 resolvedUrl.SetHostName(gNfsConnection.GetConnectedIp());
125 ret = gNfsConnection.GetImpl()->nfs_readlink(gNfsConnection.GetNfsContext(), fullpath.c_str(), resolvedLink, MAX_PATH);
129 struct stat tmpBuffer = {0};
131 URIUtils::AddSlashAtEnd(fullpath);
132 fullpath.append(resolvedLink);
134 //special case - if link target is absolute it could be even another export
135 //intervolume symlinks baby ...
136 if(resolvedLink[0] == '/')
138 //use the special stat function for using an extra context
139 //because we are inside of a dir traversation
140 //and just can't change the global nfs context here
141 //without destroying something...
142 fullpath = resolvedLink;
143 resolvedUrl.SetFileName(fullpath);
144 ret = gNfsConnection.stat(resolvedUrl, &tmpBuffer);
148 ret = gNfsConnection.GetImpl()->nfs_stat(gNfsConnection.GetNfsContext(), fullpath.c_str(), &tmpBuffer);
149 resolvedUrl.SetFileName(gNfsConnection.GetConnectedExport() + fullpath);
154 CLog::Log(LOGERROR, "NFS: Failed to stat(%s) on link resolve %s\n", fullpath.c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
159 dirent->inode = tmpBuffer.st_ino;
160 dirent->mode = tmpBuffer.st_mode;
161 dirent->size = tmpBuffer.st_size;
162 dirent->atime.tv_sec = tmpBuffer.st_atime;
163 dirent->mtime.tv_sec = tmpBuffer.st_mtime;
164 dirent->ctime.tv_sec = tmpBuffer.st_ctime;
166 //map stat mode to nf3type
167 if(S_ISBLK(tmpBuffer.st_mode)){ dirent->type = NF3BLK; }
168 else if(S_ISCHR(tmpBuffer.st_mode)){ dirent->type = NF3CHR; }
169 else if(S_ISDIR(tmpBuffer.st_mode)){ dirent->type = NF3DIR; }
170 else if(S_ISFIFO(tmpBuffer.st_mode)){ dirent->type = NF3FIFO; }
171 else if(S_ISREG(tmpBuffer.st_mode)){ dirent->type = NF3REG; }
172 else if(S_ISLNK(tmpBuffer.st_mode)){ dirent->type = NF3LNK; }
173 else if(S_ISSOCK(tmpBuffer.st_mode)){ dirent->type = NF3SOCK; }
178 CLog::Log(LOGERROR, "Failed to readlink(%s) %s\n", fullpath.c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
184 bool CNFSDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items)
186 // We accept nfs://server/path[/file]]]]
188 FILETIME fileTime, localTime;
189 CSingleLock lock(gNfsConnection);
191 CStdString strDirName="";
192 CStdString myStrPath(strPath);
193 URIUtils::AddSlashAtEnd(myStrPath); //be sure the dir ends with a slash
195 if(!gNfsConnection.Connect(url,strDirName))
197 //connect has failed - so try to get the exported filesystms if no path is given to the url
198 if(url.GetShareName().Equals(""))
200 if(url.GetHostName().Equals(""))
202 return GetServerList(items);
206 return GetDirectoryFromExportList(myStrPath, items);
215 struct nfsdir *nfsdir = NULL;
216 struct nfsdirent *nfsdirent = NULL;
218 ret = gNfsConnection.GetImpl()->nfs_opendir(gNfsConnection.GetNfsContext(), strDirName.c_str(), &nfsdir);
222 CLog::Log(LOGERROR, "Failed to open(%s) %s\n", strDirName.c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
227 while((nfsdirent = gNfsConnection.GetImpl()->nfs_readdir(gNfsConnection.GetNfsContext(), nfsdir)) != NULL)
229 CStdString strName = nfsdirent->name;
230 CStdString path(myStrPath + strName);
233 int64_t lTimeDate = 0;
236 if(nfsdirent->type == NF3LNK)
239 //resolve symlink changes nfsdirent and strName
240 if(!ResolveSymlink(strDirName,nfsdirent,linkUrl))
245 path = linkUrl.Get();
248 iSize = nfsdirent->size;
249 bIsDir = nfsdirent->type == NF3DIR;
250 lTimeDate = nfsdirent->mtime.tv_sec;
252 if (!strName.Equals(".") && !strName.Equals("..")
253 && !strName.Equals("lost+found"))
255 if(lTimeDate == 0) // if modification date is missing, use create date
257 lTimeDate = nfsdirent->ctime.tv_sec;
260 LONGLONG ll = Int32x32To64(lTimeDate & 0xffffffff, 10000000) + 116444736000000000ll;
261 fileTime.dwLowDateTime = (DWORD) (ll & 0xffffffff);
262 fileTime.dwHighDateTime = (DWORD)(ll >> 32);
263 FileTimeToLocalFileTime(&fileTime, &localTime);
265 CFileItemPtr pItem(new CFileItem(nfsdirent->name));
266 pItem->m_dateTime=localTime;
267 pItem->m_dwSize = iSize;
271 URIUtils::AddSlashAtEnd(path);
272 pItem->m_bIsFolder = true;
276 pItem->m_bIsFolder = false;
279 if (strName[0] == '.')
281 pItem->SetProperty("file:hidden", true);
283 pItem->SetPath(path);
289 gNfsConnection.GetImpl()->nfs_closedir(gNfsConnection.GetNfsContext(), nfsdir);//close the dir
294 bool CNFSDirectory::Create(const char* strPath)
299 CSingleLock lock(gNfsConnection);
300 CStdString folderName(strPath);
301 URIUtils::RemoveSlashAtEnd(folderName);//mkdir fails if a slash is at the end!!!
302 CURL url(folderName);
305 if(!gNfsConnection.Connect(url,folderName))
308 ret = gNfsConnection.GetImpl()->nfs_mkdir(gNfsConnection.GetNfsContext(), folderName.c_str());
310 success = (ret == 0 || -EEXIST == ret);
312 CLog::Log(LOGERROR, "NFS: Failed to create(%s) %s\n", folderName.c_str(), gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
316 bool CNFSDirectory::Remove(const char* strPath)
320 CSingleLock lock(gNfsConnection);
321 CStdString folderName(strPath);
322 URIUtils::RemoveSlashAtEnd(folderName);//rmdir fails if a slash is at the end!!!
323 CURL url(folderName);
326 if(!gNfsConnection.Connect(url,folderName))
329 ret = gNfsConnection.GetImpl()->nfs_rmdir(gNfsConnection.GetNfsContext(), folderName.c_str());
331 if(ret != 0 && errno != ENOENT)
333 CLog::Log(LOGERROR, "%s - Error( %s )", __FUNCTION__, gNfsConnection.GetImpl()->nfs_get_error(gNfsConnection.GetNfsContext()));
339 bool CNFSDirectory::Exists(const char* strPath)
343 CSingleLock lock(gNfsConnection);
344 CStdString folderName(strPath);
345 URIUtils::RemoveSlashAtEnd(folderName);//remove slash at end or URIUtils::GetFileName won't return what we want...
346 CURL url(folderName);
349 if(!gNfsConnection.Connect(url,folderName))
353 ret = gNfsConnection.GetImpl()->nfs_stat(gNfsConnection.GetNfsContext(), folderName.c_str(), &info);
359 return S_ISDIR(info.st_mode) ? true : false;