[PVR][Estuary] Timer settings dialog: Show client name in timer type selection dialog...
[xbmc.git] / xbmc / filesystem / NFSDirectory.cpp
blob7feba534c73f074dda5c1944d2aac5f2176b0696
1 /*
2 * Copyright (C) 2011-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #ifdef TARGET_WINDOWS
10 #include <mutex>
12 #include <sys\stat.h>
13 #endif
15 #include "FileItem.h"
16 #include "NFSDirectory.h"
17 #include "utils/StringUtils.h"
18 #include "utils/URIUtils.h"
19 #include "utils/XTimeUtils.h"
20 #include "utils/log.h"
22 #ifdef TARGET_WINDOWS
23 #include <sys\stat.h>
24 #endif
26 using namespace XFILE;
27 #include <limits.h>
28 #include <nfsc/libnfs.h>
29 #include <nfsc/libnfs-raw-nfs.h>
31 #if defined(TARGET_WINDOWS)
32 #define S_IFLNK 0120000
33 #define S_ISBLK(m) (0)
34 #define S_ISSOCK(m) (0)
35 #define S_ISLNK(m) ((m & S_IFLNK) != 0)
36 #define S_ISCHR(m) ((m & _S_IFCHR) != 0)
37 #define S_ISDIR(m) ((m & _S_IFDIR) != 0)
38 #define S_ISFIFO(m) ((m & _S_IFIFO) != 0)
39 #define S_ISREG(m) ((m & _S_IFREG) != 0)
40 #endif
42 CNFSDirectory::CNFSDirectory(void)
44 gNfsConnection.AddActiveConnection();
47 CNFSDirectory::~CNFSDirectory(void)
49 gNfsConnection.AddIdleConnection();
52 bool CNFSDirectory::GetDirectoryFromExportList(const std::string& strPath, CFileItemList &items)
54 CURL url(strPath);
55 std::string nonConstStrPath(strPath);
56 std::list<std::string> exportList=gNfsConnection.GetExportList(url);
58 for (const std::string& it : exportList)
60 const std::string& currentExport(it);
61 URIUtils::RemoveSlashAtEnd(nonConstStrPath);
63 CFileItemPtr pItem(new CFileItem(currentExport));
64 std::string path(nonConstStrPath + currentExport);
65 URIUtils::AddSlashAtEnd(path);
66 pItem->SetPath(path);
67 pItem->m_dateTime = 0;
69 pItem->m_bIsFolder = true;
70 items.Add(pItem);
73 return exportList.empty() ? false : true;
76 bool CNFSDirectory::GetServerList(CFileItemList &items)
78 struct nfs_server_list *srvrs;
79 struct nfs_server_list *srv;
80 bool ret = false;
82 srvrs = nfs_find_local_servers();
84 for (srv=srvrs; srv; srv = srv->next)
86 std::string currentExport(srv->addr);
88 CFileItemPtr pItem(new CFileItem(currentExport));
89 std::string path("nfs://" + currentExport);
90 URIUtils::AddSlashAtEnd(path);
91 pItem->m_dateTime=0;
93 pItem->SetPath(path);
94 pItem->m_bIsFolder = true;
95 items.Add(pItem);
96 ret = true; //added at least one entry
98 free_nfs_srvr_list(srvrs);
100 return ret;
103 bool CNFSDirectory::ResolveSymlink( const std::string &dirName, struct nfsdirent *dirent, CURL &resolvedUrl)
105 std::unique_lock<CCriticalSection> lock(gNfsConnection);
106 int ret = 0;
107 bool retVal = true;
108 std::string fullpath = dirName;
109 char resolvedLink[MAX_PATH];
111 URIUtils::AddSlashAtEnd(fullpath);
112 fullpath.append(dirent->name);
114 resolvedUrl.Reset();
115 resolvedUrl.SetPort(2049);
116 resolvedUrl.SetProtocol("nfs");
117 resolvedUrl.SetHostName(gNfsConnection.GetConnectedIp());
119 ret = nfs_readlink(gNfsConnection.GetNfsContext(), fullpath.c_str(), resolvedLink, MAX_PATH);
121 if(ret == 0)
123 nfs_stat_64 tmpBuffer = {};
124 fullpath = dirName;
125 URIUtils::AddSlashAtEnd(fullpath);
126 fullpath.append(resolvedLink);
128 //special case - if link target is absolute it could be even another export
129 //intervolume symlinks baby ...
130 if(resolvedLink[0] == '/')
132 //use the special stat function for using an extra context
133 //because we are inside of a dir traversal
134 //and just can't change the global nfs context here
135 //without destroying something...
136 fullpath = resolvedLink;
137 resolvedUrl.SetFileName(fullpath);
138 ret = gNfsConnection.stat(resolvedUrl, &tmpBuffer);
140 else
142 ret = nfs_stat64(gNfsConnection.GetNfsContext(), fullpath.c_str(), &tmpBuffer);
143 resolvedUrl.SetFileName(gNfsConnection.GetConnectedExport() + fullpath);
146 if (ret != 0)
148 CLog::Log(LOGERROR, "NFS: Failed to stat({}) on link resolve {}", fullpath,
149 nfs_get_error(gNfsConnection.GetNfsContext()));
150 retVal = false;
152 else
154 dirent->inode = tmpBuffer.nfs_ino;
155 dirent->mode = tmpBuffer.nfs_mode;
156 dirent->size = tmpBuffer.nfs_size;
157 dirent->atime.tv_sec = tmpBuffer.nfs_atime;
158 dirent->mtime.tv_sec = tmpBuffer.nfs_mtime;
159 dirent->ctime.tv_sec = tmpBuffer.nfs_ctime;
161 //map stat mode to nf3type
162 if (S_ISBLK(tmpBuffer.nfs_mode))
164 dirent->type = NF3BLK;
166 else if (S_ISCHR(tmpBuffer.nfs_mode))
168 dirent->type = NF3CHR;
170 else if (S_ISDIR(tmpBuffer.nfs_mode))
172 dirent->type = NF3DIR;
174 else if (S_ISFIFO(tmpBuffer.nfs_mode))
176 dirent->type = NF3FIFO;
178 else if (S_ISREG(tmpBuffer.nfs_mode))
180 dirent->type = NF3REG;
182 else if (S_ISLNK(tmpBuffer.nfs_mode))
184 dirent->type = NF3LNK;
186 else if (S_ISSOCK(tmpBuffer.nfs_mode))
188 dirent->type = NF3SOCK;
192 else
194 CLog::Log(LOGERROR, "Failed to readlink({}) {}", fullpath,
195 nfs_get_error(gNfsConnection.GetNfsContext()));
196 retVal = false;
198 return retVal;
201 bool CNFSDirectory::GetDirectory(const CURL& url, CFileItemList &items)
203 // We accept nfs://server/path[/file]]]]
204 int ret = 0;
205 KODI::TIME::FileTime fileTime, localTime;
206 std::unique_lock<CCriticalSection> lock(gNfsConnection);
207 std::string strDirName="";
208 std::string myStrPath(url.Get());
209 URIUtils::AddSlashAtEnd(myStrPath); //be sure the dir ends with a slash
211 if(!gNfsConnection.Connect(url,strDirName))
213 //connect has failed - so try to get the exported filesystems if no path is given to the url
214 if(url.GetShareName().empty())
216 if(url.GetHostName().empty())
218 return GetServerList(items);
220 else
222 return GetDirectoryFromExportList(myStrPath, items);
225 else
227 return false;
231 struct nfsdir *nfsdir = NULL;
232 struct nfsdirent *nfsdirent = NULL;
234 ret = nfs_opendir(gNfsConnection.GetNfsContext(), strDirName.c_str(), &nfsdir);
236 if(ret != 0)
238 CLog::Log(LOGERROR, "Failed to open({}) {}", strDirName,
239 nfs_get_error(gNfsConnection.GetNfsContext()));
240 return false;
242 lock.unlock();
244 while((nfsdirent = nfs_readdir(gNfsConnection.GetNfsContext(), nfsdir)) != NULL)
246 struct nfsdirent tmpDirent = *nfsdirent;
247 std::string strName = tmpDirent.name;
248 std::string path(myStrPath + strName);
249 int64_t iSize = 0;
250 bool bIsDir = false;
251 int64_t lTimeDate = 0;
253 //reslove symlinks
254 if(tmpDirent.type == NF3LNK)
256 CURL linkUrl;
257 //resolve symlink changes tmpDirent and strName
258 if(!ResolveSymlink(strDirName,&tmpDirent,linkUrl))
260 continue;
263 path = linkUrl.Get();
266 iSize = tmpDirent.size;
267 bIsDir = tmpDirent.type == NF3DIR;
268 lTimeDate = tmpDirent.mtime.tv_sec;
270 if (!StringUtils::EqualsNoCase(strName,".") && !StringUtils::EqualsNoCase(strName,"..")
271 && !StringUtils::EqualsNoCase(strName,"lost+found"))
273 if(lTimeDate == 0) // if modification date is missing, use create date
275 lTimeDate = tmpDirent.ctime.tv_sec;
278 long long ll = lTimeDate & 0xffffffff;
279 ll *= 10000000ll;
280 ll += 116444736000000000ll;
281 fileTime.lowDateTime = (DWORD)(ll & 0xffffffff);
282 fileTime.highDateTime = (DWORD)(ll >> 32);
283 KODI::TIME::FileTimeToLocalFileTime(&fileTime, &localTime);
285 CFileItemPtr pItem(new CFileItem(tmpDirent.name));
286 pItem->m_dateTime=localTime;
287 pItem->m_dwSize = iSize;
289 if (bIsDir)
291 URIUtils::AddSlashAtEnd(path);
292 pItem->m_bIsFolder = true;
294 else
296 pItem->m_bIsFolder = false;
299 if (strName[0] == '.')
301 pItem->SetProperty("file:hidden", true);
303 pItem->SetPath(path);
304 items.Add(pItem);
308 lock.lock();
309 nfs_closedir(gNfsConnection.GetNfsContext(), nfsdir);//close the dir
310 lock.unlock();
311 return true;
314 bool CNFSDirectory::Create(const CURL& url2)
316 int ret = 0;
317 bool success=true;
319 std::unique_lock<CCriticalSection> lock(gNfsConnection);
320 std::string folderName(url2.Get());
321 URIUtils::RemoveSlashAtEnd(folderName);//mkdir fails if a slash is at the end!!!
322 CURL url(folderName);
323 folderName = "";
325 if(!gNfsConnection.Connect(url,folderName))
326 return false;
328 ret = nfs_mkdir(gNfsConnection.GetNfsContext(), folderName.c_str());
330 success = (ret == 0 || -EEXIST == ret);
331 if(!success)
332 CLog::Log(LOGERROR, "NFS: Failed to create({}) {}", folderName,
333 nfs_get_error(gNfsConnection.GetNfsContext()));
334 return success;
337 bool CNFSDirectory::Remove(const CURL& url2)
339 int ret = 0;
341 std::unique_lock<CCriticalSection> lock(gNfsConnection);
342 std::string folderName(url2.Get());
343 URIUtils::RemoveSlashAtEnd(folderName);//rmdir fails if a slash is at the end!!!
344 CURL url(folderName);
345 folderName = "";
347 if(!gNfsConnection.Connect(url,folderName))
348 return false;
350 ret = nfs_rmdir(gNfsConnection.GetNfsContext(), folderName.c_str());
352 if(ret != 0 && errno != ENOENT)
354 CLog::Log(LOGERROR, "{} - Error( {} )", __FUNCTION__,
355 nfs_get_error(gNfsConnection.GetNfsContext()));
356 return false;
358 return true;
361 bool CNFSDirectory::Exists(const CURL& url2)
363 int ret = 0;
365 std::unique_lock<CCriticalSection> lock(gNfsConnection);
366 std::string folderName(url2.Get());
367 URIUtils::RemoveSlashAtEnd(folderName);//remove slash at end or URIUtils::GetFileName won't return what we want...
368 CURL url(folderName);
369 folderName = "";
371 if(!gNfsConnection.Connect(url,folderName))
372 return false;
374 nfs_stat_64 info;
375 ret = nfs_stat64(gNfsConnection.GetNfsContext(), folderName.c_str(), &info);
377 if (ret != 0)
379 return false;
381 return S_ISDIR(info.nfs_mode) ? true : false;