[windows] Fix MAC Address Discovery
[xbmc.git] / xbmc / filesystem / NFSDirectory.cpp
blob6d3588dccc96fb78d6b2aa3212cacb66428fb8b0
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 "FileItemList.h"
17 #include "NFSDirectory.h"
18 #include "utils/StringUtils.h"
19 #include "utils/URIUtils.h"
20 #include "utils/XTimeUtils.h"
21 #include "utils/log.h"
23 #ifdef TARGET_WINDOWS
24 #include <sys\stat.h>
25 #endif
27 using namespace XFILE;
28 #include <limits.h>
29 #include <nfsc/libnfs.h>
30 #include <nfsc/libnfs-raw-nfs.h>
32 #if defined(TARGET_WINDOWS)
33 #define S_IFLNK 0120000
34 #define S_ISBLK(m) (0)
35 #define S_ISSOCK(m) (0)
36 #define S_ISLNK(m) ((m & S_IFLNK) != 0)
37 #define S_ISCHR(m) ((m & _S_IFCHR) != 0)
38 #define S_ISDIR(m) ((m & _S_IFDIR) != 0)
39 #define S_ISFIFO(m) ((m & _S_IFIFO) != 0)
40 #define S_ISREG(m) ((m & _S_IFREG) != 0)
41 #endif
43 CNFSDirectory::CNFSDirectory(void)
45 gNfsConnection.AddActiveConnection();
48 CNFSDirectory::~CNFSDirectory(void)
50 gNfsConnection.AddIdleConnection();
53 bool CNFSDirectory::GetDirectoryFromExportList(const std::string& strPath, CFileItemList &items)
55 CURL url(strPath);
56 std::string nonConstStrPath(strPath);
57 std::list<std::string> exportList=gNfsConnection.GetExportList(url);
59 for (const std::string& it : exportList)
61 const std::string& currentExport(it);
62 URIUtils::RemoveSlashAtEnd(nonConstStrPath);
64 CFileItemPtr pItem(new CFileItem(currentExport));
65 std::string path(nonConstStrPath + currentExport);
66 URIUtils::AddSlashAtEnd(path);
67 pItem->SetPath(path);
68 pItem->m_dateTime = 0;
70 pItem->m_bIsFolder = true;
71 items.Add(pItem);
74 return exportList.empty() ? false : true;
77 bool CNFSDirectory::GetServerList(CFileItemList &items)
79 struct nfs_server_list *srvrs;
80 struct nfs_server_list *srv;
81 bool ret = false;
83 srvrs = nfs_find_local_servers();
85 for (srv=srvrs; srv; srv = srv->next)
87 std::string currentExport(srv->addr);
89 CFileItemPtr pItem(new CFileItem(currentExport));
90 std::string path("nfs://" + currentExport);
91 URIUtils::AddSlashAtEnd(path);
92 pItem->m_dateTime=0;
94 pItem->SetPath(path);
95 pItem->m_bIsFolder = true;
96 items.Add(pItem);
97 ret = true; //added at least one entry
99 free_nfs_srvr_list(srvrs);
101 return ret;
104 bool CNFSDirectory::ResolveSymlink( const std::string &dirName, struct nfsdirent *dirent, CURL &resolvedUrl)
106 std::unique_lock<CCriticalSection> lock(gNfsConnection);
107 int ret = 0;
108 bool retVal = true;
109 std::string fullpath = dirName;
110 char resolvedLink[MAX_PATH];
112 URIUtils::AddSlashAtEnd(fullpath);
113 fullpath.append(dirent->name);
115 resolvedUrl.Reset();
116 resolvedUrl.SetPort(2049);
117 resolvedUrl.SetProtocol("nfs");
118 resolvedUrl.SetHostName(gNfsConnection.GetConnectedIp());
120 ret = nfs_readlink(gNfsConnection.GetNfsContext(), fullpath.c_str(), resolvedLink, MAX_PATH);
122 if(ret == 0)
124 nfs_stat_64 tmpBuffer = {};
125 fullpath = dirName;
126 URIUtils::AddSlashAtEnd(fullpath);
127 fullpath.append(resolvedLink);
129 //special case - if link target is absolute it could be even another export
130 //intervolume symlinks baby ...
131 if(resolvedLink[0] == '/')
133 //use the special stat function for using an extra context
134 //because we are inside of a dir traversal
135 //and just can't change the global nfs context here
136 //without destroying something...
137 fullpath = resolvedLink;
138 resolvedUrl.SetFileName(fullpath);
139 ret = gNfsConnection.stat(resolvedUrl, &tmpBuffer);
141 else
143 ret = nfs_stat64(gNfsConnection.GetNfsContext(), fullpath.c_str(), &tmpBuffer);
144 resolvedUrl.SetFileName(gNfsConnection.GetConnectedExport() + fullpath);
147 if (ret != 0)
149 CLog::Log(LOGERROR, "NFS: Failed to stat({}) on link resolve {}", fullpath,
150 nfs_get_error(gNfsConnection.GetNfsContext()));
151 retVal = false;
153 else
155 dirent->inode = tmpBuffer.nfs_ino;
156 dirent->mode = tmpBuffer.nfs_mode;
157 dirent->size = tmpBuffer.nfs_size;
158 dirent->atime.tv_sec = tmpBuffer.nfs_atime;
159 dirent->mtime.tv_sec = tmpBuffer.nfs_mtime;
160 dirent->ctime.tv_sec = tmpBuffer.nfs_ctime;
162 //map stat mode to nf3type
163 if (S_ISBLK(tmpBuffer.nfs_mode))
165 dirent->type = NF3BLK;
167 else if (S_ISCHR(tmpBuffer.nfs_mode))
169 dirent->type = NF3CHR;
171 else if (S_ISDIR(tmpBuffer.nfs_mode))
173 dirent->type = NF3DIR;
175 else if (S_ISFIFO(tmpBuffer.nfs_mode))
177 dirent->type = NF3FIFO;
179 else if (S_ISREG(tmpBuffer.nfs_mode))
181 dirent->type = NF3REG;
183 else if (S_ISLNK(tmpBuffer.nfs_mode))
185 dirent->type = NF3LNK;
187 else if (S_ISSOCK(tmpBuffer.nfs_mode))
189 dirent->type = NF3SOCK;
193 else
195 CLog::Log(LOGERROR, "Failed to readlink({}) {}", fullpath,
196 nfs_get_error(gNfsConnection.GetNfsContext()));
197 retVal = false;
199 return retVal;
202 bool CNFSDirectory::GetDirectory(const CURL& url, CFileItemList &items)
204 // We accept nfs://server/path[/file]]]]
205 int ret = 0;
206 KODI::TIME::FileTime fileTime, localTime;
207 std::unique_lock<CCriticalSection> lock(gNfsConnection);
208 std::string strDirName="";
209 std::string myStrPath(url.Get());
210 URIUtils::AddSlashAtEnd(myStrPath); //be sure the dir ends with a slash
212 if(!gNfsConnection.Connect(url,strDirName))
214 //connect has failed - so try to get the exported filesystems if no path is given to the url
215 if(url.GetShareName().empty())
217 if(url.GetHostName().empty())
219 return GetServerList(items);
221 else
223 return GetDirectoryFromExportList(myStrPath, items);
226 else
228 return false;
232 struct nfsdir *nfsdir = NULL;
233 struct nfsdirent *nfsdirent = NULL;
235 ret = nfs_opendir(gNfsConnection.GetNfsContext(), strDirName.c_str(), &nfsdir);
237 if(ret != 0)
239 CLog::Log(LOGERROR, "Failed to open({}) {}", strDirName,
240 nfs_get_error(gNfsConnection.GetNfsContext()));
241 return false;
243 lock.unlock();
245 while((nfsdirent = nfs_readdir(gNfsConnection.GetNfsContext(), nfsdir)) != NULL)
247 struct nfsdirent tmpDirent = *nfsdirent;
248 std::string strName = tmpDirent.name;
249 std::string path(myStrPath + strName);
250 int64_t iSize = 0;
251 bool bIsDir = false;
252 int64_t lTimeDate = 0;
254 //reslove symlinks
255 if(tmpDirent.type == NF3LNK)
257 CURL linkUrl;
258 //resolve symlink changes tmpDirent and strName
259 if(!ResolveSymlink(strDirName,&tmpDirent,linkUrl))
261 continue;
264 path = linkUrl.Get();
267 iSize = tmpDirent.size;
268 bIsDir = tmpDirent.type == NF3DIR;
269 lTimeDate = tmpDirent.mtime.tv_sec;
271 if (!StringUtils::EqualsNoCase(strName,".") && !StringUtils::EqualsNoCase(strName,"..")
272 && !StringUtils::EqualsNoCase(strName,"lost+found"))
274 if(lTimeDate == 0) // if modification date is missing, use create date
276 lTimeDate = tmpDirent.ctime.tv_sec;
279 long long ll = lTimeDate & 0xffffffff;
280 ll *= 10000000ll;
281 ll += 116444736000000000ll;
282 fileTime.lowDateTime = (DWORD)(ll & 0xffffffff);
283 fileTime.highDateTime = (DWORD)(ll >> 32);
284 KODI::TIME::FileTimeToLocalFileTime(&fileTime, &localTime);
286 CFileItemPtr pItem(new CFileItem(tmpDirent.name));
287 pItem->m_dateTime=localTime;
288 pItem->m_dwSize = iSize;
290 if (bIsDir)
292 URIUtils::AddSlashAtEnd(path);
293 pItem->m_bIsFolder = true;
295 else
297 pItem->m_bIsFolder = false;
300 if (strName[0] == '.')
302 pItem->SetProperty("file:hidden", true);
304 pItem->SetPath(path);
305 items.Add(pItem);
309 lock.lock();
310 nfs_closedir(gNfsConnection.GetNfsContext(), nfsdir);//close the dir
311 lock.unlock();
312 return true;
315 bool CNFSDirectory::Create(const CURL& url2)
317 int ret = 0;
318 bool success=true;
320 std::unique_lock<CCriticalSection> lock(gNfsConnection);
321 std::string folderName(url2.Get());
322 URIUtils::RemoveSlashAtEnd(folderName);//mkdir fails if a slash is at the end!!!
323 CURL url(folderName);
324 folderName = "";
326 if(!gNfsConnection.Connect(url,folderName))
327 return false;
329 ret = nfs_mkdir(gNfsConnection.GetNfsContext(), folderName.c_str());
331 success = (ret == 0 || -EEXIST == ret);
332 if(!success)
333 CLog::Log(LOGERROR, "NFS: Failed to create({}) {}", folderName,
334 nfs_get_error(gNfsConnection.GetNfsContext()));
335 return success;
338 bool CNFSDirectory::Remove(const CURL& url2)
340 int ret = 0;
342 std::unique_lock<CCriticalSection> lock(gNfsConnection);
343 std::string folderName(url2.Get());
344 URIUtils::RemoveSlashAtEnd(folderName);//rmdir fails if a slash is at the end!!!
345 CURL url(folderName);
346 folderName = "";
348 if(!gNfsConnection.Connect(url,folderName))
349 return false;
351 ret = nfs_rmdir(gNfsConnection.GetNfsContext(), folderName.c_str());
353 if(ret != 0 && errno != ENOENT)
355 CLog::Log(LOGERROR, "{} - Error( {} )", __FUNCTION__,
356 nfs_get_error(gNfsConnection.GetNfsContext()));
357 return false;
359 return true;
362 bool CNFSDirectory::Exists(const CURL& url2)
364 int ret = 0;
366 std::unique_lock<CCriticalSection> lock(gNfsConnection);
367 std::string folderName(url2.Get());
368 URIUtils::RemoveSlashAtEnd(folderName);//remove slash at end or URIUtils::GetFileName won't return what we want...
369 CURL url(folderName);
370 folderName = "";
372 if(!gNfsConnection.Connect(url,folderName))
373 return false;
375 nfs_stat_64 info;
376 ret = nfs_stat64(gNfsConnection.GetNfsContext(), folderName.c_str(), &info);
378 if (ret != 0)
380 return false;
382 return S_ISDIR(info.nfs_mode) ? true : false;