Skip to content

Commit bc359bd

Browse files
Address review feedback for sync command
- Fix mount point matching: select longest prefix (most specific) match - Fix root mount matching for paths like /var/log - Add null device guard in flushFileSystem - Print note when device-level flush is skipped (ApiNotFoundException) - Add description attribute to path argument in XML descriptor - Extract findMountPoint into testable public static method - Add unit tests for mount point matching logic (5 tests, all pass)
1 parent efbe76c commit bc359bd

4 files changed

Lines changed: 174 additions & 16 deletions

File tree

fs/build-tests.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ help Output these messages
4040
<test name="org.jnode.test.driver.bus.ide.IDEDriveDescriptorTest" todir="${basedir}/build/reports/junit"/>
4141
<test name="org.jnode.test.fs.filesystem.FSTestSuite" todir="${basedir}/build/reports/junit"/>
4242
<test name="org.jnode.test.fs.driver.tests.BlockDeviceAPITest" todir="${basedir}/build/reports/junit"/>
43+
<test name="org.jnode.test.fs.command.SyncCommandTest" todir="${basedir}/build/reports/junit"/>
4344
</junit>
4445
</target>
4546

fs/descriptors/org.jnode.fs.command.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
<syntax alias="sync">
6464
<empty description="Flush all mounted filesystems to disk"/>
6565
<sequence description="Flush the filesystem at a specific path">
66-
<argument argLabel="path"/>
66+
<argument argLabel="path" description="the path of the filesystem to flush"/>
6767
</sequence>
6868
</syntax>
6969
</extension>

fs/src/commands/org/jnode/fs/command/SyncCommand.java

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -59,19 +59,13 @@ public void execute() throws Exception {
5959

6060
if (argPath.isSet()) {
6161
String path = argPath.getValue().getCanonicalPath();
62-
FileSystem<?> target = null;
63-
String mountPoint = null;
64-
for (Map.Entry<String, FileSystem<?>> e : fss.getMountPoints().entrySet()) {
65-
if (path.equals(e.getKey()) || path.startsWith(e.getKey() + "/")) {
66-
target = e.getValue();
67-
mountPoint = e.getKey();
68-
}
69-
}
70-
if (target == null) {
62+
Map.Entry<String, FileSystem<?>> match =
63+
findMountPoint(fss.getMountPoints(), path);
64+
if (match == null) {
7165
err.println("No filesystem mounted at " + path);
7266
exit(1);
7367
}
74-
flushFileSystem(target, mountPoint, out, err);
68+
flushFileSystem(match.getValue(), match.getKey(), out, err);
7569
} else {
7670
int errors = 0;
7771
for (Map.Entry<String, FileSystem<?>> e : fss.getMountPoints().entrySet()) {
@@ -86,17 +80,51 @@ public void execute() throws Exception {
8680
}
8781
}
8882

83+
public static Map.Entry<String, FileSystem<?>> findMountPoint(
84+
Map<String, FileSystem<?>> mountPoints, String path) {
85+
FileSystem<?> target = null;
86+
String mountPoint = null;
87+
for (Map.Entry<String, FileSystem<?>> e : mountPoints.entrySet()) {
88+
String key = e.getKey();
89+
if (path.equals(key) || path.startsWith(key + "/") ||
90+
(key.length() == 1 && path.length() > 0)) {
91+
if (target == null || e.getKey().length() > mountPoint.length()) {
92+
target = e.getValue();
93+
mountPoint = e.getKey();
94+
}
95+
}
96+
}
97+
if (target == null) {
98+
return null;
99+
}
100+
final FileSystem<?> t = target;
101+
final String mp = mountPoint;
102+
return new Map.Entry<String, FileSystem<?>>() {
103+
public String getKey() { return mp; }
104+
public FileSystem<?> getValue() { return t; }
105+
public FileSystem<?> setValue(FileSystem<?> v) { throw new UnsupportedOperationException(); }
106+
};
107+
}
108+
89109
private void flushFileSystem(FileSystem<?> fs, String mountPoint,
90110
PrintWriter out, PrintWriter err) throws IOException {
91111
if (fs instanceof AbstractFileSystem) {
92112
((AbstractFileSystem<?>) fs).flush();
93113
}
94114
Device device = fs.getDevice();
95-
try {
96-
BlockDeviceAPI api = device.getAPI(BlockDeviceAPI.class);
97-
api.flush();
98-
} catch (ApiNotFoundException ex) {
115+
boolean deviceFlushed = false;
116+
if (device != null) {
117+
try {
118+
BlockDeviceAPI api = device.getAPI(BlockDeviceAPI.class);
119+
api.flush();
120+
deviceFlushed = true;
121+
} catch (ApiNotFoundException ex) {
122+
}
123+
}
124+
out.print("synced " + mountPoint);
125+
if (!deviceFlushed) {
126+
out.print(" (device flush skipped)");
99127
}
100-
out.println("synced " + mountPoint);
128+
out.println();
101129
}
102130
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Copyright (C) 2003-2015 JNode.org
3+
*
4+
* This library is free software; you can redistribute it and/or modify it
5+
* under the terms of the GNU Lesser General Public License as published
6+
* by the Free Software Foundation; either version 2.1 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This library is distributed in the hope that it will be useful, but
10+
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11+
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12+
* License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public License
15+
* along with this library; If not, write to the Free Software Foundation, Inc.,
16+
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17+
*/
18+
19+
package org.jnode.test.fs.command;
20+
21+
import java.io.IOException;
22+
import java.util.HashMap;
23+
import java.util.Map;
24+
25+
import junit.framework.TestCase;
26+
27+
import org.jnode.driver.Device;
28+
import org.jnode.fs.FSEntry;
29+
import org.jnode.fs.FileSystem;
30+
import org.jnode.fs.FileSystemType;
31+
import org.jnode.fs.command.SyncCommand;
32+
33+
public class SyncCommandTest extends TestCase {
34+
35+
private Map<String, FileSystem<?>> mountPoints;
36+
37+
@Override
38+
protected void setUp() throws Exception {
39+
super.setUp();
40+
mountPoints = new HashMap<String, FileSystem<?>>();
41+
mountPoints.put("/", new StubFileSystem("/"));
42+
mountPoints.put("/home", new StubFileSystem("/home"));
43+
mountPoints.put("/home/user", new StubFileSystem("/home/user"));
44+
}
45+
46+
public void testExactMountPointMatch() {
47+
Map.Entry<String, FileSystem<?>> result =
48+
SyncCommand.findMountPoint(mountPoints, "/home");
49+
assertNotNull(result);
50+
assertEquals("/home", result.getKey());
51+
}
52+
53+
public void testLongestPrefixMatch() {
54+
Map.Entry<String, FileSystem<?>> result =
55+
SyncCommand.findMountPoint(mountPoints, "/home/user/docs");
56+
assertNotNull(result);
57+
assertEquals("/home/user", result.getKey());
58+
}
59+
60+
public void testNoMatchingMountPoint() {
61+
Map<String, FileSystem<?>> noRoot = new HashMap<String, FileSystem<?>>();
62+
noRoot.put("/home", new StubFileSystem("/home"));
63+
noRoot.put("/home/user", new StubFileSystem("/home/user"));
64+
Map.Entry<String, FileSystem<?>> result =
65+
SyncCommand.findMountPoint(noRoot, "/var/log");
66+
assertNull(result);
67+
}
68+
69+
public void testRootMatch() {
70+
Map.Entry<String, FileSystem<?>> result =
71+
SyncCommand.findMountPoint(mountPoints, "/var/log");
72+
assertNotNull(result);
73+
assertEquals("/", result.getKey());
74+
}
75+
76+
public void testExactRootMatch() {
77+
Map.Entry<String, FileSystem<?>> result =
78+
SyncCommand.findMountPoint(mountPoints, "/");
79+
assertNotNull(result);
80+
assertEquals("/", result.getKey());
81+
}
82+
83+
private static class StubFileSystem implements FileSystem<FSEntry> {
84+
private final String mp;
85+
86+
StubFileSystem(String mp) {
87+
this.mp = mp;
88+
}
89+
90+
public FileSystemType<? extends FileSystem<FSEntry>> getType() {
91+
return null;
92+
}
93+
94+
public Device getDevice() {
95+
return null;
96+
}
97+
98+
public FSEntry getRootEntry() throws IOException {
99+
return null;
100+
}
101+
102+
public boolean isReadOnly() {
103+
return false;
104+
}
105+
106+
public void close() throws IOException {
107+
}
108+
109+
public boolean isClosed() {
110+
return false;
111+
}
112+
113+
public long getTotalSpace() throws IOException {
114+
return 0;
115+
}
116+
117+
public long getFreeSpace() throws IOException {
118+
return 0;
119+
}
120+
121+
public long getUsableSpace() throws IOException {
122+
return 0;
123+
}
124+
125+
public String getVolumeName() throws IOException {
126+
return mp;
127+
}
128+
}
129+
}

0 commit comments

Comments
 (0)