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