1 /*
2  * Copyright (C) 2006 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 //
18 // Class providing access to a read-only asset.  Asset objects are NOT
19 // thread-safe, and should not be shared across threads.
20 //
21 #ifndef __LIBS_ASSET_H
22 #define __LIBS_ASSET_H
23 
24 #include <stdio.h>
25 #include <sys/types.h>
26 #include <memory>
27 #include <optional>
28 
29 #include <android-base/unique_fd.h>
30 #include <util/map_ptr.h>
31 
32 #include <utils/Compat.h>
33 #include <utils/Errors.h>
34 #include <utils/String8.h>
35 
36 namespace android {
37 
38 /*
39  * Instances of this class provide read-only operations on a byte stream.
40  *
41  * Access may be optimized for streaming, random, or whole buffer modes.  All
42  * operations are supported regardless of how the file was opened, but some
43  * things will be less efficient.  [pass that in??]
44  *
45  * "Asset" is the base class for all types of assets.  The classes below
46  * provide most of the implementation.  The AssetManager uses one of the
47  * static "create" functions defined here to create a new instance.
48  */
49 class Asset {
50 public:
51     virtual ~Asset(void) = default;
52     Asset(const Asset& src) = delete;
53     Asset& operator=(const Asset& src) = delete;
54 
55     static int32_t getGlobalCount();
56     static String8 getAssetAllocations();
57 
58     /* used when opening an asset */
59     typedef enum AccessMode {
60         ACCESS_UNKNOWN = 0,
61 
62         /* read chunks, and seek forward and backward */
63         ACCESS_RANDOM,
64 
65         /* read sequentially, with an occasional forward seek */
66         ACCESS_STREAMING,
67 
68         /* caller plans to ask for a read-only buffer with all data */
69         ACCESS_BUFFER,
70     } AccessMode;
71 
72     /*
73      * Read data from the current offset.  Returns the actual number of
74      * bytes read, 0 on EOF, or -1 on error.
75      */
76     virtual ssize_t read(void* buf, size_t count) = 0;
77 
78     /*
79      * Seek to the specified offset.  "whence" uses the same values as
80      * lseek/fseek.  Returns the new position on success, or (off64_t) -1
81      * on failure.
82      */
83     virtual off64_t seek(off64_t offset, int whence) = 0;
84 
85     /*
86      * Close the asset, freeing all associated resources.
87      */
88     virtual void close(void) = 0;
89 
90     /*
91      * Get a pointer to a buffer with the entire contents of the file.
92      * If `aligned` is true, the buffer data will be aligned to a 4-byte boundary.
93      *
94      * If the buffer contents reside on IncFs, the entire buffer will be scanned to ensure the
95      * presence of the data before returning a raw pointer to the buffer.
96      */
97     virtual const void* getBuffer(bool aligned) = 0;
98 
99     /*
100      * Get a incfs::map_ptr<void> to a buffer with the entire contents of the file.
101      * If `aligned` is true, the buffer data will be aligned to a 4-byte boundary.
102      *
103      * Use this function if the asset can potentially reside on IncFs to avoid the scanning of the
104      * buffer contents done in Asset::getBuffer.
105      */
106     virtual incfs::map_ptr<void> getIncFsBuffer(bool aligned) = 0;
107 
108     /*
109      * Get the total amount of data that can be read.
110      */
111     virtual off64_t getLength(void) const = 0;
112 
113     /*
114      * Get the total amount of data that can be read from the current position.
115      */
116     virtual off64_t getRemainingLength(void) const = 0;
117 
118     /*
119      * Open a new file descriptor that can be used to read this asset.
120      * Returns -1 if you can not use the file descriptor (for example if the
121      * asset is compressed).
122      */
123     virtual int openFileDescriptor(off64_t* outStart, off64_t* outLength) const = 0;
124 
125     /*
126      * Return whether this asset's buffer is allocated in RAM (not mmapped).
127      * Note: not virtual so it is safe to call even when being destroyed.
128      */
isAllocated(void)129     virtual bool isAllocated(void) const { return false; }
130 
131     /*
132      * Get a string identifying the asset's source.  This might be a full
133      * path, it might be a colon-separated list of identifiers.
134      *
135      * This is NOT intended to be used for anything except debug output.
136      * DO NOT try to parse this or use it to open a file.
137      */
getAssetSource(void)138     const char* getAssetSource(void) const { return mAssetSource.c_str(); }
139 
140     /*
141      * Create the asset from a file descriptor.
142      */
143     static Asset* createFromFd(const int fd, const char* fileName, AccessMode mode);
144 
145 protected:
146     /*
147      * Adds this Asset to the global Asset list for debugging and
148      * accounting.
149      * Concrete subclasses must call this in their constructor.
150      */
151     static void registerAsset(Asset* asset);
152 
153     /*
154      * Removes this Asset from the global Asset list.
155      * Concrete subclasses must call this in their destructor.
156      */
157     static void unregisterAsset(Asset* asset);
158 
159     Asset(void);        // constructor; only invoked indirectly
160 
161     /* handle common seek() housekeeping */
162     off64_t handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t maxPosn);
163 
164     /* set the asset source string */
setAssetSource(const String8 & path)165     void setAssetSource(const String8& path) { mAssetSource = path; }
166 
getAccessMode(void)167     AccessMode getAccessMode(void) const { return mAccessMode; }
168 
169 private:
170     /* AssetManager needs access to our "create" functions */
171     friend class AssetManager;
172     friend struct ZipAssetsProvider;
173     friend struct AssetsProvider;
174 
175     /*
176      * Create the asset from a named file on disk.
177      */
178     static Asset* createFromFile(const char* fileName, AccessMode mode);
179 
180     /*
181      * Create the asset from a named, compressed file on disk (e.g. ".gz").
182      */
183     static Asset* createFromCompressedFile(const char* fileName, AccessMode mode);
184 
185 #if 0
186     /*
187      * Create the asset from a segment of an open file.  This will fail
188      * if "offset" and "length" don't fit within the bounds of the file.
189      *
190      * The asset takes ownership of the file descriptor.
191      */
192     static Asset* createFromFileSegment(int fd, off64_t offset, size_t length,
193         AccessMode mode);
194 
195     /*
196      * Create from compressed data.  "fd" should be seeked to the start of
197      * the compressed data.  This could be inside a gzip file or part of a
198      * Zip archive.
199      *
200      * The asset takes ownership of the file descriptor.
201      *
202      * This may not verify the validity of the compressed data until first
203      * use.
204      */
205     static Asset* createFromCompressedData(int fd, off64_t offset,
206         int compressionMethod, size_t compressedLength,
207         size_t uncompressedLength, AccessMode mode);
208 #endif
209 
210     /*
211      * Create the asset from a memory-mapped file segment.
212      *
213      * The asset takes ownership of the incfs::IncFsFileMap and the file descriptor "fd". The
214      * file descriptor is used to request new file descriptors using "openFileDescriptor".
215      */
216     static std::unique_ptr<Asset> createFromUncompressedMap(incfs::IncFsFileMap&& dataMap,
217                                                             AccessMode mode,
218                                                             base::unique_fd fd = {});
219 
220     /*
221      * Create the asset from a memory-mapped file segment with compressed
222      * data.
223      *
224      * The asset takes ownership of the incfs::IncFsFileMap.
225      */
226     static std::unique_ptr<Asset> createFromCompressedMap(incfs::IncFsFileMap&& dataMap,
227                                                           size_t uncompressedLen, AccessMode mode);
228 
229     /*
230      * Create from a reference-counted chunk of shared memory.
231      */
232     // TODO
233 
234     AccessMode  mAccessMode;        // how the asset was opened
235     String8    mAssetSource;       // debug string
236 
237     Asset*		mNext;				// linked list.
238     Asset*		mPrev;
239 };
240 
241 
242 /*
243  * ===========================================================================
244  *
245  * Innards follow.  Do not use these classes directly.
246  */
247 
248 /*
249  * An asset based on an uncompressed file on disk.  It may encompass the
250  * entire file or just a piece of it.  Access is through fread/fseek.
251  */
252 class _FileAsset : public Asset {
253 public:
254     _FileAsset(void);
255     ~_FileAsset(void) override;
256 
257     /*
258      * Use a piece of an already-open file.
259      *
260      * On success, the object takes ownership of "fd".
261      */
262     status_t openChunk(const char* fileName, int fd, off64_t offset, size_t length);
263 
264     /*
265      * Use a memory-mapped region.
266      *
267      * On success, the object takes ownership of "dataMap" and "fd".
268      */
269     status_t openChunk(incfs::IncFsFileMap&& dataMap, base::unique_fd fd);
270 
271     /*
272      * Standard Asset interfaces.
273      */
274     ssize_t read(void* buf, size_t count) override;
275     off64_t seek(off64_t offset, int whence) override;
276     void close(void) override;
277     const void* getBuffer(bool aligned) override;
278     incfs::map_ptr<void> getIncFsBuffer(bool aligned) override;
getLength(void)279     off64_t getLength(void) const override { return mLength; }
getRemainingLength(void)280     off64_t getRemainingLength(void) const override { return mLength-mOffset; }
281     int openFileDescriptor(off64_t* outStart, off64_t* outLength) const override;
isAllocated(void)282     bool isAllocated(void) const override { return mBuf != NULL; }
283 
284 private:
285     incfs::map_ptr<void> ensureAlignment(const incfs::IncFsFileMap& map);
286 
287     off64_t         mStart;         // absolute file offset of start of chunk
288     off64_t         mLength;        // length of the chunk
289     off64_t         mOffset;        // current local offset, 0 == mStart
290     FILE*           mFp;            // for read/seek
291     char*           mFileName;      // for opening
292     base::unique_fd mFd;            // for opening file descriptors
293 
294     /*
295      * To support getBuffer() we either need to read the entire thing into
296      * a buffer or memory-map it.  For small files it's probably best to
297      * just read them in.
298      */
299     enum { kReadVsMapThreshold = 4096 };
300 
301     unsigned char*                      mBuf;     // for read
302     std::optional<incfs::IncFsFileMap>  mMap;     // for memory map
303 };
304 
305 
306 /*
307  * An asset based on compressed data in a file.
308  */
309 class _CompressedAsset : public Asset {
310 public:
311     _CompressedAsset(void);
312     virtual ~_CompressedAsset(void);
313 
314     /*
315      * Use a piece of an already-open file.
316      *
317      * On success, the object takes ownership of "fd".
318      */
319     status_t openChunk(int fd, off64_t offset, int compressionMethod,
320         size_t uncompressedLen, size_t compressedLen);
321 
322     /*
323      * Use a memory-mapped region.
324      *
325      * On success, the object takes ownership of "fd".
326      */
327     status_t openChunk(incfs::IncFsFileMap&& dataMap, size_t uncompressedLen);
328 
329     /*
330      * Standard Asset interfaces.
331      */
332     virtual ssize_t read(void* buf, size_t count);
333     virtual off64_t seek(off64_t offset, int whence);
334     virtual void close(void);
335     virtual const void* getBuffer(bool aligned);
336     virtual incfs::map_ptr<void> getIncFsBuffer(bool aligned);
getLength(void)337     virtual off64_t getLength(void) const { return mUncompressedLen; }
getRemainingLength(void)338     virtual off64_t getRemainingLength(void) const { return mUncompressedLen-mOffset; }
openFileDescriptor(off64_t *,off64_t *)339     virtual int openFileDescriptor(off64_t* /* outStart */, off64_t* /* outLength */) const { return -1; }
isAllocated(void)340     virtual bool isAllocated(void) const { return mBuf != NULL; }
341 
342 private:
343     off64_t mStart;           // offset to start of compressed data
344     off64_t mCompressedLen;   // length of the compressed data
345     off64_t mUncompressedLen; // length of the uncompressed data
346     off64_t mOffset;          // current offset, 0 == start of uncomp data
347     int     mFd;              // for file input
348 
349     class StreamingZipInflater*         mZipInflater; // for streaming large compressed assets
350     unsigned char*                      mBuf;         // for getBuffer()
351     std::optional<incfs::IncFsFileMap>  mMap;         // for memory-mapped input
352 };
353 
354 // need: shared mmap version?
355 
356 }; // namespace android
357 
358 #endif // __LIBS_ASSET_H
359