1 /*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "file.h"
18
19 #include <errno.h>
20 #include <stddef.h>
21 #include <unistd.h>
22
23 #include <algorithm>
24 #include <cstdio>
25 #include <cstring>
26 #include <utility>
27
28 #include "cpp/fpdf_scopers.h"
29 #include "fpdf_dataavail.h"
30 #include "fpdf_save.h"
31 #include "fpdfview.h"
32 #include "linux_fileops.h"
33 #include "logging.h"
34
35 #define LOG_TAG "file"
36
37 namespace pdfClient {
38
GetFileSize(int fd)39 size_t GetFileSize(int fd) {
40 off_t end = lseek(fd, 0, SEEK_END);
41 return std::max(end, 0L);
42 }
43
FileReader(LinuxFileOps::FDCloser fd)44 FileReader::FileReader(LinuxFileOps::FDCloser fd) : fd_(std::move(fd)) {
45 complete_size_ = GetFileSize(fd_.get());
46 InitImplementation();
47 }
48
FileReader(LinuxFileOps::FDCloser fd,size_t completeSize)49 FileReader::FileReader(LinuxFileOps::FDCloser fd, size_t completeSize)
50 : fd_(std::move(fd)), complete_size_(completeSize) {
51 InitImplementation();
52 }
53
FileReader(FileReader && fr)54 FileReader::FileReader(FileReader&& fr)
55 : fpdf_avail_(std::move(fr.fpdf_avail_)),
56 fd_(std::move(fr.fd_)),
57 complete_size_(fr.complete_size_) {}
58
~FileReader()59 FileReader::~FileReader() {}
60
ReleaseFd()61 int FileReader::ReleaseFd() {
62 return fd_.Release();
63 }
64
IsComplete() const65 bool FileReader::IsComplete() const {
66 return GetFileSize(fd_.get()) >= complete_size_;
67 }
68
CanReadBlock(size_t pos,size_t size) const69 bool FileReader::CanReadBlock(size_t pos, size_t size) const {
70 // Return false if pos + size overflows:
71 return pos + size >= pos && pos + size <= GetFileSize(fd_.get());
72 }
73
DoReadBlock(size_t pos,void * buffer,size_t size) const74 size_t FileReader::DoReadBlock(size_t pos, void* buffer, size_t size) const {
75 if (!CanReadBlock(pos, size)) {
76 return 0;
77 }
78 if (lseek(fd_.get(), pos, SEEK_SET) == -1) {
79 return 0;
80 }
81 return read(fd_.get(), buffer, size);
82 }
83
RequestBlock(size_t offset,size_t size)84 void FileReader::RequestBlock(size_t offset, size_t size) {
85 if (!CanReadBlock(offset, size)) {
86 LOGI("pdfClient requests segment: offset=%zu, size=%zu", offset, size);
87 }
88 }
89
InitImplementation()90 void FileReader::InitImplementation() {
91 // Implements FPDF_FILEACCESS:
92 FPDF_FILEACCESS::m_FileLen = complete_size_;
93 FPDF_FILEACCESS::m_GetBlock = &StaticGetBlockImpl;
94 FPDF_FILEACCESS::m_Param = this;
95
96 // Implements FX_FILEAVAIL:
97 FX_FILEAVAIL::IsDataAvail = &StaticIsDataAvailImpl;
98 FX_FILEAVAIL::version = 1;
99
100 // And create an FPDF_AVAIL:
101 fpdf_avail_.reset(FPDFAvail_Create(this, this));
102
103 // Implements FX_DOWNLOADHINTS:
104 FX_DOWNLOADHINTS::version = 1;
105 FX_DOWNLOADHINTS::AddSegment = &StaticAddSegmentImpl;
106 }
107
StaticIsDataAvailImpl(FX_FILEAVAIL * pThis,size_t pos,size_t size)108 int FileReader::StaticIsDataAvailImpl(FX_FILEAVAIL* pThis, size_t pos, size_t size) {
109 FileReader* fileReader = static_cast<FileReader*>(pThis);
110 return fileReader->CanReadBlock(pos, size);
111 }
112
StaticGetBlockImpl(void * param,unsigned long pos,unsigned char * buffer,unsigned long size)113 int FileReader::StaticGetBlockImpl(void* param, unsigned long pos, // NOLINT
114 unsigned char* buffer,
115 unsigned long size) { // NOLINT
116 FileReader* fileReader = static_cast<FileReader*>(param);
117 return fileReader->DoReadBlock(pos, buffer, size);
118 }
119
StaticAddSegmentImpl(FX_DOWNLOADHINTS * pThis,size_t pos,size_t size)120 void FileReader::StaticAddSegmentImpl(FX_DOWNLOADHINTS* pThis, size_t pos, size_t size) {
121 FileReader* fileReader = static_cast<FileReader*>(pThis);
122 fileReader->RequestBlock(pos, size);
123 }
124
FileWriter(LinuxFileOps::FDCloser fd)125 FileWriter::FileWriter(LinuxFileOps::FDCloser fd) : fd_(std::move(fd)) {
126 // Implements FPDF_FILEWRITE:
127 version = 1;
128 WriteBlock = &StaticWriteBlockImpl;
129 }
130
~FileWriter()131 FileWriter::~FileWriter() {
132 if (fd_.get() >= 0) {
133 fsync(fd_.get());
134 }
135 }
136
DoWriteBlock(const void * data,size_t size)137 size_t FileWriter::DoWriteBlock(const void* data, size_t size) {
138 size_t written = write(fd_.get(), data, size);
139 if (written != size) {
140 LOGE("Error performing write to fd: %s", strerror(errno));
141 }
142 return written;
143 }
144
StaticWriteBlockImpl(FPDF_FILEWRITE * pThis,const void * data,unsigned long size)145 int FileWriter::StaticWriteBlockImpl(FPDF_FILEWRITE* pThis, const void* data,
146 unsigned long size) { // NOLINT
147 FileWriter* file_write = static_cast<FileWriter*>(pThis);
148 return file_write->DoWriteBlock(data, size);
149 }
150
LogAddSegment(FX_DOWNLOADHINTS * pThis,size_t offset,size_t size)151 static void LogAddSegment(FX_DOWNLOADHINTS* pThis, size_t offset, size_t size) {
152 LOGI("pdfClient requests segment: offset=%zu, size=%zu", offset, size);
153 }
154
LogOnlyDownloadHints()155 FX_DOWNLOADHINTS* LogOnlyDownloadHints() {
156 static FX_DOWNLOADHINTS downloadHints;
157 downloadHints.version = 1;
158 downloadHints.AddSegment = &LogAddSegment;
159 return &downloadHints;
160 }
161
162 } // namespace pdfClient