package io.eguan.utils.unix;
/*
* #%L
* Project eguan
* %%
* Copyright (C) 2012 - 2017 Oodrive
* %%
* 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.
* #L%
*/
import io.eguan.utils.RunCmdUtils;
import java.io.File;
import java.io.IOException;
import javax.annotation.concurrent.Immutable;
/**
* File system inside a file.
*
* @author oodrive
*/
@Immutable
public class UnixFsFile {
/** File containing the file system */
private final File file;
/** File / file system size */
private final long size;
/** file system type (ext3, bfs, reiserfs, vfat, ...) */
private final String type;
/** Mount options */
private final String mopts;
/**
* Define a file containing a file system.
*
* @param file
* file containing the file system. The file may not exist: in this case, the method
* {@link UnixFsFile#create()} must be call before mounting the file system.
* @param size
* file size. The size must be large enough to contain a file system of the given type.
* @param type
* file system type (ext3, bfs, xfs, ...)
* @param mopts
* mount options string. The options are separated by a comma. May be <code>null</code>
*/
public UnixFsFile(final File file, final long size, final String type, final String mopts) {
super();
if ((file == null) || (type == null)) {
throw new NullPointerException();
}
if (size <= 0) {
throw new IllegalArgumentException("size=" + size);
}
this.file = file;
this.size = size;
this.type = type;
this.mopts = mopts;
}
/**
* Create the file and the file system. If a file already exists, it is deleted.
*
* @throws IOException
* on any failure.
*/
public final void create() throws IOException {
// Delete any existing element and create a new file
this.file.delete();
if (!this.file.createNewFile()) {
throw new IOException("Failed to create file '" + this.file.getAbsolutePath() + "'");
}
// Set the file size to the give FS size
// try with fallocate. Use standard call on failure
final String absPath = this.file.getAbsolutePath();
final String sizeStr = Long.toString(this.size);
try {
final String[] fallocate = new String[] { "fallocate", "-l", sizeStr, absPath };
RunCmdUtils.runCmd(fallocate, this.file);
}
catch (final IOException e1) {
// No fallocate? try truncate
try {
final String[] truncate = new String[] { "truncate", "-s", sizeStr, absPath };
RunCmdUtils.runCmd(truncate, this.file);
}
catch (final IOException e2) {
// No truncate? try dd
// Need to set the dd block size (bs=1 is REALLY slow)
final String sizeStrKo = Long.toString(this.size / 1024);
final String[] dd = new String[] { "dd", "if=/dev/zero", "of=" + absPath, "bs=1024",
"count=" + sizeStrKo };
RunCmdUtils.runCmd(dd, this.file);
}
}
// Create the file system
// TODO: add mkfs options
// May have to respond to some questions, for example with ext2/3/4 (Proceed anyway? (y,n))
final String[] mkfs = new String[] { "/sbin/mkfs", "-t", this.type, absPath };
RunCmdUtils.runCmd(mkfs, this.file, "y\n", new String[] { "LANG", "C", "LANGUAGE", "C" });
}
/**
* Create a new {@link UnixMount} to mount the {@link UnixFsFile} in the given directory.
*
* @param mountPoint
* @return {@link UnixMount} to mount the {@link UnixFsFile} in the given directory.
*/
public final UnixMount newUnixMount(final File mountPoint) {
// Add the option loop
String opts = "loop";
if (this.mopts != null) {
opts = opts + "," + this.mopts;
}
return new UnixMount(mountPoint, this.file.getAbsolutePath(), opts, this.type);
}
}