Skip to content

Return empty defaults when ImageFs metadata files exist but are empty#1565

Open
Smuger wants to merge 1 commit into
utkarshdalal:masterfrom
Smuger:fix/imagefs-empty-variant-crash
Open

Return empty defaults when ImageFs metadata files exist but are empty#1565
Smuger wants to merge 1 commit into
utkarshdalal:masterfrom
Smuger:fix/imagefs-empty-variant-crash

Conversation

@Smuger

@Smuger Smuger commented Jun 10, 2026

Copy link
Copy Markdown

Description

ImageFs.getVariant(), getArch(), and getVersion() called .get(0) on the result of readLines() after only checking file existence. A zero-byte .variant file (caused by interrupted install) crashed the app on every game launch with IndexOutOfBoundsException. Empty files now return the same defaults as missing files, triggering a clean re-install of the base image.

Recording

N/A — crash fix, no visual change.

Type of Change

  • Bug fix

Checklist

  • If I have access to #code-changes, I have discussed this change there and it has been green-lighted. If I do not have access, I have still provided clear context in this PR. If I skip both, I accept that this change may face delays in review, may not be reviewed at all, or may be closed.
  • This change aligns with the current project scope (core functionality, stability, or performance). If not, it has been explicitly approved beforehand.
  • I have attached a recording of the change.
  • I have read and agree to the contribution guidelines in CONTRIBUTING.md.

Summary by cubic

Return safe defaults when ImageFs metadata files are empty, whitespace-only, or invalid to prevent crashes and trigger a clean re-install. Also hardens container migration version parsing, adds docs/tests, and makes FileUtils.readFirstLine() null-safe.

  • Bug Fixes

    • getVariant(), getArch(), and getVersion() now use readFirstLine(), trim values, treat empty/whitespace as missing, and return 0 on non-numeric version; avoids crashes and bad parses.
    • ContainerMigrator uses readFirstLine(), trims, and defaults to 0 for empty/missing .container_migration_version; added tests for readFirstLine() and ImageFs covering missing, empty, whitespace-only, multi-line, and non-numeric cases.
  • Refactors

    • Expanded Javadoc across ImageFs and FileUtils to document behavior and shared directory helpers.

Written for commit 0a17b48. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • Safer handling of small configuration/metadata files: missing, empty, or whitespace-only values now yield safe defaults (version → 0; variant/arch → empty string). Non-numeric version content falls back to 0 to avoid errors.
  • Tests

    • New and expanded tests verifying behavior for missing, empty, whitespace-only, multi-line, and non-numeric file contents.

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds FileUtils.readFirstLine(File), refactors ImageFs and ContainerMigrator to use it with explicit null/blank handling, and adds unit tests for FileUtils plus expanded tests for ImageFs getters.

Changes

File Reading Utility Refactoring

Layer / File(s) Summary
readFirstLine utility method
app/src/main/java/com/winlator/core/FileUtils.java
FileUtils.readFirstLine(File) opens a file, reads the first line as UTF-8 via BufferedReader, and returns it; returns null on IOException or empty input.
FileUtils unit tests
app/src/test/java/com/winlator/core/FileUtilsTest.kt
FileUtilsTest uses TemporaryFolder to validate readFirstLine for missing, empty, single-line, multi-line (first-line only), and whitespace-preserving cases.
ImageFs getter refactors
app/src/main/java/com/winlator/xenvironment/ImageFs.java
getVersion(), getVariant(), and getArch() now use readFirstLine(...) and explicitly handle missing/null/blank content (version -> 0 when absent/unparseable; variant/arch -> "" when absent/null/blank). Javadoc across related accessors updated.
ImageFsTest infrastructure and validation
app/src/test/java/com/winlator/xenvironment/ImageFsTest.kt
ImageFsTest adds a TemporaryFolder rule, helpers to build isolated ImageFs instances, and tests covering missing, empty, whitespace-only, non-numeric (for version), and valid-file cases for variant, arch, and version.
ContainerMigrator refactoring
app/src/main/java/app/gamenative/utils/ContainerMigrator.kt
getContainerMigrationVersion() switches from readLines(...)[0].toInt() to readFirstLine(...)? .trim()? .toInt() ?: 0, improving handling of missing or blank version files.

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I nibble the first line, neat and small,
I hop past blank files and dodge their fall,
A BufferedReader hums beneath my paws,
Tests tuck in corners to check all laws,
ImageFs and Migrator rest without a fright.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 64.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main fix: returning empty defaults when ImageFs metadata files exist but are empty, which directly addresses the core crash fix in this changeset.
Description check ✅ Passed The PR description covers the bug fix, explains the problem and solution, and includes completed checklist items. The optional recording field is marked N/A with justification (crash fix with no visual change), which is reasonable.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Smuger Smuger force-pushed the fix/imagefs-empty-variant-crash branch from 95d09b5 to 0d8c4d3 Compare June 10, 2026 15:57
@Smuger Smuger marked this pull request as ready for review June 11, 2026 09:11
@Smuger Smuger requested a review from utkarshdalal as a code owner June 11, 2026 09:11

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
app/src/test/java/com/winlator/xenvironment/ImageFsTest.kt (1)

61-122: ⚡ Quick win

Add edge-case tests for whitespace and malformed version content.

Coverage is strong for missing/empty/populated files, but it should also assert behavior for:

  • whitespace-only .variant / .arch
  • non-numeric .img_version (expected fallback 0)

That locks in the crash fix and defaulting contract against corrupted metadata formats.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/test/java/com/winlator/xenvironment/ImageFsTest.kt` around lines 61 -
122, Add tests to cover whitespace and malformed version edge cases: create
tests that write whitespace-only content into the .variant and .arch files
(using configDir() and File(...).writeText("   ") or via imageFs() helpers) and
assert imageFs().variant and imageFs().arch return empty strings; also create a
test that writes a non-numeric value into .img_version (e.g., "abc") and assert
imageFs().version returns 0. Use the existing patterns in the file (imageFs(),
configDir(), createVariantFile/createArchFile/createImgVersionFile where
appropriate) and mirror the structure/naming of the other tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/main/java/com/winlator/xenvironment/ImageFs.java`:
- Line 96: getVariant() and getArch() currently return whitespace-only strings
as real values; change their normalization so that if the read line is null or
blank (use String.isBlank()) it is treated as missing and returns the empty
default. In both getVariant() and getArch(), replace the current null-only check
(return line == null ? "" : line) with a check that returns "" when line == null
|| line.isBlank(), otherwise return the original line (or trimmed value if
desired).
- Line 73: The current return in ImageFs that does "return line == null ||
line.isBlank() ? 0 : Integer.parseInt(line.trim());" can throw
NumberFormatException for corrupted/non-numeric .img_version content; wrap the
parsing in a safe parse path (e.g., try-catch around Integer.parseInt or use a
safe parse helper) so that any NumberFormatException results in returning 0
instead of crashing; update the method in ImageFs where this line appears to
trim the line, attempt Integer.parseInt inside a catch(NumberFormatException)
and return 0 on failure.

---

Nitpick comments:
In `@app/src/test/java/com/winlator/xenvironment/ImageFsTest.kt`:
- Around line 61-122: Add tests to cover whitespace and malformed version edge
cases: create tests that write whitespace-only content into the .variant and
.arch files (using configDir() and File(...).writeText("   ") or via imageFs()
helpers) and assert imageFs().variant and imageFs().arch return empty strings;
also create a test that writes a non-numeric value into .img_version (e.g.,
"abc") and assert imageFs().version returns 0. Use the existing patterns in the
file (imageFs(), configDir(),
createVariantFile/createArchFile/createImgVersionFile where appropriate) and
mirror the structure/naming of the other tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 04539604-0676-406b-ac2c-272774f5f542

📥 Commits

Reviewing files that changed from the base of the PR and between a0b49d1 and 0d8c4d3.

📒 Files selected for processing (5)
  • app/src/main/java/app/gamenative/utils/ContainerMigrator.kt
  • app/src/main/java/com/winlator/core/FileUtils.java
  • app/src/main/java/com/winlator/xenvironment/ImageFs.java
  • app/src/test/java/com/winlator/core/FileUtilsTest.kt
  • app/src/test/java/com/winlator/xenvironment/ImageFsTest.kt

Comment thread app/src/main/java/com/winlator/xenvironment/ImageFs.java Outdated
Comment thread app/src/main/java/com/winlator/xenvironment/ImageFs.java Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 5 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread app/src/main/java/com/winlator/xenvironment/ImageFs.java Outdated
Comment thread app/src/main/java/com/winlator/xenvironment/ImageFs.java Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/main/java/com/winlator/core/FileUtils.java`:
- Around line 259-260: The readFirstLine(File file) helper should early-return
null when its input is null to avoid a NullPointerException from new
FileInputStream(file); add a null check at the top of readFirstLine (and the
analogous code region around lines 263-266) so that if file == null the method
returns null immediately, preserving the safe-default behavior for unreadable
input and preventing the FileInputStream constructor from being invoked with a
null reference.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6b906810-2673-43f9-9c41-1ab71aa855ca

📥 Commits

Reviewing files that changed from the base of the PR and between 0715398 and 289502d.

📒 Files selected for processing (2)
  • app/src/main/java/com/winlator/core/FileUtils.java
  • app/src/main/java/com/winlator/xenvironment/ImageFs.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/main/java/com/winlator/xenvironment/ImageFs.java

Comment thread app/src/main/java/com/winlator/core/FileUtils.java

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread app/src/main/java/com/winlator/core/FileUtils.java
@Smuger Smuger force-pushed the fix/imagefs-empty-variant-crash branch from e058998 to 0a17b48 Compare June 12, 2026 19:11
@Smuger

Smuger commented Jun 12, 2026

Copy link
Copy Markdown
Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/src/test/java/com/winlator/core/FileUtilsTest.kt (1)

15-44: ⚡ Quick win

Add a test for the explicit null-input contract.

FileUtils.readFirstLine(File) now guarantees null when file is null, but this branch is not covered yet (Lines 15-44). Please add one test to lock that behavior.

Suggested test addition
 class FileUtilsTest {
@@
     `@Test`
     fun `readFirstLine returns null when file is empty`() {
         val file = tmp.newFile("empty.txt")
         assertNull(FileUtils.readFirstLine(file))
     }
+
+    `@Test`
+    fun `readFirstLine returns null when file is null`() {
+        assertNull(FileUtils.readFirstLine(null))
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/test/java/com/winlator/core/FileUtilsTest.kt` around lines 15 - 44,
Add a unit test in FileUtilsTest.kt that asserts FileUtils.readFirstLine(null)
returns null to cover the explicit null-input contract; create a new `@Test`
function (e.g., `readFirstLine_returns_null_when_input_is_null`) that calls
FileUtils.readFirstLine with a null File reference and uses assertNull to verify
the result, matching the style and naming of the existing tests and using the
same test class and imports.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/main/java/com/winlator/xenvironment/ImageFs.java`:
- Around line 240-241: Several ImageFs methods (e.g., getInstalledWineDir)
construct File with an absolute child ("/...") which causes new File(rootDir,
child) to ignore rootDir; change each to use a relative child path (no leading
'/') or build paths via rootDir.toPath().resolve(...) so the result stays inside
the image root. Update all occurrences that call new File(rootDir, "/...")
(including getInstalledWineDir and the other similarly-implemented getters) to
remove the leading slash or use Path.resolve to compose children relative to
rootDir.

---

Nitpick comments:
In `@app/src/test/java/com/winlator/core/FileUtilsTest.kt`:
- Around line 15-44: Add a unit test in FileUtilsTest.kt that asserts
FileUtils.readFirstLine(null) returns null to cover the explicit null-input
contract; create a new `@Test` function (e.g.,
`readFirstLine_returns_null_when_input_is_null`) that calls
FileUtils.readFirstLine with a null File reference and uses assertNull to verify
the result, matching the style and naming of the existing tests and using the
same test class and imports.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9e7f2922-4081-493d-984c-2c4dcea30951

📥 Commits

Reviewing files that changed from the base of the PR and between a14d949 and 0a17b48.

📒 Files selected for processing (5)
  • app/src/main/java/app/gamenative/utils/ContainerMigrator.kt
  • app/src/main/java/com/winlator/core/FileUtils.java
  • app/src/main/java/com/winlator/xenvironment/ImageFs.java
  • app/src/test/java/com/winlator/core/FileUtilsTest.kt
  • app/src/test/java/com/winlator/xenvironment/ImageFsTest.kt

Comment on lines 240 to 241
public File getInstalledWineDir() {
return new File(rootDir, "/opt/installed-wine");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use relative child paths when composing imagefs subdirectories.

Line 241 (and the other listed methods) passes an absolute child path ("/...") to new File(rootDir, child), which ignores rootDir. These methods can resolve outside the imagefs root and violate their own “inside the image” contract.

Proposed fix
-    public File getInstalledWineDir() {
-        return new File(rootDir, "/opt/installed-wine");
-    }
+    public File getInstalledWineDir() {
+        return new File(rootDir, "opt/installed-wine");
+    }

-    public File getTmpDir() {
-        return new File(rootDir, "/tmp");
-    }
+    public File getTmpDir() {
+        return new File(rootDir, "tmp");
+    }

-    public File getLibDir() { return new File(rootDir, "/usr/lib"); }
+    public File getLibDir() { return new File(rootDir, "usr/lib"); }

-    public File getBinDir() { return new File(rootDir, "/usr/bin"); }
+    public File getBinDir() { return new File(rootDir, "usr/bin"); }

-    public File getShareDir() {
-        return new File(rootDir, "/usr/share");
-    }
+    public File getShareDir() {
+        return new File(rootDir, "usr/share");
+    }

-    public File getGlibc32Dir() {
-        return new File(rootDir, "/usr/lib/arm-linux-gnueabihf");
-    }
+    public File getGlibc32Dir() {
+        return new File(rootDir, "usr/lib/arm-linux-gnueabihf");
+    }

-    public File getGlibc64Dir() {
-        return new File(rootDir, "/usr/lib");
-    }
+    public File getGlibc64Dir() {
+        return new File(rootDir, "usr/lib");
+    }

-    public File getLib32Dir() {
-        return new File(rootDir, "/usr/lib/arm-linux-gnueabihf");
-    }
+    public File getLib32Dir() {
+        return new File(rootDir, "usr/lib/arm-linux-gnueabihf");
+    }

-    public File getLib64Dir() {
-        return new File(rootDir, "/usr/lib");
-    }
+    public File getLib64Dir() {
+        return new File(rootDir, "usr/lib");
+    }

-    public File getStorageDir() {
-        return new File(rootDir, "/storage");
-    }
+    public File getStorageDir() {
+        return new File(rootDir, "storage");
+    }

Also applies to: 248-249, 256-257, 262-263, 268-269, 276-277, 284-285, 292-293, 300-301, 308-309

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/winlator/xenvironment/ImageFs.java` around lines 240 -
241, Several ImageFs methods (e.g., getInstalledWineDir) construct File with an
absolute child ("/...") which causes new File(rootDir, child) to ignore rootDir;
change each to use a relative child path (no leading '/') or build paths via
rootDir.toPath().resolve(...) so the result stays inside the image root. Update
all occurrences that call new File(rootDir, "/...") (including
getInstalledWineDir and the other similarly-implemented getters) to remove the
leading slash or use Path.resolve to compose children relative to rootDir.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant