Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
231 changes: 109 additions & 122 deletions src/main/java/org/vafer/jdeb/DataBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,20 @@
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.archivers.tar.TarConstants;
import org.apache.commons.compress.archivers.zip.ZipEncoding;
import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
import org.apache.commons.compress.compressors.CompressorException;
import org.vafer.jdeb.utils.Utils;

/**
* Builds the data archive of the Debian package.
*/
class DataBuilder {
abstract class DataBuilder {

private Console console;
protected Console console;

private ZipEncoding encoding;
protected ZipEncoding encoding;

private static final class Total {
private BigInteger count = BigInteger.valueOf(0);
protected static final class Total {
protected BigInteger count = BigInteger.valueOf(0);

public void add( long size ) {
count = count.add(BigInteger.valueOf(size));
Expand All @@ -58,12 +57,7 @@ public String toString() {
}
}

DataBuilder(Console console) {
this.console = console;
this.encoding = ZipEncodingHelper.getZipEncoding(null);
}

private void checkField(String name, int length) throws IOException {
protected void checkField(String name, int length) throws IOException {
if (name != null) {
ByteBuffer b = encoding.encode(name);
if (b.limit() > length) {
Expand Down Expand Up @@ -98,59 +92,61 @@ BigInteger buildData(Collection<DataProducer> producers, File output, final Stri

final Total dataSize = new Total();

final List<String> addedDirectories = new ArrayList<String>();
final DataConsumer receiver = new DataConsumer() {
public void onEachDir( String dirname, String linkname, String user, int uid, String group, int gid, int mode, long size ) throws IOException {
// Check link name
checkField(linkname, TarConstants.NAMELEN);
// Check user name
checkField(user, TarConstants.UNAMELEN);
// Check group name
checkField(group, TarConstants.GNAMELEN);
final List<String> addedDirectories = new ArrayList<String>();
final DataConsumer receiver = new DataConsumer() {
public void onEachDir(String dirname, String linkname, String user, int uid, String group, int gid, int mode, long size) throws IOException {
// Check link name
checkField(linkname, TarConstants.NAMELEN);
// Check user name
checkField(user, TarConstants.UNAMELEN);
// Check group name
checkField(group, TarConstants.GNAMELEN);

dirname = fixPath(dirname);
dirname = fixPath(dirname);

createParentDirectories(dirname, user, uid, group, gid);
createParentDirectories(dirname, user, uid, group, gid);

// The directory passed in explicitly by the caller also gets the passed-in mode. (Unlike
// the parent directories for now. See related comments at "int mode =" in
// createParentDirectories, including about a possible bug.)
createDirectory(dirname, user, uid, group, gid, mode, 0);
// The directory passed in explicitly by the caller also gets
// the passed-in mode. (Unlike
// the parent directories for now. See related comments at
// "int mode =" in
// createParentDirectories, including about a possible bug.)
createDirectory(dirname, user, uid, group, gid, mode, 0);

console.debug("dir: " + dirname);
}

public void onEachFile( InputStream inputStream, String filename, String linkname, String user, int uid, String group, int gid, int mode, long size ) throws IOException {
// Check link name
checkField(linkname, TarConstants.NAMELEN);
// Check user name
checkField(user, TarConstants.UNAMELEN);
// Check group name
checkField(group, TarConstants.GNAMELEN);
console.debug("dir: " + dirname);
}

filename = fixPath(filename);
public void onEachFile(InputStream inputStream, String filename, String linkname, String user, int uid, String group, int gid, int mode, long size) throws IOException {
// Check link name
checkField(linkname, TarConstants.NAMELEN);
// Check user name
checkField(user, TarConstants.UNAMELEN);
// Check group name
checkField(group, TarConstants.GNAMELEN);

createParentDirectories(filename, user, uid, group, gid);
filename = fixPath(filename);

final TarArchiveEntry entry = new TarArchiveEntry(filename, true);
createParentDirectories(filename, user, uid, group, gid);

entry.setUserName(user);
entry.setUserId(uid);
entry.setGroupName(group);
entry.setGroupId(gid);
entry.setMode(mode);
entry.setSize(size);
final TarArchiveEntry entry = new TarArchiveEntry(filename, true);

tarOutputStream.putArchiveEntry(entry);
entry.setUserName(user);
entry.setUserId(uid);
entry.setGroupName(group);
entry.setGroupId(gid);
entry.setMode(mode);
entry.setSize(size);

dataSize.add(size);
digest.reset();
tarOutputStream.putArchiveEntry(entry);

Utils.copy(inputStream, new DigestOutputStream(tarOutputStream, digest));
dataSize.add(size);
digest.reset();

final String md5 = Utils.toHex(digest.digest());
Utils.copy(inputStream, new DigestOutputStream(tarOutputStream, digest));

tarOutputStream.closeArchiveEntry();
final String md5 = Utils.toHex(digest.digest());

tarOutputStream.closeArchiveEntry();

console.debug(
"file:" + entry.getName() +
Expand All @@ -165,33 +161,33 @@ public void onEachFile( InputStream inputStream, String filename, String linknam
" md5: " + md5
);

// append to file md5 list
checksums.append(md5).append(" ").append(entry.getName()).append('\n');
}
// append to file md5 list
checksums.append(md5).append(" ").append(entry.getName()).append('\n');
}

public void onEachLink(String path, String linkname, boolean symlink, String user, int uid, String group, int gid, int mode) throws IOException {
// Check link name
checkField(linkname, TarConstants.NAMELEN);
// Check user name
checkField(user, TarConstants.UNAMELEN);
// Check group name
checkField(group, TarConstants.GNAMELEN);
public void onEachLink(String path, String linkname, boolean symlink, String user, int uid, String group, int gid, int mode) throws IOException {
// Check link name
checkField(linkname, TarConstants.NAMELEN);
// Check user name
checkField(user, TarConstants.UNAMELEN);
// Check group name
checkField(group, TarConstants.GNAMELEN);

path = fixPath(path);
path = fixPath(path);

createParentDirectories(path, user, uid, group, gid);
createParentDirectories(path, user, uid, group, gid);

final TarArchiveEntry entry = new TarArchiveEntry(path, symlink ? TarArchiveEntry.LF_SYMLINK : TarArchiveEntry.LF_LINK);
entry.setLinkName(linkname);
final TarArchiveEntry entry = new TarArchiveEntry(path, symlink ? TarArchiveEntry.LF_SYMLINK : TarArchiveEntry.LF_LINK, true);
entry.setLinkName(linkname);

entry.setUserName(user);
entry.setUserId(uid);
entry.setGroupName(group);
entry.setGroupId(gid);
entry.setMode(mode);
entry.setUserName(user);
entry.setUserId(uid);
entry.setGroupName(group);
entry.setGroupId(gid);
entry.setMode(mode);

tarOutputStream.putArchiveEntry(entry);
tarOutputStream.closeArchiveEntry();
tarOutputStream.putArchiveEntry(entry);
tarOutputStream.closeArchiveEntry();

console.debug(
"link:" + entry.getName() +
Expand All @@ -201,54 +197,61 @@ public void onEachLink(String path, String linkname, boolean symlink, String use
" userid:" + entry.getUserId() +
" groupname:" + entry.getGroupName() +
" groupid:" + entry.getGroupId()
);
}


private void createDirectory( String directory, String user, int uid, String group, int gid, int mode, long size ) throws IOException {
// All dirs should end with "/" when created, or the test DebAndTaskTestCase.testTarFileSet() thinks its a file
// and so thinks it has the wrong permission.
// This consistency also helps when checking if a directory already exists in addedDirectories.

if (!directory.endsWith("/")) {
directory += "/";
}

if (!addedDirectories.contains(directory)) {
TarArchiveEntry entry = new TarArchiveEntry(directory, true);
entry.setUserName(user);
entry.setUserId(uid);
entry.setGroupName(group);
entry.setGroupId(gid);
entry.setMode(mode);
entry.setSize(size);

tarOutputStream.putArchiveEntry(entry);
tarOutputStream.closeArchiveEntry();
addedDirectories.add(directory); // so addedDirectories consistently have "/" for finding duplicates.
}
}
);
}

private void createDirectory(String directory, String user, int uid, String group, int gid, int mode, long size) throws IOException {
// All dirs should end with "/" when created, or the test
// DebAndTaskTestCase.testTarFileSet() thinks its a file
// and so thinks it has the wrong permission.
// This consistency also helps when checking if a directory
// already exists in addedDirectories.

if (directory == null) {
return;
}

if (!directory.endsWith("/")) {
directory += "/";
}

if (!addedDirectories.contains(directory)) {
TarArchiveEntry entry = new TarArchiveEntry(directory, true);
entry.setUserName(user);
entry.setUserId(uid);
entry.setGroupName(group);
entry.setGroupId(gid);
entry.setMode(mode);
entry.setSize(size);

tarOutputStream.putArchiveEntry(entry);
tarOutputStream.closeArchiveEntry();
addedDirectories.add(directory); // so addedDirectories consistently have "/" for finding duplicates.
}
}

private void createParentDirectories( String filename, String user, int uid, String group, int gid ) throws IOException {
String dirname = fixPath(new File(filename).getParent());
String dirname = fixPath(new File(filename).getParent());

// Debian packages must have parent directories created
// before sub-directories or files can be installed.
// For example, if an entry of ./usr/lib/foo/bar existed
// in a .deb package, but the ./usr/lib/foo directory didn't
// exist, the package installation would fail. The .deb must
// then have an entry for ./usr/lib/foo and then ./usr/lib/foo/bar

if (dirname == null) {
return;
}

// The loop below will create entries for all parent directories
// to ensure that .deb packages will install correctly.
String[] pathParts = dirname.split("/");
String parentDir = "./";
String parentDir = fixPath(pathParts[0] + "/");

for (int i = 1; i < pathParts.length; i++) {
parentDir += pathParts[i] + "/";

// Make it so the dirs can be traversed by users.
// We could instead try something more granular, like setting the directory
// permission to 'rx' for each of the 3 user/group/other read permissions
Expand Down Expand Up @@ -284,24 +287,8 @@ private void createParentDirectories( String filename, String user, int uid, Str
return dataSize.count;
}

private String fixPath( String path ) {
if (path == null || path.equals(".")) {
return path;
}

// If we're receiving directory names from Windows, then we'll convert to use slash
// This does eliminate the ability to use of a backslash in a directory name on *NIX,
// but in practice, this is a non-issue
if (path.contains("\\")) {
path = path.replace('\\', '/');
}
// ensure the path is like : ./foo/bar
if (path.startsWith("/")) {
path = "." + path;
} else if (!path.startsWith("./")) {
path = "./" + path;
}
return path;
}
protected abstract String fixPath( String path );

protected abstract String getPathPrefix();

}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
/**
* Builds the control archive of the Debian package.
*/
class ControlBuilder {
class DebControlBuilder {

/** The name of the package maintainer scripts */
private static final Set<String> MAINTAINER_SCRIPTS = new HashSet<String>(Arrays.asList("preinst", "postinst", "prerm", "postrm", "config"));
Expand All @@ -59,7 +59,7 @@ class ControlBuilder {
private final String openReplaceToken;
private final String closeReplaceToken;

ControlBuilder(Console console, VariableResolver resolver, String openReplaceToken, String closeReplaceToken) {
DebControlBuilder(Console console, VariableResolver resolver, String openReplaceToken, String closeReplaceToken) {
this.console = console;
this.resolver = resolver;
this.openReplaceToken = openReplaceToken;
Expand Down
59 changes: 59 additions & 0 deletions src/main/java/org/vafer/jdeb/DebDataBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2014 The jdeb developers.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.vafer.jdeb;

import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;

/**
* Builds the data archive of the Debian package.
*/
class DebDataBuilder extends DataBuilder {

private static final String PATH_PREFIX = "./";

DebDataBuilder(final Console console) {
this.console = console;
this.encoding = ZipEncodingHelper.getZipEncoding(null);
}

@Override
protected String fixPath(String path) {
if (path == null || path.equals(".")) {
return path;
}

// If we're receiving directory names from Windows, then we'll convert to use slash
// This does eliminate the ability to use of a backslash in a directory name on *NIX,
// but in practice, this is a non-issue
if (path.contains("\\")) {
path = path.replace('\\', '/');
}
// ensure the path is like : ./foo/bar
if (path.startsWith("/")) {
path = "." + path;
} else if (!path.startsWith(PATH_PREFIX)) {
path = PATH_PREFIX + path;
}
return path;
}

@Override
protected String getPathPrefix() {
return PATH_PREFIX;
}

}
Loading