Merge pull request #4857 from t-nelson/Gotham_13.2_backports
[vuplus_xbmc] / xbmc / filesystem / HDFile.cpp
1 /*
2  *      Copyright (c) 2002 Frodo
3  *      Portions Copyright (c) by the authors of ffmpeg and xvid
4  *      Copyright (C) 2002-2013 Team XBMC
5  *      http://xbmc.org
6  *
7  *  This Program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2, or (at your option)
10  *  any later version.
11  *
12  *  This Program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with XBMC; see the file COPYING.  If not, see
19  *  <http://www.gnu.org/licenses/>.
20  *
21  */
22
23 #include "system.h"
24 #include "HDFile.h"
25 #include "Util.h"
26 #include "URL.h"
27 #include "utils/AliasShortcutUtils.h"
28 #ifdef TARGET_POSIX
29 #include "XHandle.h"
30 #endif
31
32 #include <sys/stat.h>
33 #ifdef TARGET_POSIX
34 #include <fcntl.h>
35 #include <sys/ioctl.h>
36 #else
37 #include <io.h>
38 #include "utils/CharsetConverter.h"
39 #include "utils/URIUtils.h"
40 #include "win32/WIN32Util.h"
41 #endif
42 #include "utils/log.h"
43
44 #include <algorithm>
45
46 using namespace XFILE;
47
48 //////////////////////////////////////////////////////////////////////
49 // Construction/Destruction
50 //////////////////////////////////////////////////////////////////////
51
52 //*********************************************************************************************
53 CHDFile::CHDFile()
54     : m_hFile(INVALID_HANDLE_VALUE),
55       m_i64LastDropPos(0)
56 {}
57
58 //*********************************************************************************************
59 CHDFile::~CHDFile()
60 {
61   if (m_hFile != INVALID_HANDLE_VALUE) Close();
62 }
63 //*********************************************************************************************
64 std::string CHDFile::GetLocal(const CURL &url)
65 {
66   std::string path(url.GetFileName());
67
68   if(url.GetProtocol() == "file")
69   {
70     // file://drive[:]/path
71     // file:///drive:/path
72     std::string host(url.GetHostName());
73
74     if(!host.empty())
75     {
76       if(host[host.length()-1] == ':')
77         path = host + "/" + path;
78       else
79         path = host + ":/" + path;
80     }
81   }
82
83   if (IsAliasShortcut(path))
84     TranslateAliasShortcut(path);
85
86   return path;
87 }
88
89 //*********************************************************************************************
90 bool CHDFile::Open(const CURL& url)
91 {
92   std::string strFile(GetLocal(url));
93
94 #ifdef TARGET_WINDOWS
95   m_hFile.attach(CreateFileW(CWIN32Util::ConvertPathToWin32Form(strFile).c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL));
96 #else
97   m_hFile.attach(CreateFile(strFile.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL));
98 #endif
99   if (!m_hFile.isValid()) return false;
100
101   m_i64FilePos = 0;
102   m_i64FileLen = 0;
103   m_i64LastDropPos = 0;
104
105   return true;
106 }
107
108 bool CHDFile::Exists(const CURL& url)
109 {
110   std::string strFile(GetLocal(url));
111
112 #ifdef TARGET_WINDOWS
113   URIUtils::RemoveSlashAtEnd(strFile);
114   DWORD attributes = GetFileAttributesW(CWIN32Util::ConvertPathToWin32Form(strFile).c_str());
115   if(attributes == INVALID_FILE_ATTRIBUTES)
116     return false;
117   return true;
118 #else
119   struct __stat64 buffer;
120   return (_stat64(strFile.c_str(), &buffer)==0);
121 #endif
122 }
123
124 int CHDFile::Stat(struct __stat64* buffer)
125 {
126 #ifdef TARGET_POSIX
127   return _fstat64((*m_hFile).fd, buffer);
128 #else
129   // Duplicate the handle, as retrieving and closing a matching crt handle closes the crt handle AND the original Windows handle.
130   HANDLE hFileDup;
131   if (0 == DuplicateHandle(GetCurrentProcess(), (HANDLE)m_hFile, GetCurrentProcess(), &hFileDup, 0, FALSE, DUPLICATE_SAME_ACCESS))
132   {
133     CLog::Log(LOGERROR, __FUNCTION__" - DuplicateHandle()");
134     return -1;
135   }
136
137   int fd;
138   fd = _open_osfhandle((intptr_t)((HANDLE)hFileDup), 0);
139   if (fd == -1)
140   {
141     CLog::Log(LOGERROR, "Stat: fd == -1");
142     return -1;
143   }
144   int result = _fstat64(fd, buffer);
145   _close(fd);
146   return result;
147 #endif
148 }
149
150 int CHDFile::Stat(const CURL& url, struct __stat64* buffer)
151 {
152   std::string strFile(GetLocal(url));
153
154 #ifdef TARGET_WINDOWS
155   std::wstring strWFile(CWIN32Util::ConvertPathToWin32Form(strFile));
156   /* _wstat64 can't handle long paths therefore we remove the \\?\ */
157   CWIN32Util::RemoveExtraLongPathPrefix(strWFile);
158   // win32 can only stat root drives with a slash at the end
159   if(strWFile.length() == 2 && strWFile[1] == L':')
160     strWFile.push_back(L'\\');
161   /* _wstat64 calls FindFirstFileEx. According to MSDN, the path should not end in a trailing backslash.
162     Remove it before calling _wstat64 */
163   else if (strWFile.length() > 3 && URIUtils::HasSlashAtEnd(strFile))
164     strWFile.pop_back();
165   return _wstat64(strWFile.c_str(), buffer);
166 #else
167   return _stat64(strFile.c_str(), buffer);
168 #endif
169 }
170
171 bool CHDFile::SetHidden(const CURL &url, bool hidden)
172 {
173 #ifdef TARGET_WINDOWS
174   DWORD attributes = hidden ? FILE_ATTRIBUTE_HIDDEN : FILE_ATTRIBUTE_NORMAL;
175   if (SetFileAttributesW(CWIN32Util::ConvertPathToWin32Form(GetLocal(url)).c_str(), attributes))
176     return true;
177 #endif
178   return false;
179 }
180
181 //*********************************************************************************************
182 bool CHDFile::OpenForWrite(const CURL& url, bool bOverWrite)
183 {
184   // make sure it's a legal FATX filename (we are writing to the harddisk)
185   std::string strPath(GetLocal(url));
186
187 #ifdef TARGET_WINDOWS
188   m_hFile.attach(CreateFileW(CWIN32Util::ConvertPathToWin32Form(strPath).c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, bOverWrite ? CREATE_ALWAYS : OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));
189 #else
190   m_hFile.attach(CreateFile(strPath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, bOverWrite ? CREATE_ALWAYS : OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));
191 #endif
192   if (!m_hFile.isValid())
193     return false;
194
195   m_i64FilePos = 0;
196   Seek(0, SEEK_SET);
197
198   return true;
199 }
200
201 //*********************************************************************************************
202 unsigned int CHDFile::Read(void *lpBuf, int64_t uiBufSize)
203 {
204   if (!m_hFile.isValid()) return 0;
205   DWORD nBytesRead;
206   if ( ReadFile((HANDLE)m_hFile, lpBuf, (DWORD)uiBufSize, &nBytesRead, NULL) )
207   {
208     m_i64FilePos += nBytesRead;
209 #if defined(HAVE_POSIX_FADVISE)
210     // Drop the cache between where we last seeked and 16 MB behind where
211     // we are now, to make sure the file doesn't displace everything else.
212     // However, we never throw out the first 16 MB of the file, as we might
213     // want the header etc., and we never ask the OS to drop in chunks of
214     // less than 1 MB.
215     int64_t start_drop = std::max<int64_t>(m_i64LastDropPos, 16 << 20);
216     int64_t end_drop = std::max<int64_t>(m_i64FilePos - (16 << 20), 0);
217     if (end_drop - start_drop >= (1 << 20))
218     {
219       posix_fadvise((*m_hFile).fd, start_drop, end_drop - start_drop, POSIX_FADV_DONTNEED);
220       m_i64LastDropPos = end_drop;
221     }
222 #endif
223     return nBytesRead;
224   }
225   return 0;
226 }
227
228 //*********************************************************************************************
229 int CHDFile::Write(const void *lpBuf, int64_t uiBufSize)
230 {
231   if (!m_hFile.isValid())
232     return 0;
233
234   DWORD nBytesWriten;
235   if ( WriteFile((HANDLE)m_hFile, (void*) lpBuf, (DWORD)uiBufSize, &nBytesWriten, NULL) )
236     return nBytesWriten;
237
238   return 0;
239 }
240
241 //*********************************************************************************************
242 void CHDFile::Close()
243 {
244   m_hFile.reset();
245 }
246
247 //*********************************************************************************************
248 int64_t CHDFile::Seek(int64_t iFilePosition, int iWhence)
249 {
250   LARGE_INTEGER lPos, lNewPos;
251   lPos.QuadPart = iFilePosition;
252   int bSuccess;
253
254   switch (iWhence)
255   {
256   case SEEK_SET:
257     bSuccess = SetFilePointerEx((HANDLE)m_hFile, lPos, &lNewPos, FILE_BEGIN);
258     break;
259
260   case SEEK_CUR:
261     bSuccess = SetFilePointerEx((HANDLE)m_hFile, lPos, &lNewPos, FILE_CURRENT);
262     break;
263
264   case SEEK_END:
265     bSuccess = SetFilePointerEx((HANDLE)m_hFile, lPos, &lNewPos, FILE_END);
266     break;
267
268   default:
269     return -1;
270   }
271   if (bSuccess)
272   {
273     if (m_i64FilePos != lNewPos.QuadPart)
274     {
275       // If we seek, disable the cache drop heuristic until we
276       // have played sequentially for a while again from here.
277       m_i64LastDropPos = lNewPos.QuadPart;
278     }
279     m_i64FilePos = lNewPos.QuadPart;
280     return m_i64FilePos;
281   }
282   else
283     return -1;
284 }
285
286 //*********************************************************************************************
287 int64_t CHDFile::GetLength()
288 {
289   if(m_i64FileLen <= m_i64FilePos || m_i64FileLen == 0)
290   {
291     LARGE_INTEGER i64Size;
292     if(GetFileSizeEx((HANDLE)m_hFile, &i64Size))
293       m_i64FileLen = i64Size.QuadPart;
294     else
295       CLog::Log(LOGERROR, "CHDFile::GetLength - GetFileSizeEx failed with error %d", GetLastError());
296   }
297   return m_i64FileLen;
298 }
299
300 //*********************************************************************************************
301 int64_t CHDFile::GetPosition()
302 {
303   return m_i64FilePos;
304 }
305
306 bool CHDFile::Delete(const CURL& url)
307 {
308   std::string strFile(GetLocal(url));
309
310 #ifdef TARGET_WINDOWS
311   return ::DeleteFileW(CWIN32Util::ConvertPathToWin32Form(strFile).c_str()) ? true : false;
312 #else
313   return ::DeleteFile(strFile.c_str()) ? true : false;
314 #endif
315 }
316
317 bool CHDFile::Rename(const CURL& url, const CURL& urlnew)
318 {
319   std::string strFile(GetLocal(url));
320   std::string strNewFile(GetLocal(urlnew));
321
322 #ifdef TARGET_WINDOWS
323   return ::MoveFileW(CWIN32Util::ConvertPathToWin32Form(strFile).c_str(), CWIN32Util::ConvertPathToWin32Form(strNewFile).c_str()) ? true : false;
324 #else
325   return ::MoveFile(strFile.c_str(), strNewFile.c_str()) ? true : false;
326 #endif
327 }
328
329 void CHDFile::Flush()
330 {
331   ::FlushFileBuffers(m_hFile);
332 }
333
334 int CHDFile::IoControl(EIoControl request, void* param)
335 {
336 #ifdef TARGET_POSIX
337   if(request == IOCTRL_NATIVE && param)
338   {
339     SNativeIoControl* s = (SNativeIoControl*)param;
340     return ioctl((*m_hFile).fd, s->request, s->param);
341   }
342 #endif
343   return -1;
344 }
345
346 int CHDFile::Truncate(int64_t size)
347 {
348 #ifdef TARGET_WINDOWS
349   // Duplicate the handle, as retrieving and closing a matching crt handle closes the crt handle AND the original Windows handle.
350   HANDLE hFileDup;
351   if (0 == DuplicateHandle(GetCurrentProcess(), (HANDLE)m_hFile, GetCurrentProcess(), &hFileDup, 0, FALSE, DUPLICATE_SAME_ACCESS))
352   {
353     CLog::Log(LOGERROR, __FUNCTION__" - DuplicateHandle()");
354     return -1;
355   }
356
357   int fd;
358   fd = _open_osfhandle((intptr_t)((HANDLE)hFileDup), 0);
359   if (fd == -1)
360   {
361     CLog::Log(LOGERROR, "Stat: fd == -1");
362     return -1;
363   }
364   int result = _chsize_s(fd, (long) size);
365   _close(fd);
366   return result;
367 #else
368   return ftruncate((*m_hFile).fd, (off_t) size);
369 #endif
370   return -1;
371 }