Commit Graph

117 Commits

Author SHA1 Message Date
Treehugger Robot 5664489a31 Merge "Ziparchive: Enable -Wconversion" 2019-04-19 03:33:19 +00:00
Andreas Gampe 964b95cf61 Ziparchive: Enable -Wconversion
Enable -Wconversion (but not -Wsign-conversion). Fix up code. Handle
some actual error cases:

* too many files
* files too large

Bug: 130039052
Test: atest ziparchive-tests
Change-Id: I632af317b9e65dbc728851efefd0d11a2b5c29b9
2019-04-05 13:50:48 -07:00
Nick Kralevich c0bf36647b zip_archive.cc: Use static cast instead of masking
Although ubsan's implicit-unsigned-integer-truncation sanitizer may be
happy, this code still performs an implicit conversion from a wider
width data structure to a narrower width data structure. Rather than
masking the bits, make the conversion explicit. This keeps ubsan happy
as well as addressing a -Wconversion warning.

This change addresses comments from the post-submit review
of a4e5433660.

Test: compiles and boots.
Bug: 122975762
Change-Id: I1fa6d6f8a6fcfb93ba9916b7d2b3564ca1d8caf3
2019-04-05 09:32:14 -07:00
Nick Kralevich a4e5433660 zip_archive.cc: fix ubsan false positive
std::hash returns a 64 bit value, which is truncated to a 32 bit value
in ComputeHash. ubsan's implicit-unsigned-integer-truncation doesn't
like this implicit truncation and crashes the program. Explicitly strip
off the top order bits after computing the hash.

Remove the windows specific version of the hash computation. The windows
compile now uses clang, so this code is obsolete. This also avoids us
having to add __attribute__((no_sanitize("integer"))) to the windows
code.

This is needed to support Android booting with ubsan's
implicit-unsigned-integer-truncation option enabled.

Test: compiles and boots
Bug: 122975762
Change-Id: I2f05fbf5ffee8e90a66a6fda32e80de9cca246c0
2019-04-04 14:53:11 -07:00
Nick Kralevich 104771e55e zip_archive.cc: delete android_errorWriteLog
The process of determining whether or not to emit a safetynet error
entry while processing a malformed file relies on addition overflow.
Since this is only logging, and logging which isn't used, delete the
code instead of trying to fix the logic which is causing the integer
overflow.

This change is necessary to enable integer sanitization on this code.

Somewhat related to Bug: 122975762
Test: atest ziparchive-tests

Change-Id: I6b41ccf7881348cb4e5236324eaa44a05662a725
2019-03-21 09:58:56 -07:00
Nick Kralevich 3bdf744a55 zip_archive.cc: add O_CLOEXEC
Prevent file descriptors from leaking across an exec() boundary.

Bug: 120983106
Test: compiles and boots
Change-Id: I392b0767674b557b1e4404a2ba63bc48e3e37b24
2018-12-18 12:48:06 -08:00
Elliott Hughes 9c8bd669e8 Yet another Mac fix.
system/core/libziparchive/zip_archive.cc:847:36: error: use of undeclared identifier 'lseek64'; did you mean 'lseek'?
  const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);

Bug: N/A
Test: builds
Change-Id: If762011722d53376bb6dab35c6ee8031762e5a95
2018-10-26 16:14:21 -07:00
Elliott Hughes e8f4b14301 Add a simple MappedFile to libbase and switch fastboot and libziparchive over.
This allows us to remove libziparchive's dependency on libutils.

Bug: http://b/79112958
Test: ran libbase and libziparchive tests, ran fastboot manually
Change-Id: I95c651976dad222863e5b8c37d4514b778f5dce7
2018-10-23 13:20:49 -07:00
Ryan Prichard 3673f99dd2 Change ZipArchiveHandle from void* to ZipArchive*
A typedef to void* allows an implicit conversion from ZipArchiveHandle*
(or any other pointer type) to ZipArchiveHandle.

See I95d79809b6e118fb3c39c7b98b8055c8e324db1a in platform/bionic.

Bug: none
Test: m checkbuild
Change-Id: I3dd426cb64c46ef81e1dd81b4a2e4f40ac2701df
2018-10-11 14:18:08 -07:00
Chih-Hung Hsieh 747eb149d0 Add noexcept to move constructors and assignment operators.
Bug: 116614593
Test: build with WITH_TIDY=1
Change-Id: I5a7461386946ca623ab509609092aa0ac8418b80
2018-10-05 16:43:47 +00:00
Josh Gao 3da42a6c05 Merge "libziparchive: encode type in fdsan owner tag." 2018-09-20 19:22:03 +00:00
Zimuzo 5a503efaf0 Reduce libziparchive internal hashtable memory size
Debugging memory allocations on the nexus launcher unveiled significant memory allocations for the hashatable used in libziparchive, ~1MB.
This is partly because of the ZipString struct storing an entry in the table. The struct stored a pointer to a string (on 64 bit, 8 bytes) and the length to read from that pointer, 2 bytes. Because of alignment, the structure consumed 16 bytes, wasting 6 bytes.
Now, we store entries in the hashtable as a ZipStringOffset. This new structure stores a 4 byte offset from a fixed location in the memory mapped file instead of the entire address, consuming 8 bytes with alignment.

Bug: 79416399
Test: Builds successfully and manual testing by opening launcher on Pixel 2 shows precisely 50% decrease in memory allocated for the hashtable. From 909312 bytes to 454656.
Change-Id: I28b43699233fbee7f63fccae2d4fe96fcc07e5c4
2018-09-19 19:20:21 +01:00
Josh Gao abdfc248f3 libziparchive: encode type in fdsan owner tag.
Test: debuggerd `pidof system_server` | grep " fd "
Change-Id: I05577adb94cf58b549aa0af03b42653e345bcd11
2018-09-07 14:09:10 -07:00
Ryan Mitchell c77f9d380f Open zips using _wopen on windows.
Allows for opening zip files usng paths longer than 260 characters and
with unicode characters on Windows.

Bug: 113110184
Test: manual and libziparchive_tests
Change-Id: I9ce96ac2f1b1e448ae2a2f69c1d4cb3395ea79ee
2018-08-28 09:31:26 -07:00
Josh Gao 1b49634f83 libziparchive: use fdsan in ZipArchive.
Test: treehugger
Change-Id: I8586b8ad27c4f1eda1a5266867da8dbbf4870c5e
2018-07-18 18:11:46 -07:00
Adam Lesinski b354dcef98 Remove empty zip warning on host builds
Change-Id: Iaafe208930c316cc32f39ce2687be555b9eeb35f
2018-03-01 21:33:49 +00:00
Yurii Zubrytskyi 834326ce7a Get rid of unneeded allocations in Extract...() APIs
Both Extract...() functions don't need dynamic allocation
for the writers, as those are strictly scoped. This CL
changes heap allocation to stack allocation.

Test: zip_archive_test

Change-Id: Id727e4b9848235cd063cc67ecbe052d21ca21326
2017-12-20 18:36:03 +00:00
Elliott Hughes bfb96796a4 Merge "use std::hash instead of hashing byte by byte"
am: aaa0bbce8e

Change-Id: Iee321900f8ef386cd118d874c31c5b0d2741551a
2017-11-30 23:25:55 +00:00
Sebastian Pop 1f93d71022 use std::hash instead of hashing byte by byte
This patch uses libcxx's string_view hashing.  The performance is better than the
current implementation, and almost the same as for the hashing implementation amended
by the patch in https://android-review.googlesource.com/404742

The following experiments were conducted on a HiKey aarch64 A53.

No changes:
$ adb push out/target/product/hikey/data/benchmarktest64/ziparchive-benchmarks /data
$ adb shell /data/ziparchive-benchmarks --benchmark_repetitions=5
-----------------------------------------------------------------
Benchmark                          Time           CPU Iterations
-----------------------------------------------------------------
FindEntry_no_match          82279629 ns   81759676 ns          9
FindEntry_no_match          81648056 ns   81627431 ns          9
FindEntry_no_match          81384074 ns   81369057 ns          9
FindEntry_no_match          89618889 ns   82437755 ns          9
FindEntry_no_match          81811389 ns   81785261 ns          9
FindEntry_no_match_mean     83348407 ns   81795836 ns          9
FindEntry_no_match_stddev    3520421 ns     394962 ns          0
Iterate_all_files          137622000 ns  137589032 ns          5
Iterate_all_files          139666333 ns  138409469 ns          5
Iterate_all_files          150070000 ns  140883697 ns          5
Iterate_all_files          138600667 ns  138540646 ns          5
Iterate_all_files          137599833 ns  137567438 ns          5
Iterate_all_files_mean     140711767 ns  138598056 ns          5
Iterate_all_files_stddev     5299929 ns    1354928 ns          0

$ cd system/core/libziparchive
$ git fetch https://android.googlesource.com/platform/system/core refs/changes/42/404742/7 && git cherry-pick FETCH_HEAD
$ mma
$ cd -
$ adb push out/target/product/hikey/data/benchmarktest64/ziparchive-benchmarks /data
$ adb shell /data/ziparchive-benchmarks --benchmark_repetitions=5
-----------------------------------------------------------------
Benchmark                          Time           CPU Iterations
-----------------------------------------------------------------
FindEntry_no_match          53302756 ns   53291178 ns         13
FindEntry_no_match          54314487 ns   54272272 ns         13
FindEntry_no_match          53866923 ns   53851178 ns         13
FindEntry_no_match          53324423 ns   53317296 ns         13
FindEntry_no_match          53289231 ns   53289159 ns         13
FindEntry_no_match_mean     53619564 ns   53604216 ns         13
FindEntry_no_match_stddev     458449 ns     443527 ns          0
Iterate_all_files          128211111 ns  112254783 ns          6
Iterate_all_files          110695000 ns  110677726 ns          6
Iterate_all_files          109351250 ns  109350998 ns          6
Iterate_all_files          109367500 ns  109367796 ns          6
Iterate_all_files          110872222 ns  110591407 ns          6
Iterate_all_files_mean     113699417 ns  110448542 ns          6
Iterate_all_files_stddev     8143723 ns    1194577 ns          0

$ cd system/core/libziparchive
$ git checkout HEAD~
$ git am 0001-use-std-hash.patch
$ mma
$ cd -
$ adb push out/target/product/hikey/data/benchmarktest64/ziparchive-benchmarks /data
$ adb shell /data/ziparchive-benchmarks --benchmark_repetitions=5
-----------------------------------------------------------------
Benchmark                          Time           CPU Iterations
-----------------------------------------------------------------
FindEntry_no_match          55339872 ns   55195112 ns         13
FindEntry_no_match          56232628 ns   56069924 ns         13
FindEntry_no_match          55694103 ns   55255946 ns         13
FindEntry_no_match          55275064 ns   55120136 ns         13
FindEntry_no_match          54971987 ns   54944411 ns         13
FindEntry_no_match_mean     55502731 ns   55317106 ns         13
FindEntry_no_match_stddev     482032 ns     436766 ns          0
Iterate_all_files          114618611 ns  114487804 ns          6
Iterate_all_files          112552917 ns  112229801 ns          6
Iterate_all_files          111288750 ns  111255044 ns          6
Iterate_all_files          111291528 ns  111259045 ns          6
Iterate_all_files          114347222 ns  113677119 ns          6
Iterate_all_files_mean     112819806 ns  112581763 ns          6
Iterate_all_files_stddev     1606214 ns    1454858 ns          0

Change-Id: I1e3413d331bcb460ca38bc2c87e23f89b456cd2f
2017-11-29 09:44:28 -06:00
Xin Li 23e27db576 Merge commit 'a63ccea6abc7ea02e2d98e41c80793ca97237bd3' from
oc-mr1-dev-plus-aosp into stage-aosp-master

Change-Id: Ia33311cd1fd26dfaea59a69317b306fb91203c40
Merged-In: I03d06b10807e8a313c9654c2e1db36bfb59e3f99
2017-11-14 13:19:45 -08:00
Narayan Kamath 2d1e23f5d6 zip_archive: Allow crc_out to be nullptr in Inflate.
Only compute the crc32 if required. In addition :
- Add unit tests for Inflate that cover this addition.
- Fix an inconsistency in return codes that was revealed
  by this new test.

Bug: 35246701
Test: zip_archive_tests
Test: make; zipalign.
Merged-In: I31d7554378f94fc8995f707471d57cb98311e2c2
Change-Id: I05111bfa665c610f93d1c1dee987a509bf87aa65
2017-10-30 18:26:41 +00:00
Treehugger Robot b88aa023d4 Merge "fastboot should fail if it runs out of space while unzipping." 2017-10-28 01:16:29 +00:00
Elliott Hughes 4089d34b93 fastboot should fail if it runs out of space while unzipping.
Previously fastboot would carry on regardless if decompression failed:

  fastboot: archive does not contain 'vbmeta.img'
  fastboot: extracting vendor.img (260 MB)...
  fastboot: W/ziparchive(56777): Zip: unable to allocate  272781472 bytes at offset 0 : No space left on device
  fastboot: failed to extract 'vendor.img': I/O error
  fastboot: archive does not contain 'vendor_other.img'
  fastboot: wiping userdata...

This is because all but "boot" and "system" are considered "optional",
and the implementation of "optional" was "ignore any failures". What it
_should_ have meant was "it's okay if these don't exist, but if they do,
failures matter".

Fix this logic, use die() more aggressively, and remove spurious "\n"s
from die() format strings.

Also fix spurious whitespace in the libziparchive format string. Before:

  Zip: unable to allocate  272781472 bytes at offset 0 : No space left on device

After:

  Zip: unable to allocate 272781472 bytes at offset 0: No space left on device

Bug: http://b/68383022
Test: `fastboot update` on marlin
Change-Id: I3cbf55f1a33ca125f293f873eafbcfb86c880ba8
2017-10-27 14:21:12 -07:00
Narayan Kamath 485b3640a4 zip_archive: Make Inflate a public API.
For use from zipalign and androidfw::ZipUtils.

Test: make
Bug: 35246701

Change-Id: I1bdeff3770d0833dfc0807d8a69ba9fea525772a
2017-10-26 18:43:49 +01:00
Narayan Kamath 8b8faed17d zip_archive: generalize deflate method.
Remove dependencies on zip_archive specific data structures. In follow
up changes, this method will be promoted to a public API and used from
androidfw/ZipUtils as well as tools/zipalign, thereby allowing us to
remove a near-exact copy of this code and to simplify depdencies.

Test: zip_archive_test
Bug: 35246701
Change-Id: If24a9965fbd6fff308c8758859026684fd2af3b9
2017-10-26 17:47:50 +01:00
Narayan Kamath 8310613c37 Merge "zip_archive: reject files that don't start with an LFH signature." into oc-mr1-dev
am: b669c3311b

Change-Id: Id46f20e402c105108309addb0349cb64437d2541
2017-08-16 14:02:30 +00:00
Narayan Kamath c1a56dcab7 zip_archive: reject files that don't start with an LFH signature.
Bug: 64211847
Test: zip_archive_test

Change-Id: I275e7c4da05ceeb20401b560c72294f29ef63642
2017-08-15 13:44:05 +01:00
Adam Lesinski de117e4a49 libziparchive: Use ReadAtOffset exclusively
The use of ReadAtOffset is meant to allow concurrent access
to the zip archive once it has been loaded. There were places
where this was the case, and some places that did a seek + read
combination, which could lead to data races.

NOTE: On Windows, we are not using pread as the implementation of
ReadAtOffset, therefore the guarantees on Windows are weaker.

On Linux, pread allows the file descriptor to be read at a specific
offset without changing the read pointer. This allows inherited fd's
and duped fds to be read concurrently.

On Windows, we use the ReadFile API, which allows for an atomic seek +
read operation, but modifies the read pointer. This means that any mix
use of ReadAtOffset and Read will have races. Just using ReadAtOffset is
safe.

For the Windows case, this is fine as the libziparchive code now only
uses ReadAtOffset.

Bug: 62184114
Bug: 62101783
Test: make ziparchive-tests (existing tests pass)
Change-Id: Ia7f9a30af2216682cdd9d578d26e84bc46773bb9
2017-07-25 18:12:12 +00:00
Jiyong Park cd997e6094 Ran clang-format on libziparchive sources and headers
Test: build aosp_arm
Change-Id: I469b82b68f2c457f480fb9cd9da2026672985ce3
2017-07-02 11:46:53 +09:00
Tianjie Xu 0fda1cf633 Fix out of bound read in libziparchive
We should check the boundary of central directory before checking its
signature. Swap the order of these two checks.

Bug: 36392138
Test: libziparchive doesn't read the signature after boundary check fails.
Merged-In: Ie89f709bb2d1ccb647116fb7ccb1e23c943e5ab8
Change-Id: Ie89f709bb2d1ccb647116fb7ccb1e23c943e5ab8
(cherry picked from commit 74464a1361)
2017-06-30 17:19:56 +09:00
Tianjie Xu 9e020e2d11 Check filename memory bound when parsing ziparchive
Add a check to ensure the filename boundary doesn't exceed the mapped
memory region. Also add the corresponding unit test.

Bug: 28802225
Test: New unit test passes.
Merged-In: Ibf543a7da3d7898952e9eb332c84cdfc67cf5aa4
Change-Id: Ibf543a7da3d7898952e9eb332c84cdfc67cf5aa4
(cherry picked from commit bcc4431f24)
2017-06-30 17:19:28 +09:00
Narayan Kamath d1e9e7bc06 Merge "zip_archive_test: compare error codes directly." 2017-06-20 10:50:27 +00:00
Narayan Kamath 1ef9d2dfbe zip_archive_test: compare error codes directly.
Make them available to tests so that they can compare values
directly instead of comparing error code strings.

Test: zip_archive_test
Change-Id: I1c78c24651ff7549b5125a49a4e67bbd8295c617
2017-06-19 13:03:41 +01:00
Elliott Hughes 3e67944d2e Merge "Add libziparchive-based unzip." 2017-06-15 21:30:56 +00:00
Narayan Kamath 162b705801 zip_archive: validate data descriptor contents.
- Ensure that the compressed and uncompressed lengths in the data
  descriptor match those in the central directory.

- Calculate CRCs for compressed entries and ensure that those CRCs
  match the information in the central directory / data descriptor.
  This is currently guarded by a flag and will be turned on at a
  later stage.

- Finally, always trust the LFH when it comes to indicating the
  existence of a data descriptor. We no longer require this info to
  match the central directory to bring this in line with other zip
  processing tools. (see NOTE in the included change).

Bug: 62047801
Test: zip_archive_test
Change-Id: Icceea66a8b1937c08a4af9aee97bc77ad7218c0f
2017-06-14 14:29:53 +01:00
Adam Lesinski 1d105aa597 libziparchive: verify that gpb flags match
The Central File Header and Local File Header should
have the same general purpose bit flags set for
consistency.

Bug: 36686974
Test: existing tests pass (ziparchive-tests)

(cherry picked from commit 73b3aa541f)

Change-Id: Ia672a28732154a564ea2f2e3520238bb77924a56
2017-06-14 14:25:55 +01:00
Elliott Hughes 55fd293dfe Add libziparchive-based unzip.
Bug: N/A
Test: new toybox unzip.test
Change-Id: I00be388578be9a0a0390a9e2ecfac664c6ab39ca
2017-06-04 11:53:48 -07:00
Adam Lesinski d987c9dd9e ZipWriter: Keep LFH and CFH in sync
We change the GPB in the LocalFileHeader if the entry can not
have a trailing DataDescriptor. Make sure to patch the
CentralFileHeader to have the same bits set.

Modify ZipArchive to check that the data descriptor bit is consistent
between Central and Local file headers.

(cherry-pick of commit e0eca55fe6)

Test: make ziparchive-tests
Bug: 36686974
Change-Id: Ied167570abcf6426b1c678cd40123e5ad65909db
2017-04-11 01:45:25 +00:00
Tao Bao af2a8858c4 Merge "ziparchive: Allow ExtractEntryToFile() to work with block device."
am: 787482ecd9

Change-Id: Icdce4207cb64e084e200b6f7f8ae194831d5c4f0
2016-11-15 22:11:21 +00:00
Tao Bao a456c21348 ziparchive: Allow ExtractEntryToFile() to work with block device.
FileWriter::Create() calls ftruncate(2) to truncate the destination
file, which doesn't work with FD that references a block device. It
leads to kIoError when calling ExtractEntryToFile() to extract an entry
to block device FD.

As a result, it fails the package_extract_file() command in OTA updates
(e.g. 'package_extract_file("boot.img",
"/dev/block/platform/soc.0/f9824900.sdhci/by-name/boot")').

This CL skips the call to ftruncate(2) if FD references a block device.

Bug: 32903624
Test: ziparchive-tests works.
Test: Build an OTA updater (m updater) and call package_extract_file().

Change-Id: Ia81116f1a8d7cab802396bdc32c6096b4cb56a3c
2016-11-15 10:19:04 -08:00
Mark Salyzyn 8a7297a09f Merge "system/core: preparation to pull back interfaces from android/log.h"
am: 27d2d49f48

Change-Id: I604bb1d4cf62636663fa92e3d14a55887dbcae23
2016-10-20 18:03:57 +00:00
Mark Salyzyn cfd5b080af system/core: preparation to pull back interfaces from android/log.h
Point to log/log.h where necessary, define LOG_TAG where necessary.
Accept that private/android_logger.h is suitable replacement for
log/logger.h and android/log.h.

Correct liblog/README

Effectively a cleanup and controlled select revert of
'system/core: drop or replace log/logger.h' and
'system/core: Replace log/log.h with android/log.h'.

Test: compile
Bug: 30465923
Change-Id: Ic2ad157bad6f5efe2c6af293a73bb753300b17a2
2016-10-20 08:11:39 -07:00
Tianjie Xu b2ca85184b Merge "Add functions in recovery/minzip to libziparchive"
am: 4bc51d1ea4

Change-Id: Ica94e8050286bed1c5bc23a33305050ab8da8ec5
2016-10-18 21:57:30 +00:00
Tianjie Xu 18c25920c2 Add functions in recovery/minzip to libziparchive
Add two functions libziparchive that libminzip has. And create
corresponding unit tests.
1. Open a zip archive from a memory mapped region.
2. A new writer that takes a call back function pointer.
(Used by the OTA updater to stream the data.)

Test: Unit tests passed
Bug: 19472796
Change-Id: I2b2daec71174afe221030357e39bff5faea51e72
2016-10-15 23:16:34 -07:00
Tianjie Xu 206150bf78 Fix out of bound access in libziparchive am: 1ee4892e66 am: c24dd97654 am: 8788591224 am: 167562aa76 am: 398adf9b55 am: 680c3f1dc4 am: f0ce69815f
am: 6fb68bb196

Change-Id: Id8f75debfae94d35dbbee9799632cba3f72648d0
2016-10-07 00:30:02 +00:00
Tianjie Xu 6fb68bb196 Fix out of bound access in libziparchive am: 1ee4892e66 am: c24dd97654 am: 8788591224 am: 167562aa76 am: 398adf9b55 am: 680c3f1dc4
am: f0ce69815f

Change-Id: I8e69df083b6d5cd3a7e6bf7113ae836932c2e9f2
2016-10-07 00:26:31 +00:00
Tianjie Xu 167562aa76 Fix out of bound access in libziparchive am: 1ee4892e66 am: c24dd97654
am: 8788591224

Change-Id: I2f0ca98c74f23b1125f29b1eaf810143a83754b4
2016-10-07 00:11:29 +00:00
Mark Salyzyn ff2dcd9af9 system/core Replace log/log.h with android/log.h
Should use android/log.h instead of log/log.h as a good example
to all others.  Adjust header order to comply with Android Coding
standards.

Test: Compile
Bug: 26552300
Bug: 31289077
Change-Id: I33a8fb4e754d2dc4754d335660c450e0a67190fc
2016-09-30 12:47:05 -07:00
Tianjie Xu 1ee4892e66 Fix out of bound access in libziparchive
The boundary check of an invalid EOCD record may succeed due to the
overflow of uint32_t. Fix the check.

Test: Open the crash.apk and libziparchive reports the offset error as expected.

Bug: 31251826
Change-Id: I1d8092a19b73886a671bc9d291cfc27d65e3d236
(cherry picked from commit ae8180c06d)
2016-09-22 18:24:53 -07:00
Colin Cross 7c6c7f0b9f Fix warnings in libziparchive
system/core/include is included in the global include path using
-isystem, which hides all warnings.  zlib.h is included through
system/core/include/ziparchive/zip_archive.h, which was hiding warnings
in it.  Use a #pragma around the call to deflateInit2, it is a macro
that expands to an old-style cast, in preparation for moving from
-isystem to -I.  Also move the ZipString constructor to zip_archive.cc
so it can assert on the length of the string parameter and fix an
implicit conversion from size_t to uint16_t.

Test: m -j native
Bug: 31492149
Change-Id: I74cdad7fe9c723859b5cfbea73c8f27d9d9ca265
2016-09-20 09:23:47 -07:00