1/*
2 * Copyright (C) 2015 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 "flatten/Archive.h"
18#include "util/Files.h"
19#include "util/StringPiece.h"
20
21#include <cstdio>
22#include <memory>
23#include <string>
24#include <vector>
25#include <ziparchive/zip_writer.h>
26
27namespace aapt {
28
29namespace {
30
31struct DirectoryWriter : public IArchiveWriter {
32    std::string mOutDir;
33    std::unique_ptr<FILE, decltype(fclose)*> mFile = { nullptr, fclose };
34
35    bool open(IDiagnostics* diag, const StringPiece& outDir) {
36        mOutDir = outDir.toString();
37        file::FileType type = file::getFileType(mOutDir);
38        if (type == file::FileType::kNonexistant) {
39            diag->error(DiagMessage() << "directory " << mOutDir << " does not exist");
40            return false;
41        } else if (type != file::FileType::kDirectory) {
42            diag->error(DiagMessage() << mOutDir << " is not a directory");
43            return false;
44        }
45        return true;
46    }
47
48    bool startEntry(const StringPiece& path, uint32_t flags) override {
49        if (mFile) {
50            return false;
51        }
52
53        std::string fullPath = mOutDir;
54        file::appendPath(&fullPath, path);
55        file::mkdirs(file::getStem(fullPath));
56
57        mFile = { fopen(fullPath.data(), "wb"), fclose };
58        if (!mFile) {
59            return false;
60        }
61        return true;
62    }
63
64    bool writeEntry(const BigBuffer& buffer) override {
65        if (!mFile) {
66            return false;
67        }
68
69        for (const BigBuffer::Block& b : buffer) {
70            if (fwrite(b.buffer.get(), 1, b.size, mFile.get()) != b.size) {
71                mFile.reset(nullptr);
72                return false;
73            }
74        }
75        return true;
76    }
77
78    bool writeEntry(const void* data, size_t len) override {
79        if (fwrite(data, 1, len, mFile.get()) != len) {
80            mFile.reset(nullptr);
81            return false;
82        }
83        return true;
84    }
85
86    bool finishEntry() override {
87        if (!mFile) {
88            return false;
89        }
90        mFile.reset(nullptr);
91        return true;
92    }
93};
94
95struct ZipFileWriter : public IArchiveWriter {
96    std::unique_ptr<FILE, decltype(fclose)*> mFile = { nullptr, fclose };
97    std::unique_ptr<ZipWriter> mWriter;
98
99    bool open(IDiagnostics* diag, const StringPiece& path) {
100        mFile = { fopen(path.data(), "w+b"), fclose };
101        if (!mFile) {
102            diag->error(DiagMessage() << "failed to open " << path << ": " << strerror(errno));
103            return false;
104        }
105        mWriter = util::make_unique<ZipWriter>(mFile.get());
106        return true;
107    }
108
109    bool startEntry(const StringPiece& path, uint32_t flags) override {
110        if (!mWriter) {
111            return false;
112        }
113
114        size_t zipFlags = 0;
115        if (flags & ArchiveEntry::kCompress) {
116            zipFlags |= ZipWriter::kCompress;
117        }
118
119        if (flags & ArchiveEntry::kAlign) {
120            zipFlags |= ZipWriter::kAlign32;
121        }
122
123        int32_t result = mWriter->StartEntry(path.data(), zipFlags);
124        if (result != 0) {
125            return false;
126        }
127        return true;
128    }
129
130    bool writeEntry(const void* data, size_t len) override {
131        int32_t result = mWriter->WriteBytes(data, len);
132        if (result != 0) {
133            return false;
134        }
135        return true;
136    }
137
138    bool writeEntry(const BigBuffer& buffer) override {
139        for (const BigBuffer::Block& b : buffer) {
140            int32_t result = mWriter->WriteBytes(b.buffer.get(), b.size);
141            if (result != 0) {
142                return false;
143            }
144        }
145        return true;
146    }
147
148    bool finishEntry() override {
149        int32_t result = mWriter->FinishEntry();
150        if (result != 0) {
151            return false;
152        }
153        return true;
154    }
155
156    virtual ~ZipFileWriter() {
157        if (mWriter) {
158            mWriter->Finish();
159        }
160    }
161};
162
163} // namespace
164
165std::unique_ptr<IArchiveWriter> createDirectoryArchiveWriter(IDiagnostics* diag,
166                                                             const StringPiece& path) {
167
168    std::unique_ptr<DirectoryWriter> writer = util::make_unique<DirectoryWriter>();
169    if (!writer->open(diag, path)) {
170        return {};
171    }
172    return std::move(writer);
173}
174
175std::unique_ptr<IArchiveWriter> createZipFileArchiveWriter(IDiagnostics* diag,
176                                                           const StringPiece& path) {
177    std::unique_ptr<ZipFileWriter> writer = util::make_unique<ZipFileWriter>();
178    if (!writer->open(diag, path)) {
179        return {};
180    }
181    return std::move(writer);
182}
183
184} // namespace aapt
185