diff options
Diffstat (limited to 'src/filesystem/APKFile.cpp')
-rw-r--r-- | src/filesystem/APKFile.cpp | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/src/filesystem/APKFile.cpp b/src/filesystem/APKFile.cpp new file mode 100644 index 0000000000..5eb70e3094 --- /dev/null +++ b/src/filesystem/APKFile.cpp @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2012-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +// Android apk file i/o. Depends on libzip +// Basically the same format as zip. +// We might want to refactor CFileZip someday... +////////////////////////////////////////////////////////////////////// +#include "system.h" + +#include "APKFile.h" +#include "utils/log.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" + +#include <zip.h> + +using namespace XFILE; + +CAPKFile::CAPKFile() +{ + m_file_pos = 0; + m_file_size = 0; + m_zip_index =-1; + m_zip_file = NULL; + m_zip_archive = NULL; +} + +CAPKFile::~CAPKFile() +{ +} + +bool CAPKFile::Open(const CURL& url) +{ + Close(); + + m_url = url; + std::string path = url.GetFileName(); + std::string host = url.GetHostName(); + + int zip_flags = 0, zip_error = 0; + m_zip_archive = zip_open(host.c_str(), zip_flags, &zip_error); + if (!m_zip_archive || zip_error) + { + CLog::Log(LOGERROR, "CAPKFile::Open: Unable to open archive : '%s'", + host.c_str()); + return false; + } + + m_zip_index = zip_name_locate(m_zip_archive, path.c_str(), zip_flags); + if (m_zip_index == -1) + { + // might not be an error if caller is just testing for presence/absence + CLog::Log(LOGDEBUG, "CAPKFile::Open: Unable to locate file : '%s'", + path.c_str()); + zip_close(m_zip_archive); + m_zip_archive = NULL; + return false; + } + + // cache the file size + struct zip_stat sb; + zip_stat_init(&sb); + int rtn = zip_stat_index(m_zip_archive, m_zip_index, zip_flags, &sb); + if (rtn == -1) + { + CLog::Log(LOGERROR, "CAPKFile::Open: Unable to stat file : '%s'", + path.c_str()); + zip_close(m_zip_archive); + m_zip_archive = NULL; + return false; + } + m_file_pos = 0; + m_file_size = sb.size; + + // finally open the file + m_zip_file = zip_fopen_index(m_zip_archive, m_zip_index, zip_flags); + if (!m_zip_file) + { + CLog::Log(LOGERROR, "CAPKFile::Open: Unable to open file : '%s'", + path.c_str()); + zip_close(m_zip_archive); + m_zip_archive = NULL; + return false; + } + + // We've successfully opened the file! + return true; +} + +bool CAPKFile::Exists(const CURL& url) +{ + struct __stat64 buffer; + return (Stat(url, &buffer) == 0); +} + +void CAPKFile::Close() +{ + if (m_zip_archive) + { + if (m_zip_file) + zip_fclose(m_zip_file); + m_zip_file = NULL; + } + zip_close(m_zip_archive); + m_zip_archive = NULL; + m_file_pos = 0; + m_file_size = 0; + m_zip_index =-1; +} + +int64_t CAPKFile::Seek(int64_t iFilePosition, int iWhence) +{ + // libzip has no seek so we have to fake it with reads + off64_t file_pos = -1; + if (m_zip_archive && m_zip_file) + { + switch(iWhence) + { + default: + case SEEK_CUR: + // set file offset to current plus offset + if (m_file_pos + iFilePosition > m_file_size) + return -1; + file_pos = m_file_pos + iFilePosition; + break; + + case SEEK_SET: + // set file offset to offset + if (iFilePosition > m_file_size) + return -1; + file_pos = iFilePosition; + break; + + case SEEK_END: + // set file offset to EOF minus offset + if (iFilePosition > m_file_size) + return -1; + file_pos = m_file_size - iFilePosition; + break; + } + // if offset is past current file position + // then we must close, open then seek from zero. + if (file_pos < m_file_pos) + { + zip_fclose(m_zip_file); + int zip_flags = 0; + m_zip_file = zip_fopen_index(m_zip_archive, m_zip_index, zip_flags); + } + char buffer[1024]; + int read_bytes = 1024 * (file_pos / 1024); + for (int i = 0; i < read_bytes; i += 1024) + zip_fread(m_zip_file, buffer, 1024); + if (file_pos - read_bytes > 0) + zip_fread(m_zip_file, buffer, file_pos - read_bytes); + m_file_pos = file_pos; + } + + return m_file_pos; +} + +ssize_t CAPKFile::Read(void *lpBuf, size_t uiBufSize) +{ + if (uiBufSize > SSIZE_MAX) + uiBufSize = SSIZE_MAX; + + ssize_t bytes_read = uiBufSize; + if (m_zip_archive && m_zip_file) + { + // check for a read pas EOF and clamp it to EOF + if ((m_file_pos + bytes_read) > m_file_size) + bytes_read = m_file_size - m_file_pos; + + bytes_read = zip_fread(m_zip_file, lpBuf, bytes_read); + if (bytes_read != -1) + m_file_pos += bytes_read; + else + bytes_read = 0; + } + + return bytes_read; +} + +int CAPKFile::Stat(struct __stat64* buffer) +{ + return Stat(m_url, buffer); +} + +int CAPKFile::Stat(const CURL& url, struct __stat64* buffer) +{ + memset(buffer, 0, sizeof(struct __stat64)); + + // do not use interal member vars here, + // we might be called without opening + std::string path = url.GetFileName(); + std::string host = url.GetHostName(); + + struct zip *zip_archive; + int zip_flags = 0, zip_error = 0; + zip_archive = zip_open(host.c_str(), zip_flags, &zip_error); + if (!zip_archive || zip_error) + { + CLog::Log(LOGERROR, "CAPKFile::Stat: Unable to open archive : '%s'", + host.c_str()); + errno = ENOENT; + return -1; + } + + // check if file exists + int zip_index = zip_name_locate(zip_archive, url.GetFileName().c_str(), zip_flags); + if (zip_index != -1) + { + struct zip_stat sb; + zip_stat_init(&sb); + int rtn = zip_stat_index(zip_archive, zip_index, zip_flags, &sb); + if (rtn != -1) + { + buffer->st_gid = 0; + buffer->st_size = sb.size; + buffer->st_mode = _S_IFREG; + buffer->st_atime = sb.mtime; + buffer->st_ctime = sb.mtime; + buffer->st_mtime = sb.mtime; + } + } + + // check if directory exists + if (buffer->st_mode != _S_IFREG) + { + // zip directories have a '/' at end. + if (!URIUtils::HasSlashAtEnd(path)) + URIUtils::AddSlashAtEnd(path); + + int numFiles = zip_get_num_files(zip_archive); + for (int i = 0; i < numFiles; i++) + { + std::string name = zip_get_name(zip_archive, i, zip_flags); + if (!name.empty() && StringUtils::StartsWith(name, path)) + { + buffer->st_gid = 0; + buffer->st_mode = _S_IFDIR; + break; + } + } + } + zip_close(zip_archive); + + if (buffer->st_mode != 0) + { + errno = 0; + return 0; + } + else + { + errno = ENOENT; + return -1; + } +} + +int64_t CAPKFile::GetPosition() +{ + return m_file_pos; +} + +int64_t CAPKFile::GetLength() +{ + return m_file_size; +} + +int CAPKFile::GetChunkSize() +{ + return 1; +} |