Package org.vafer.jdeb

Source Code of org.vafer.jdeb.DataBuilder$Total

/*
* 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 java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
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 {

    private Console console;
   
    private ZipEncoding encoding;
   
    private static final class Total {
        private BigInteger count = BigInteger.valueOf(0);

        public void add( long size ) {
            count = count.add(BigInteger.valueOf(size));
        }

        public String toString() {
            return "" + count;
        }
    }

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

    private void checkField(String name, int length) throws IOException {
        if (name != null) {
            ByteBuffer b = encoding.encode(name);
            if (b.limit() > length) {
                throw new IllegalArgumentException("Field '" + name + "' too long, maximum is " + length);
            }
        }
    }

    /**
     * Build the data archive of the deb from the provided DataProducers
     *
     * @param producers
     * @param output
     * @param checksums
     * @param compression the compression method used for the data file
     * @return
     * @throws java.security.NoSuchAlgorithmException
     * @throws java.io.IOException
     * @throws org.apache.commons.compress.compressors.CompressorException
     */
    BigInteger buildData(Collection<DataProducer> producers, File output, final StringBuilder checksums, Compression compression) throws NoSuchAlgorithmException, IOException, CompressorException {

        final File dir = output.getParentFile();
        if (dir != null && (!dir.exists() || !dir.isDirectory())) {
            throw new IOException("Cannot write data file at '" + output.getAbsolutePath() + "'");
        }

        final TarArchiveOutputStream tarOutputStream = new TarArchiveOutputStream(compression.toCompressedOutputStream(new FileOutputStream(output)));
        tarOutputStream.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);

        final MessageDigest digest = MessageDigest.getInstance("MD5");

        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);

                dirname = fixPath(dirname);

                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);

                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);

                filename = fixPath(filename);

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

                final TarArchiveEntry entry = new TarArchiveEntry(filename, true);

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

                tarOutputStream.putArchiveEntry(entry);

                dataSize.add(size);
                digest.reset();

                Utils.copy(inputStream, new DigestOutputStream(tarOutputStream, digest));

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

                tarOutputStream.closeArchiveEntry();

                console.debug(
                    "file:" + entry.getName() +
                        " size:" + entry.getSize() +
                        " mode:" + entry.getMode() +
                        " linkname:" + entry.getLinkName() +
                        " username:" + entry.getUserName() +
                        " userid:" + entry.getUserId() +
                        " groupname:" + entry.getGroupName() +
                        " groupid:" + entry.getGroupId() +
                        " modtime:" + entry.getModTime() +
                        " md5: " + md5
                );

                // append to file md5 list, two spaces to be compatible with GNU coreutils md5sum
                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);

                path = fixPath(path);

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

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

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

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

                console.debug(
                    "link:" + entry.getName() +
                    " mode:" + entry.getMode() +
                    " linkname:" + entry.getLinkName() +
                    " username:" + entry.getUserName() +
                    " 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 createParentDirectories( String filename, String user, int uid, String group, int gid ) throws IOException {
                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 = "./";
                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
                    // found on the file being added (ie, only if "other" has read
                    // permission on the main node, then add o+rx permission on all the containing
                    // directories, same w/ user & group), and then also we'd have to
                    // check the parentDirs collection of those already added to
                    // see if those permissions need to be similarly updated.  (Note, it hasn't
                    // been demonstrated, but there might be a bug if a user specifically
                    // requests a directory with certain permissions,
                    // that has already been auto-created because it was a parent, and if so, go set
                    // the user-requested mode on that directory instead of this automatic one.)
                    // But for now, keeping it simple by making every dir a+rx.   Examples are:
                    // drw-r----- fs/fs   # what you get with setMode(mode)
                    // drwxr-xr-x fs/fs   # Usable. Too loose?
                    int mode = TarArchiveEntry.DEFAULT_DIR_MODE;
       
                    createDirectory(parentDir, user, uid, group, gid, mode, 0);
                }
            }
        };

        try {
            for (DataProducer data : producers) {
                data.produce(receiver);
            }
        } finally {
            tarOutputStream.close();
        }

        console.debug("Total size: " + dataSize);

        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;
    }

}
TOP

Related Classes of org.vafer.jdeb.DataBuilder$Total

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.