Compare commits
2 Commits
feature/ja
...
fix/bugs
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
063cbbd568 | ||
| e15e7f26d5 |
33
README.md
33
README.md
@@ -1,15 +1,13 @@
|
|||||||
# cflash
|
# cflash
|
||||||
Small and lightweight image and iso flasher build on `dd`.
|
A small and lightweight wrapper for `dd` that strips away the complexity of CLI flashing.
|
||||||
|
|
||||||
[](https://opensource.org/licenses/MIT)
|
[](https://opensource.org/licenses/MIT)
|
||||||
[](https://www.java.com/en/)
|
[](https://www.java.com/en/)
|
||||||
[](https://www.linux.org/)
|
[](https://www.linux.org/)
|
||||||
[](https://en.wikipedia.org/wiki/Software_release_life_cycle#Beta)
|
[](https://github.com/Cametendo/cflash-git/releases/tag/cflash-1.0.0)
|
||||||
|
|
||||||
# About
|
# About
|
||||||
Java program using `dd` to make flashing iso and image files easier on the terminal. This program allows anyone to flash iso and image files without having to search for extra GUI tools by keeping it simple and resource-friendly.
|
Small and lightweight wrapper written in Java for `dd` designed to simplify CLI flashing while protecting your hardware from accidental command-line errors. cflash replaces the syntax with a clear, safe interface, providing a reliable workflow for both newcomers and power users.
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
# Requirements
|
# Requirements
|
||||||
- `Java`: 21 (Download [here](https://www.oracle.com/java/technologies/downloads/#java21))
|
- `Java`: 21 (Download [here](https://www.oracle.com/java/technologies/downloads/#java21))
|
||||||
@@ -32,12 +30,19 @@ Java program using `dd` to make flashing iso and image files easier on the termi
|
|||||||
mvn clean package
|
mvn clean package
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Run the application:
|
3. Run cflash:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
java -jar target/cflash-<version>.jar (optionally add arguments here, like with dd)
|
java -jar target/cflash-<version>.jar (optionally add arguments here, like with dd)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
4. Update cflash:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git pull
|
||||||
|
mvn clean package
|
||||||
|
```
|
||||||
|
|
||||||
### System-wide Installation
|
### System-wide Installation
|
||||||
|
|
||||||
To install cflash globally so that it can be run from any terminal:
|
To install cflash globally so that it can be run from any terminal:
|
||||||
@@ -55,10 +60,10 @@ To install cflash globally so that it can be run from any terminal:
|
|||||||
chmod +x build.sh install.sh
|
chmod +x build.sh install.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Build the project using the provided build script:
|
3. Build the project using the provided build script (requires root privileges):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./build.sh
|
sudo ./build.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Install globally (requires root privileges):
|
4. Install globally (requires root privileges):
|
||||||
@@ -73,6 +78,12 @@ To install cflash globally so that it can be run from any terminal:
|
|||||||
cflash
|
cflash
|
||||||
```
|
```
|
||||||
|
|
||||||
|
6. Update cflash (reguires root privileges):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo cflash --update
|
||||||
|
```
|
||||||
|
|
||||||
**Notes:**
|
**Notes:**
|
||||||
|
|
||||||
* The `build.sh` script compiles all Java source files and creates an executable `cflash.jar`.
|
* The `build.sh` script compiles all Java source files and creates an executable `cflash.jar`.
|
||||||
@@ -85,6 +96,12 @@ To install cflash globally so that it can be run from any terminal:
|
|||||||
cd cflash
|
cd cflash
|
||||||
makepkg -si
|
makepkg -si
|
||||||
```
|
```
|
||||||
|
* You can update cflash using `yay` / `paru` via `yay -S cflash` / `paru -S cflash` or by rebuilding the package:
|
||||||
|
```bash
|
||||||
|
git clone https://aur.archlinux.org/cflash.git
|
||||||
|
cd cflash
|
||||||
|
makepkg -si
|
||||||
|
```
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
- Using the command `cflash` in the terminal, will start the flashing process. You will be asked several question before the flashing begins:
|
- Using the command `cflash` in the terminal, will start the flashing process. You will be asked several question before the flashing begins:
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
package org.cametendo;
|
package org.cametendo;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@@ -11,29 +12,31 @@ import org.jline.terminal.TerminalBuilder;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles file path input and validation for disk image files.
|
* Handles file path input and validation for disk image files.
|
||||||
*
|
* * <p>This class provides functionality to interactively prompt users for image file paths
|
||||||
* <p>This class provides functionality to interactively prompt users for image file paths
|
* with tab completion support via JLine. It handles Unix-style tilde (~) expansion
|
||||||
* with tab completion support, validate that files exist and are regular files, and
|
* to the user's home directory and validates that paths point to existing regular files.</p>
|
||||||
* validate file paths from command-line arguments.</p>
|
* * @author Cametendo
|
||||||
*
|
* @version 1.1
|
||||||
* @author Cametendo
|
|
||||||
* @version 1.0
|
|
||||||
*/
|
*/
|
||||||
public class FilePathAdd {
|
public class FilePathAdd {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the validated path to the selected image file.
|
* Stores the validated absolute path to the selected image file.
|
||||||
*/
|
*/
|
||||||
public static String ImagePath = "";
|
public static String ImagePath = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant representing the current user's home directory path.
|
||||||
|
*/
|
||||||
|
public static final String Home = System.getProperty("user.home");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interactively prompts the user to select an image file path.
|
* Interactively prompts the user to select an image file path.
|
||||||
*
|
* * <p>Uses JLine for enhanced terminal interaction with {@link FileNameCompleter}.
|
||||||
* <p>Uses JLine for enhanced terminal interaction with tab completion support.
|
* The method expands the tilde (~) character if present at the start of the string
|
||||||
* Validates that the selected path points to an existing regular file.</p>
|
* and validates that the resulting path is a regular file before returning.</p>
|
||||||
*
|
* * @return The validated absolute path to the selected image file
|
||||||
* @return The validated path to the selected image file
|
* @throws IOException If there are I/O errors during terminal setup or file resolution
|
||||||
* @throws IOException If there are I/O errors during terminal setup or file validation
|
|
||||||
*/
|
*/
|
||||||
protected static String filePath() throws IOException {
|
protected static String filePath() throws IOException {
|
||||||
fileQuestion();
|
fileQuestion();
|
||||||
@@ -42,37 +45,48 @@ public class FilePathAdd {
|
|||||||
LineReader reader = LineReaderBuilder.builder().terminal(terminal).completer(new FileNameCompleter()).build();
|
LineReader reader = LineReaderBuilder.builder().terminal(terminal).completer(new FileNameCompleter()).build();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
ImagePath = reader.readLine("Path: ").trim();
|
String input = reader.readLine("Path: ").trim();
|
||||||
|
|
||||||
if (ImagePath.isBlank()) {
|
if (input.isBlank()) {
|
||||||
System.out.println("Oops... You didn't specify a file!");
|
System.out.println("Oops... You didn't specify a file!");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Path path = Path.of(ImagePath);
|
// Expand tilde to the full home directory path
|
||||||
|
if (input.startsWith("~")) {
|
||||||
|
input = Home + input.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Path path = Path.of(input);
|
||||||
|
|
||||||
if (!Files.exists(path) || !Files.isRegularFile(path)) {
|
if (!Files.exists(path) || !Files.isRegularFile(path)) {
|
||||||
System.out.println("Invalid file! Please ensure the path points to an ISO / image file.");
|
System.out.println("Invalid file! Please ensure the path points to an ISO / image file.");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert to a real, absolute path and return
|
||||||
|
ImagePath = path.toRealPath().toString();
|
||||||
System.out.println("Using File: " + ImagePath);
|
System.out.println("Using File: " + ImagePath);
|
||||||
return ImagePath;
|
return ImagePath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates and returns the full path to an image file.
|
* Validates and returns the full path to an image file from a provided string.
|
||||||
*
|
* * <p>This is primarily used for validating command-line arguments. It supports
|
||||||
* <p>Takes a file path, validates that it exists and is a regular file,
|
* tilde expansion and verifies file existence and type.</p>
|
||||||
* and returns the real path. Used for command-line argument validation.</p>
|
* * @param inputPath Raw path string to validate
|
||||||
*
|
* @return Full validated absolute path, or {@code null} if the path is invalid or inaccessible
|
||||||
* @param ImagePath Path to the image file to validate
|
|
||||||
* @return Full validated path to the file, or null if invalid
|
|
||||||
*/
|
*/
|
||||||
public static String validateAndGetFile(String ImagePath) {
|
public static String validateAndGetFile(String inputPath) {
|
||||||
|
if (inputPath == null) return null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Path path = Path.of(ImagePath);
|
if (inputPath.startsWith("~")) {
|
||||||
|
inputPath = Home + inputPath.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Path path = Path.of(inputPath);
|
||||||
if (Files.exists(path) && Files.isRegularFile(path)) {
|
if (Files.exists(path) && Files.isRegularFile(path)) {
|
||||||
return path.toRealPath().toString();
|
return path.toRealPath().toString();
|
||||||
} else {
|
} else {
|
||||||
@@ -86,12 +100,9 @@ public class FilePathAdd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays the prompt for file path input.
|
* Displays the prompt for file path input to the standard output.
|
||||||
*
|
|
||||||
* <p>Informs the user that they should enter the full path to their ISO/image file
|
|
||||||
* and mentions that tab completion is supported for convenience.</p>
|
|
||||||
*/
|
*/
|
||||||
protected static void fileQuestion() {
|
protected static void fileQuestion() {
|
||||||
System.out.println("Please enter the FULL Path of your ISO / Image. (Tab-completion supported)");
|
System.out.println("Please enter the FULL Path of your ISO / Image. (Tab-completion supported)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
1
src/main/java/org/cametendo/GetHome.java
Normal file
1
src/main/java/org/cametendo/GetHome.java
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -23,14 +23,5 @@ public class Greeting {
|
|||||||
*/
|
*/
|
||||||
public static void greeting(Scanner UserInput) {
|
public static void greeting(Scanner UserInput) {
|
||||||
System.out.println("Welcome to cflash!");
|
System.out.println("Welcome to cflash!");
|
||||||
System.out.println("Would you like to flash an image (Y/n)");
|
|
||||||
String input = UserInput.nextLine();
|
|
||||||
if (YesNo.check(input)) {
|
|
||||||
System.out.println("Please choose the to be flashed device (f. e. sda)");
|
|
||||||
} else {
|
|
||||||
System.out.println("Canceling...");
|
|
||||||
System.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,9 @@ public class OSDetector {
|
|||||||
osName = "Haiku OS";
|
osName = "Haiku OS";
|
||||||
} else if (fileName.contains("reactos")) {
|
} else if (fileName.contains("reactos")) {
|
||||||
osName = "ReactOS";
|
osName = "ReactOS";
|
||||||
}
|
} else if (fileName.contains("jgh")) {
|
||||||
|
osName = "JGH OS (Sauerkraut juice)";
|
||||||
|
}
|
||||||
// Generic Fallbacks
|
// Generic Fallbacks
|
||||||
else if (fileName.contains("linux")) {
|
else if (fileName.contains("linux")) {
|
||||||
osName = "Linux";
|
osName = "Linux";
|
||||||
@@ -90,6 +92,6 @@ public class OSDetector {
|
|||||||
osName = "new OS";
|
osName = "new OS";
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("\nFlash complete! Have fun with your " + osName + " installation! 🚀");
|
System.out.println("\nFlash complete! Have fun with your " + osName + " installation!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
package org.cametendo;
|
package org.cametendo;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
@@ -7,99 +8,127 @@ import java.util.Scanner;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles storage device detection, listing, and validation for the flashing process.
|
* Handles storage device detection, listing, and validation for the flashing process.
|
||||||
*
|
* * <p>Includes safety checks to ensure we don't nuke a partition that's currently in use.
|
||||||
* <p>This class provides functionality to list available storage devices using lsblk,
|
* Keeps the user in the loop with a more personal touch.</p>
|
||||||
* validate device paths, and interactively prompt users to select a target device
|
* * @author Cametendo
|
||||||
* for flashing operations.</p>
|
* @version 1.1
|
||||||
*
|
|
||||||
* @author Cametendo
|
|
||||||
* @version 1.0
|
|
||||||
*/
|
*/
|
||||||
public class StorageDeviceLister {
|
public class StorageDeviceLister {
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores the selected device name (without /dev/ prefix).
|
|
||||||
*/
|
|
||||||
public static String device = "";
|
public static String device = "";
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores the full validated path to the selected device.
|
|
||||||
*/
|
|
||||||
public static String fullPath = "";
|
public static String fullPath = "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interactively prompts the user to select a storage device.
|
* Guides the user through selecting a safe, unmounted storage device.
|
||||||
*
|
|
||||||
* <p>Displays a list of available storage devices using the lsblk command,
|
|
||||||
* then prompts the user to enter a device name. Validates the device path
|
|
||||||
* and continues prompting until a valid device is selected.</p>
|
|
||||||
*
|
|
||||||
* @param UserInput Scanner object for reading user input
|
|
||||||
* @return The full validated path to the selected device
|
|
||||||
*/
|
*/
|
||||||
protected static String deviceCheck(Scanner UserInput) {
|
protected static String deviceCheck(Scanner UserInput) {
|
||||||
deviceList();
|
deviceList();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
device = UserInput.nextLine();
|
System.out.print("Target device: ");
|
||||||
|
device = UserInput.nextLine().trim();
|
||||||
|
|
||||||
if (device.isBlank()) {
|
if (device.isBlank()) {
|
||||||
System.out.println("Oops... Device name is empty. Did you missclick?");
|
System.out.println("Oops... Device name is empty. Did you missclick?");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Path path = Path.of("/dev/" + device);
|
|
||||||
|
// Standardize path - allows entering 'sda' or '/dev/sda'
|
||||||
|
String checkPath = device.startsWith("/dev/") ? device : "/dev/" + device;
|
||||||
|
Path path = Path.of(checkPath);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fullPath = path.toRealPath().toString();
|
fullPath = path.toRealPath().toString();
|
||||||
|
|
||||||
|
// Check if the user is about to break their system
|
||||||
|
if (isMounted(fullPath)) {
|
||||||
|
System.out.println("Wait a second! " + fullPath + " is currently mounted.");
|
||||||
|
System.out.println("I can't flash to a device that's in use. Unmount it and try again!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
System.out.println("Using device: " + fullPath);
|
System.out.println("Solid choice. Using device: " + fullPath);
|
||||||
return fullPath;
|
return fullPath;
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
System.out.println("Failed to access device! Invalid path or no access. Please try again.");
|
System.out.println("Hmm... I can't seem to find or access that device. Are you sure you have access to it?");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates and returns the full path to a storage device.
|
* Asks lsblk if the device or its children have an active mount point.
|
||||||
*
|
|
||||||
* <p>Takes a device name (without /dev/ prefix), constructs the full path,
|
|
||||||
* and validates that the device exists and is accessible. Used for command-line
|
|
||||||
* argument validation.</p>
|
|
||||||
*
|
|
||||||
* @param deviceName Device name without /dev/ prefix (e.g., "sda")
|
|
||||||
* @return Full validated path to the device, or null if invalid
|
|
||||||
*/
|
*/
|
||||||
public static String validateAndGetPath(String deviceName) {
|
private static boolean isMounted(String devicePath) {
|
||||||
try {
|
try {
|
||||||
Path path = Path.of("/dev/" + deviceName);
|
ProcessBuilder pb = new ProcessBuilder("lsblk", "-no", "MOUNTPOINT", devicePath);
|
||||||
return path.toRealPath().toString();
|
Process process = pb.start();
|
||||||
} catch (IOException e) {
|
|
||||||
System.out.println("Device not found. Invalid Path or no access.");
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
|
||||||
return null;
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
if (!line.trim().isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
process.waitFor();
|
||||||
|
} catch (IOException | InterruptedException e) {
|
||||||
|
System.out.println("Warning: Mountstatus couldn't be verified");
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays a list of available storage devices using the lsblk command.
|
* Lists devices with enough info for the user to make a smart decision.
|
||||||
*
|
|
||||||
* <p>Executes the lsblk command to show block devices and their properties,
|
|
||||||
* then prompts the user to enter a device name for selection.</p>
|
|
||||||
*/
|
*/
|
||||||
private static void deviceList() {
|
private static void deviceList() {
|
||||||
try {
|
try {
|
||||||
ProcessBuilder pb = new ProcessBuilder("lsblk");
|
System.out.println("Scanning for block devices...");
|
||||||
|
ProcessBuilder pb = new ProcessBuilder("lsblk", "-o", "NAME,SIZE,TYPE,RM,MOUNTPOINT");
|
||||||
Process process = pb.start();
|
Process process = pb.start();
|
||||||
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||||
String line;
|
String line;
|
||||||
while ((line = reader.readLine()) != null) {
|
while ((line = reader.readLine()) != null) {
|
||||||
System.out.println(line);
|
System.out.println(line);
|
||||||
}
|
}
|
||||||
process.waitFor();
|
process.waitFor();
|
||||||
System.out.println("Please enter the name of your device (without /dev/):");
|
|
||||||
|
System.out.println("Enter the name of your device (e.g., sdb or nvme1n1):");
|
||||||
|
|
||||||
} catch (IOException | InterruptedException e) {
|
} catch (IOException | InterruptedException e) {
|
||||||
e.printStackTrace();
|
System.out.println("Failed to run lsblk. Do you have it installed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates a device path (e.g., from a CLI argument).
|
||||||
|
* * <p>Checks if the device exists, resolves the real path, and ensures
|
||||||
|
* it isn't currently mounted before giving the green light.</p>
|
||||||
|
* * @param deviceName Device name (e.g., "sda" or "/dev/sda")
|
||||||
|
* @return Full validated path, or null if it's a bad idea to use it
|
||||||
|
*/
|
||||||
|
public static String validateAndGetPath(String deviceName) {
|
||||||
|
if (deviceName == null || deviceName.isBlank()) return null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Support both "sda" and "/dev/sda"
|
||||||
|
String checkPath = deviceName.startsWith("/dev/") ? deviceName : "/dev/" + deviceName;
|
||||||
|
Path path = Path.of(checkPath);
|
||||||
|
String resolvedPath = path.toRealPath().toString();
|
||||||
|
|
||||||
|
// Safety check for CLI arguments too!
|
||||||
|
if (isMounted(resolvedPath)) {
|
||||||
|
System.out.println("Hold up! " + resolvedPath + " is mounted. I won't let you flash it like that.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolvedPath;
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("Hmm... I couldn't find a device at '" + deviceName + "'. Is it plugged in?");
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user