options);
31 |
32 | /**
33 | * Unmount this object.
34 | */
35 | void unmount();
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/NotImplemented.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java;
2 |
3 | import java.lang.annotation.Documented;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | import static java.lang.annotation.ElementType.METHOD;
9 |
10 | /**
11 | * If a method is marked with this annotation it won't be registered in DokanOperations struct.
12 | *
13 | * This annotation is not inheritable, so all overridden method will be registered.
14 | *
15 | * The goal of this annotation is performance, if the method is not registered in the DokanOperations struct
16 | * then the native → java call will not be performed.
17 | *
18 | *
This class is a copy of ru.serce.jnrfuse.NotImplemented.
19 | *
20 | * @author Sergey Tselovalnikov
21 | * @since 2.0
22 | */
23 | @Documented
24 | @Retention(RetentionPolicy.RUNTIME)
25 | @Target(value = METHOD)
26 | public @interface NotImplemented {
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/UnmountFailedException.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java;
2 |
3 | public class UnmountFailedException extends RuntimeException {
4 |
5 | public UnmountFailedException(String msg) {
6 | super(msg);
7 | }
8 |
9 | public UnmountFailedException(String msg, Throwable cause) {
10 | super(msg, cause);
11 | }
12 |
13 | public UnmountFailedException(Throwable cause) {
14 | super(cause);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/Unsigned.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java;
2 |
3 |
4 | import java.lang.annotation.Documented;
5 | import java.lang.annotation.Retention;
6 | import java.lang.annotation.RetentionPolicy;
7 | import java.lang.annotation.Target;
8 |
9 | import static java.lang.annotation.ElementType.TYPE_PARAMETER;
10 | import static java.lang.annotation.ElementType.TYPE_USE;
11 |
12 |
13 | /**
14 | * This annotation is used used to indicate that a value with an integer type or an integer type pointer refers to an unsigned value.
15 | * In this case integers are numbers without positions after decimal point.
16 | * (Java's Default integer types being {@code byte, short, int, long} and their corresponding wrappers.)
17 | * Introduction
18 | * Java stores integer types in Two's complement-Representation.
19 | * Usually numbers represented as Two's complement use the Most-Significant-Bit (MSB) to store the sign of the number.
20 | * If a field is annotated with @Unsigned this rule does not apply!
21 | * Instead the MSB should be considered as part of the number itself, resulting in a doubled storage capacity of the field
22 | * while removing the support for signed values (the field becomes unsigned and the value should only be interpreted as positive.)
23 | * See this table as reference for the resulting differences when interpreting numbers:
24 | *
25 | *
26 | *
27 | * Signed value (Java Default) |
28 | * Unsigned values (@Unsigned) |
29 | * Bitmask (for a byte) |
30 | *
31 | *
32 | *
33 | *
34 | * 0 |
35 | * 0 |
36 | * 0000 0000 |
37 | *
38 | *
39 | * 1 |
40 | * 1 |
41 | * 0000 0001 |
42 | *
43 | *
44 | * 2 |
45 | * 2 |
46 | * 0000 0010 |
47 | *
48 | *
49 | * 64 |
50 | * 64 |
51 | * 0100 0000 |
52 | *
53 | *
54 | * -128 |
55 | * 128 |
56 | * 1000 0000 |
57 | *
58 | *
59 | * -127 |
60 | * 129 |
61 | * 1000 0001 |
62 | *
63 | *
64 | * -1 |
65 | * 255 |
66 | * 1111 1111 |
67 | *
68 | *
69 | *
70 | * Usage
71 | * As Java only supports signed numbers, the correct interpretation and handling of unsigned numbers must be dealt with by the developer.
72 | * Because of this it's strongly recommended to tag all values that are unsigned as such by using this annotation.
73 | * @Unsigned is defined to {@link Target target} {@link java.lang.annotation.ElementType#TYPE_PARAMETER} and
74 | * {@link java.lang.annotation.ElementType#TYPE_USE} and can therefore be applied to all usages of types (type contexts)
75 | * and parameterized types respectively. See §9.6.4.1
76 | * and §4.11 of "The Java® Language Specification" for
77 | * further reference.
78 | *
79 | * At least the following cases ("minimum usage") should be annotated with @Unsigned to
80 | * guarantee the best quality of code:
81 | *
82 | * - Field declarations: {@code @Unsigned private final int index;}
83 | *
- Local variable declarations: {@code @Unsigned int i;}
84 | *
- Parameters: {@code public void remove(@Unsigned int index) {...}}
85 | *
- Method return types: {@code public @Unsigned int getIndex() {...}}
86 | *
87 | * Additionally it's recommended to annotate any other usage of unsigned types ("advanced usage"),
88 | * especially (but not limited to):
89 | *
90 | * - Parameterized types: {@code List<@Unsigned Integer> indices = new ArrayList<>();}
91 | *
- Casts: {@code Integer i = (@Unsigned Integer) uInt;}
92 | *
- Type declarations: {@code public class @Unsigned UInt {...}}
93 | *
- Extension/Implementation of unsigned types: {@code public class //@Unsigned// Index extends @Unsigned UInt {...}}
94 | *
95 | * Note: Contributions to the dokan-java project must annotate all usages of unsigned types (Minimum usage and advanced usage)
96 | * with @Unsigned.
97 | * Pitfalls
98 | * If a field is annotated with @Unsigned developers should take extra care handling it as some unsafe operations may lead
99 | * to unexpected results.
100 | * An operation is considered safe if using it with unsigned values yields the same results as using it with signed numbers.
101 | * ("An operation is safe if it can be used the same way when dealing with unsigned values as one would use it with signed numbers.")
102 | * The following (inconclusive) table shows which operations are safe or unsafe and how operations can be dealt with alternatively.
103 | *
104 | *
105 | *
106 | * Operation |
107 | * Safe/Unsafe |
108 | * Recommended course of action |
109 | *
110 | *
111 | *
112 | *
113 | * Adding (+) |
114 | * Safe |
115 | * |
116 | *
117 | *
118 | * Subtracting (-) |
119 | * Safe |
120 | * |
121 | *
122 | *
123 | * Multiplication (*) |
124 | * Safe |
125 | * |
126 | *
127 | *
128 | * Division (/) |
129 | * Unsafe |
130 | * {@link UnsignedNumbers#divideUnsigned(int, int)} |
131 | *
132 | *
133 | * Remainder/Modulo |
134 | * Unsafe |
135 | * {@link UnsignedNumbers#remainderUnsigned(int, int)} |
136 | *
137 | *
138 | * Comparision (%) |
139 | * Unsafe |
140 | * {@link UnsignedNumbers#compareUnsigned(int, int)} |
141 | *
142 | *
143 | * Printing |
144 | * Unsafe |
145 | * {@link UnsignedNumbers#toUnsignedString(int)} |
146 | *
147 | *
148 | * Downcasting |
149 | * Safe |
150 | * {@link UnsignedNumbers#toUnsignedInt(long)} |
151 | *
152 | *
153 | * Upcasting |
154 | * Unsafe |
155 | * {@link UnsignedNumbers#toUnsignedInt(short)} |
156 | *
157 | *
158 | *
159 | * Note: Operations that require two numbers (e.g. addition, subtraction) should never be used with a signed
160 | * and an unsigned number as arguments, even if the operation is usually considered safe.
161 | * Doing so can result in heavy computational errors, as seen here:
162 | *
163 | * {@code //Don't do this:}
164 | * {@code byte a = 64; //0100 0000}
165 | * {@code @Unsigned byte b = 64; //0100 0000}
166 | * {@code byte c = a + b; //0100 0000 + 0100 0000}
167 | * {@code //--> c = -128; //1000 0000}
168 | *
169 | * This is a problem for the following reason: Before the addition the status (signed/unsigned) of
170 | * a and b didn't matter (in fact they were equal). As soon as the value exceeds 127 the developer needs to decide
171 | * whether c is signed or unsigned.
172 | * If c is unsigned all future users of c must take care that they
173 | * interpret it correctly. Also a comes from a signed context and could be negative.
174 | * If a's MSB is set to indicate a negative value any computation that considers a unsigned must be wrong
175 | * as it would interpret a as a big positive number instead of a negative number.
176 | * If c is interpreted as signed, the computation is plain wrong (64 + 64 is not -128) because it leads to a number-overflow.
177 | *
178 | * {@code //Instead do this:}
179 | * {@code byte a = 64;}
180 | * {@code @Unsigned byte b = 64;}
181 | * {@code short s = UnsignedNumbers.toUnsignedShort(b);}
182 | * {@code short c = s + a;}
183 | * {@code //--> c = 128;}
184 | *
185 | * @author JaniruTEC
186 | * @see UnsignedNumbers
187 | * @since 2.0
188 | */
189 | @Documented
190 | @Retention(RetentionPolicy.RUNTIME)
191 | //In Theory TYPE_USE should contain TYPE_PARAMETER,
192 | //but the documentation is so vague that I'm really not sure to be honest
193 | @Target(value = {TYPE_PARAMETER, TYPE_USE})
194 | public @interface Unsigned {
195 |
196 | }
197 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/UnsignedNumbers.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java;
2 |
3 |
4 | public class UnsignedNumbers {
5 |
6 | private static final short BYTE_MASK = 0xff;
7 | private static final int SHORT_MASK = 0xffff;
8 | private static final long INT_MASK = 0xffffff;
9 |
10 | public static String toUnsignedString(@Unsigned byte value) {
11 | return toUnsignedString(Byte.toUnsignedInt(value));
12 | }
13 |
14 | public static String toUnsignedString(@Unsigned short value) {
15 | return toUnsignedString(Short.toUnsignedInt(value));
16 | }
17 |
18 | public static String toUnsignedString(@Unsigned int value) {
19 | return Integer.toUnsignedString(value);
20 | }
21 |
22 | public static String toUnsignedString(@Unsigned long value) {
23 | return Long.toUnsignedString(value);
24 | }
25 |
26 | /**
27 | * Convenience method that stands in for the missing method {@code Byte#divideUnsigned(byte, byte)}.
28 | * This method was inspired by {@link Integer#divideUnsigned(int, int) the method of the same in Integer}
29 | *
30 | * Description copied from {@link Integer#divideUnsigned(int, int)}
31 | * Returns the unsigned quotient of dividing the first argument by
32 | * the second where each argument and the result is interpreted as
33 | * an unsigned value.
34 | *
35 | * Note that in two's complement arithmetic, the three other
36 | * basic arithmetic operations of add, subtract, and multiply are
37 | * bit-wise identical if the two operands are regarded as both
38 | * being signed or both being unsigned. Therefore separate {@code
39 | * addUnsigned}, etc. methods are not provided.
40 | *
41 | * @param dividend the value to be divided
42 | * @param divisor the value doing the dividing
43 | * @return the unsigned quotient of the first argument divided by
44 | * the second argument
45 | * @see #remainderUnsigned
46 | * @since 1.8
47 | *
48 | * @see Integer#divideUnsigned(int, int)
49 | */
50 | public static @Unsigned byte divideUnsigned(@Unsigned byte dividend, @Unsigned byte divisor) {
51 | return (byte) (Byte.toUnsignedInt(dividend) / Byte.toUnsignedInt(divisor));
52 | }
53 |
54 | /**
55 | * Convenience method that stands in for the missing method {@code Short#divideUnsigned(short, short)}.
56 | * This method was inspired by {@link Integer#divideUnsigned(int, int) the method of the same in Integer}
57 | *
58 | * Description copied from {@link Integer#divideUnsigned(int, int)}
59 | * Returns the unsigned quotient of dividing the first argument by
60 | * the second where each argument and the result is interpreted as
61 | * an unsigned value.
62 | *
63 | *
Note that in two's complement arithmetic, the three other
64 | * basic arithmetic operations of add, subtract, and multiply are
65 | * bit-wise identical if the two operands are regarded as both
66 | * being signed or both being unsigned. Therefore separate {@code
67 | * addUnsigned}, etc. methods are not provided.
68 | *
69 | * @param dividend the value to be divided
70 | * @param divisor the value doing the dividing
71 | * @return the unsigned quotient of the first argument divided by
72 | * the second argument
73 | * @see #remainderUnsigned
74 | * @since 1.8
75 | *
76 | * @see Integer#divideUnsigned(int, int)
77 | */
78 | public static @Unsigned short divideUnsigned(@Unsigned short dividend, @Unsigned short divisor) {
79 | return (short) (Short.toUnsignedInt(dividend) / Short.toUnsignedInt(divisor));
80 | }
81 |
82 | /**
83 | * Convenience method that delegates to {@link Integer#divideUnsigned(int, int)}.
84 | *
85 | * Description copied from {@link Integer#divideUnsigned(int, int)}
86 | * Returns the unsigned quotient of dividing the first argument by
87 | * the second where each argument and the result is interpreted as
88 | * an unsigned value.
89 | *
90 | *
Note that in two's complement arithmetic, the three other
91 | * basic arithmetic operations of add, subtract, and multiply are
92 | * bit-wise identical if the two operands are regarded as both
93 | * being signed or both being unsigned. Therefore separate {@code
94 | * addUnsigned}, etc. methods are not provided.
95 | *
96 | * @param dividend the value to be divided
97 | * @param divisor the value doing the dividing
98 | * @return the unsigned quotient of the first argument divided by
99 | * the second argument
100 | * @see #remainderUnsigned
101 | * @since 1.8
102 | *
103 | * @see Integer#divideUnsigned(int, int)
104 | */
105 | public static @Unsigned int divideUnsigned(@Unsigned int dividend, @Unsigned int divisor) {
106 | return Integer.divideUnsigned(dividend, divisor);
107 | }
108 |
109 | /**
110 | * Convenience method that delegates to {@link Long#divideUnsigned(long, long)}.
111 | *
112 | * Description copied from {@link Long#divideUnsigned(long, long)}
113 | * Returns the unsigned quotient of dividing the first argument by
114 | * the second where each argument and the result is interpreted as
115 | * an unsigned value.
116 | *
117 | *
Note that in two's complement arithmetic, the three other
118 | * basic arithmetic operations of add, subtract, and multiply are
119 | * bit-wise identical if the two operands are regarded as both
120 | * being signed or both being unsigned. Therefore separate {@code
121 | * addUnsigned}, etc. methods are not provided.
122 | *
123 | * @param dividend the value to be divided
124 | * @param divisor the value doing the dividing
125 | * @return the unsigned quotient of the first argument divided by
126 | * the second argument
127 | * @see #remainderUnsigned
128 | * @since 1.8
129 | *
130 | * @see Long#divideUnsigned(long, long)
131 | */
132 | public static @Unsigned long divideUnsigned(@Unsigned long dividend, @Unsigned long divisor) {
133 | return Long.divideUnsigned(dividend, divisor);
134 | }
135 |
136 | public static @Unsigned byte remainderUnsigned(@Unsigned byte dividend, @Unsigned byte divisor) {
137 | return (byte) (Byte.toUnsignedInt(dividend) % Byte.toUnsignedInt(divisor));
138 | }
139 |
140 | public static @Unsigned short remainderUnsigned(@Unsigned short dividend, @Unsigned short divisor) {
141 | return (short) (Short.toUnsignedInt(dividend) % Short.toUnsignedInt(divisor));
142 | }
143 |
144 | public static @Unsigned int remainderUnsigned(@Unsigned int dividend, @Unsigned int divisor) {
145 | return Integer.remainderUnsigned(dividend, divisor);
146 | }
147 |
148 | public static @Unsigned long remainderUnsigned(@Unsigned long dividend, @Unsigned long divisor) {
149 | return Long.remainderUnsigned(dividend, divisor);
150 | }
151 |
152 | public static int compareUnsigned(byte x, byte y) {
153 | return Byte.compareUnsigned(x, y);
154 | }
155 |
156 | public static int compareUnsigned(short x, short y) {
157 | return Short.compareUnsigned(x, y);
158 | }
159 |
160 | public static int compareUnsigned(int x, int y) {
161 | return Integer.compareUnsigned(x, y);
162 | }
163 |
164 | public static int compareUnsigned(long x, long y) {
165 | return Long.compareUnsigned(x, y);
166 | }
167 |
168 | /////////////////////////////////////////////////////////
169 | ////////////////////// Conversions //////////////////////
170 | /////////////////////////////////////////////////////////
171 |
172 | ////////////////////// byte //////////////////////
173 |
174 | public static @Unsigned byte toUnsignedByte(@Unsigned short value) {
175 | return (byte) value;
176 | }
177 |
178 | public static @Unsigned byte toUnsignedByte(@Unsigned int value) {
179 | return (byte) value;
180 | }
181 |
182 | public static @Unsigned byte toUnsignedByte(@Unsigned long value) {
183 | return (byte) value;
184 | }
185 |
186 | ////////////////////// short //////////////////////
187 |
188 | public static @Unsigned short toUnsignedShort(@Unsigned byte value) {
189 | return (short) (((short) value) & BYTE_MASK);
190 | }
191 |
192 | public static @Unsigned short toUnsignedShort(@Unsigned int value) {
193 | return (short) value;
194 | }
195 |
196 | public static @Unsigned short toUnsignedShort(@Unsigned long value) {
197 | return (short) value;
198 | }
199 |
200 | ////////////////////// int //////////////////////
201 |
202 | public static @Unsigned int toUnsignedInt(@Unsigned byte value) {
203 | return ((int) value) & BYTE_MASK;
204 | }
205 |
206 | public static @Unsigned int toUnsignedInt(@Unsigned short value) {
207 | return ((int) value) & SHORT_MASK;
208 | }
209 |
210 | public static @Unsigned int toUnsignedInt(@Unsigned long value) {
211 | return (int) value;
212 | }
213 |
214 | ////////////////////// long //////////////////////
215 |
216 | public static @Unsigned long toUnsignedLong(@Unsigned byte value) {
217 | return ((long) value) & BYTE_MASK;
218 | }
219 |
220 | public static @Unsigned long toUnsignedLong(@Unsigned short value) {
221 | return ((long) value) & SHORT_MASK;
222 | }
223 |
224 | public static @Unsigned long toUnsignedLong(@Unsigned int value) {
225 | return ((long) value) & INT_MASK;
226 | }
227 | }
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/Win32FindStreamData.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java;
2 |
3 | import java.util.Arrays;
4 | import java.util.List;
5 |
6 | import com.sun.jna.Structure;
7 |
8 | public class Win32FindStreamData extends Structure implements DokanOperations.Win32FindStreamDataInterface {
9 | public long length;
10 | //max path = 260 under windows
11 | public char[] cFileName = new char[260 + 36];
12 |
13 | @Override
14 | public void length(final long val) {
15 | length = val;
16 | }
17 |
18 | @Override
19 | public char[] cFileName() {
20 | return cFileName;
21 | }
22 |
23 | @Override
24 | protected List getFieldOrder() {
25 | return Arrays.asList(
26 | "length",
27 | "cFileName");
28 | }
29 | }
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/constants/dokany/MountError.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.constants.dokany;
2 |
3 | import dev.dokan.dokan_java.masking.EnumInteger;
4 |
5 | /**
6 | * Return values of com.dokan.java.NativeMethods#DokanMain(DokanOptions, DokanOperations)
7 | *
8 | * @see Dokany documentation
9 | */
10 | public enum MountError implements EnumInteger {
11 | SUCCESS(0, "Mount succeed."),
12 | MOUNT_ERROR(-1, "Mount error."),
13 | DRIVE_LETTER_ERROR(-2, "Mount failed: Bad drive letter."),
14 | DRIVER_INSTALL_ERROR(-3, "Mount failed: Can't install driver."),
15 | START_ERROR(-4, "Mount failed: Driver answer that something is wrong."),
16 | CANNOT_ASSIGN(-5, "Mount failed: Cannot assign a drive letter or mount point. Probably already used by another volume."),
17 | MOUNT_POINT_ERROR(-6, "Mount failed: Mount point is invalid."),
18 | VERSION_ERROR(-7, "Mount failed: Requested an incompatible version.");
19 |
20 | private final int intValue;
21 | private final String description;
22 |
23 | public static MountError fromInt(final int value) {
24 | return EnumInteger.enumFromInt(value, values());
25 | }
26 |
27 | MountError(final int intValue, final String description) {
28 | this.intValue = intValue;
29 | this.description = description;
30 | }
31 |
32 | @Override
33 | public int intValue() {
34 | return this.intValue;
35 | }
36 |
37 | public String getDescription() {
38 | return this.description;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/constants/dokany/MountOption.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.constants.dokany;
2 |
3 | import dev.dokan.dokan_java.masking.MaskValueSet;
4 | import dev.dokan.dokan_java.masking.MaskValueEnum;
5 | import dev.dokan.dokan_java.structure.DokanOptions;
6 |
7 | /**
8 | * Enumeration of features that can be enabled for a mount. Part of {@link DokanOptions}
9 | *
10 | * @see Dokany documentation
11 | */
12 | public enum MountOption implements MaskValueEnum {
13 | DEBUG_MODE(1, "Enable output debug message."),
14 | STD_ERR_OUTPUT(2, "Enable output debug message to stderr."),
15 | ALT_STREAM(4, "Use alternate stream."),
16 | WRITE_PROTECTION(8, "Enable mount drive as write-protected."),
17 | NETWORK_DRIVE(16, "Use network drive - Dokan network provider need to be installed."),
18 | REMOVABLE(32, "Use removable drive."),
19 | MOUNT_MANAGER(64, "Use mount manager."),
20 | CURRENT_SESSION(128, "Mount the drive on current session only."),
21 | FILELOCK_USER_MODE(256, "Enable Lockfile/Unlockfile operations. Otherwise Dokan will take care of it.");
22 |
23 | private final int maskingValue;
24 | private final String description;
25 |
26 | MountOption(final int maskingValue, final String desc) {
27 | this.maskingValue = maskingValue;
28 | this.description = desc;
29 | }
30 |
31 | public static MaskValueSet maskValueSet(final int mask) {
32 | return MaskValueSet.maskValueSet(mask, values());
33 | }
34 |
35 | @Override
36 | public int intValue() {
37 | return this.maskingValue;
38 | }
39 |
40 | public String getDescription() {
41 | return this.description;
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/constants/microsoft/CreateDisposition.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.constants.microsoft;
2 |
3 | import dev.dokan.dokan_java.DokanNativeMethods;
4 | import dev.dokan.dokan_java.DokanOperations;
5 | import dev.dokan.dokan_java.masking.EnumInteger;
6 | import com.sun.jna.ptr.IntByReference;
7 |
8 | /**
9 | * Enum of possible actions to perform on a file when the function {@link DokanOperations#ZwCreateFile} is called.
10 | *
11 | *
12 | * Attention!
13 | * The members of this enum are the kernel flags, not to be confused with the user flags in {@link CreationDisposition}. To convert them, use {@link DokanNativeMethods#DokanMapKernelToUserCreateFileFlags(long, long, long, long, IntByReference, IntByReference, IntByReference)}. The relation between this two is also
14 | * descriped under this link.
15 | *
16 | *
17 | *
18 | * The following table shows the actions performed on a file for the given flag distinguishing if the file already exists or not:
19 | *
20 | *
21 | *
22 | * additional options
23 | *
24 | *
25 | * Option |
26 | * Action if file exists |
27 | * Action if file does not exist |
28 | *
29 | *
30 | *
31 | *
32 | * FILE_SUPERSEDE |
33 | * Replace the file. |
34 | * Create the file. |
35 | *
36 | *
37 | * FILE_CREATE |
38 | * Return an error. |
39 | * Create the file. |
40 | *
41 | *
42 | * FILE_OPEN |
43 | * Open the file. |
44 | * Return an error. |
45 | *
46 | *
47 | * FILE_OPEN_IF |
48 | * Open the file. |
49 | * Create the file. |
50 | *
51 | *
52 | * FILE_OVERWRITE |
53 | * Open the file, and overwrite it. |
54 | * Return an error. |
55 | *
56 | *
57 | * FILE_OVERWRITE_IF |
58 | * Open the file, and overwrite it. |
59 | * Create the file. |
60 | *
61 | *
62 | *
63 | *
64 | *
65 | * @see Microsoft documentation of ZwCreateFile
66 | * @see Dokany documentation of ZwCreateFile
67 | */
68 | public enum CreateDisposition implements EnumInteger {
69 |
70 | FILE_SUPERSEDE(CreateDispositions.FILE_SUPERSEDE),
71 | FILE_CREATE(CreateDispositions.FILE_CREATE),
72 | FILE_OPEN(CreateDispositions.FILE_OPEN),
73 | FILE_OPEN_IF(CreateDispositions.FILE_OPEN_IF),
74 | FILE_OVERWRITE(CreateDispositions.FILE_OVERWRITE),
75 | FILE_OVERWRITE_IF(CreateDispositions.FILE_OVERWRITE_IF);
76 |
77 | private final int intValue;
78 |
79 | CreateDisposition(int intValue) {
80 | this.intValue = intValue;
81 | }
82 |
83 | public static CreateDisposition fromInt(final int value) {
84 | return EnumInteger.enumFromInt(value, values());
85 | }
86 |
87 | @Override
88 | public int intValue() {
89 | return this.intValue;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/constants/microsoft/CreateDispositions.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.constants.microsoft;
2 |
3 | public final class CreateDispositions {
4 |
5 | public final static int FILE_SUPERSEDE = 0;
6 | public final static int FILE_OPEN = 1;
7 | public final static int FILE_CREATE = 2;
8 | public final static int FILE_OPEN_IF = 3;
9 | public final static int FILE_OVERWRITE = 4;
10 | public final static int FILE_OVERWRITE_IF = 5;
11 |
12 | private CreateDispositions() {
13 |
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/constants/microsoft/CreateOption.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.constants.microsoft;
2 |
3 | import dev.dokan.dokan_java.masking.MaskValueSet;
4 | import dev.dokan.dokan_java.masking.MaskValueEnum;
5 |
6 | /**
7 | * Enum of flags specifying the options to apply when the driver creates or opens the file.
8 | *
9 | * @see Microsofts documentation of ZwCreateFile
10 | * @see Dokany documentation of ZwCreateFile
11 | */
12 | public enum CreateOption implements MaskValueEnum {
13 | FILE_DIRECTORY_FILE(CreateOptions.FILE_DIRECTORY_FILE),
14 | FILE_WRITE_THROUGH(CreateOptions.FILE_WRITE_THROUGH),
15 | FILE_SEQUENTIAL_ONLY(CreateOptions.FILE_SEQUENTIAL_ONLY),
16 | FILE_NO_INTERMEDIATE_BUFFERING(CreateOptions.FILE_NO_INTERMEDIATE_BUFFERING),
17 | FILE_SYNCHRONOUS_IO_ALERT(CreateOptions.FILE_SYNCHRONOUS_IO_ALERT),
18 | FILE_SYNCHRONOUS_IO_NONALERT(CreateOptions.FILE_SYNCHRONOUS_IO_NONALERT),
19 | FILE_NON_DIRECTORY_FILE(CreateOptions.FILE_NON_DIRECTORY_FILE),
20 | FILE_CREATE_TREE_CONNECTION(CreateOptions.FILE_CREATE_TREE_CONNECTION),
21 | FILE_COMPLETE_IF_OPLOCKED(CreateOptions.FILE_COMPLETE_IF_OPLOCKED),
22 | FILE_NO_EA_KNOWLEDGE(CreateOptions.FILE_NO_EA_KNOWLEDGE),
23 | FILE_OPEN_REMOTE_INSTANCE(CreateOptions.FILE_OPEN_REMOTE_INSTANCE),
24 | FILE_RANDOM_ACCESS(CreateOptions.FILE_RANDOM_ACCESS),
25 | FILE_OPEN_REPARSE_POINT(CreateOptions.FILE_OPEN_REPARSE_POINT),
26 | FILE_DELETE_ON_CLOSE(CreateOptions.FILE_DELETE_ON_CLOSE),
27 | FILE_OPEN_BY_FILE_ID(CreateOptions.FILE_OPEN_BY_FILE_ID),
28 | FILE_OPEN_FOR_BACKUP_INTENT(CreateOptions.FILE_OPEN_FOR_BACKUP_INTENT),
29 | FILE_NO_COMPRESSION(CreateOptions.FILE_NO_COMPRESSION),
30 | FILE_SESSION_AWARE(CreateOptions.FILE_SESSION_AWARE);
31 |
32 | private final int maskingValue;
33 |
34 | CreateOption(final int maskingValue) {
35 | this.maskingValue = maskingValue;
36 | }
37 |
38 | public static MaskValueSet maskValueSet(final int mask) {
39 | return MaskValueSet.maskValueSet(mask, values());
40 | }
41 |
42 | @Override
43 | public int intValue() {
44 | return this.maskingValue;
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/constants/microsoft/CreateOptions.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.constants.microsoft;
2 |
3 | public final class CreateOptions {
4 |
5 | public static final int FILE_DIRECTORY_FILE = 0x00000001;
6 | public static final int FILE_WRITE_THROUGH = 0x00000002;
7 | public static final int FILE_SEQUENTIAL_ONLY = 0x00000004;
8 | public static final int FILE_NO_INTERMEDIATE_BUFFERING = 0x00000008;
9 | public static final int FILE_SYNCHRONOUS_IO_ALERT = 0x00000010;
10 | public static final int FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020;
11 | public static final int FILE_NON_DIRECTORY_FILE = 0x00000040;
12 | public static final int FILE_CREATE_TREE_CONNECTION = 0x00000080;
13 | public static final int FILE_COMPLETE_IF_OPLOCKED = 0x00000100;
14 | public static final int FILE_NO_EA_KNOWLEDGE = 0x00000200;
15 | public static final int FILE_OPEN_REMOTE_INSTANCE = 0x00000400;
16 | public static final int FILE_RANDOM_ACCESS = 0x00000800;
17 | public static final int FILE_OPEN_REPARSE_POINT = 0x00200000;
18 | public static final int FILE_DELETE_ON_CLOSE = 0x00001000;
19 | public static final int FILE_OPEN_BY_FILE_ID = 0x00002000;
20 | public static final int FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000;
21 | public static final int FILE_NO_COMPRESSION = 0x00008000;
22 | public static final int FILE_SESSION_AWARE = 0x00040000;
23 |
24 | private CreateOptions() {
25 |
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/constants/microsoft/CreationDisposition.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.constants.microsoft;
2 |
3 | import com.sun.jna.platform.win32.WinNT;
4 | import dev.dokan.dokan_java.DokanNativeMethods;
5 | import dev.dokan.dokan_java.masking.EnumInteger;
6 |
7 | /**
8 | * Enum of actions to take on a not-necessarily existing file or device.
9 | * These values are the userspace equivalent of {@link CreateDisposition}.
10 | * For a given CreateDisposition value the corresponding CreationDisposition value can be computed via the {@link DokanNativeMethods#DokanMapKernelToUserCreateFileFlags(long, long, long, long,
11 | * IntByReference, IntByReference, IntByReference)}
12 | *
13 | *
14 | *
15 | * The following table shows the actions performed on a file for the given flag distinguishing if the file already exists or not:
16 | *
17 | *
18 | *
19 | * additional options
20 | *
21 | *
22 | * Option |
23 | * Action if file exists |
24 | * Action if file does not exist |
25 | *
26 | *
27 | *
28 | *
29 | * CREATE_ALWAYS |
30 | * Overwrite the file and return {@link Win32ErrorCodes#ERROR_ALREADY_EXISTS}. |
31 | * Create the file. |
32 | *
33 | *
34 | * CREATE_NEW |
35 | * Fail and return {@link Win32ErrorCodes#ERROR_ALREADY_EXISTS}. |
36 | * Create the file. |
37 | *
38 | *
39 | * OPEN_ALWAYS |
40 | * Open the file and return {@link Win32ErrorCodes#ERROR_ALREADY_EXISTS}. |
41 | * Create the file. |
42 | *
43 | *
44 | * OPEN_EXISTING |
45 | * Open the file. |
46 | * Fail and return {@link Win32ErrorCodes#ERROR_FILE_NOT_FOUND}. |
47 | *
48 | *
49 | * TRUNCATE_EXISTING |
50 | * Truncate the file, and overwrite it. |
51 | * Fail. |
52 | *
53 | *
54 | *
55 | *
56 | * @see The Microsoft Documentation of CreateFile
57 | * @see StackOverFlow Answer summing up the above table.
58 | */
59 | public enum CreationDisposition implements EnumInteger {
60 | CREATE_NEW(WinNT.CREATE_NEW),
61 | CREATE_ALWAYS(WinNT.CREATE_ALWAYS),
62 | OPEN_EXISTING(WinNT.OPEN_EXISTING),
63 | OPEN_ALWAYS(WinNT.OPEN_ALWAYS),
64 | TRUNCATE_EXISTING(WinNT.TRUNCATE_EXISTING);
65 |
66 | private final int intValue;
67 |
68 | CreationDisposition(final int intValue) {
69 | this.intValue = intValue;
70 | }
71 |
72 | public static CreationDisposition fromInt(final int value) {
73 | return EnumInteger.enumFromInt(value, values());
74 | }
75 |
76 | @Override
77 | public int intValue() {
78 | return this.intValue;
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/constants/microsoft/FileAttribute.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.constants.microsoft;
2 |
3 |
4 | import com.sun.jna.platform.win32.WinNT;
5 | import dev.dokan.dokan_java.masking.MaskValueSet;
6 | import dev.dokan.dokan_java.masking.MaskValueEnum;
7 |
8 |
9 | /**
10 | * File attribute flags. They are metadata values stored on the disk by the filesystem to be used by the system.
11 | *
12 | * @see Microsoft documentation of file attribute constants
13 | * @see Microsoft documentation of CreateFileA function including the list of valid file attributes
14 | */
15 | public enum FileAttribute implements MaskValueEnum {
16 | ARCHIVE(WinNT.FILE_ATTRIBUTE_ARCHIVE),
17 | COMPRESSED(WinNT.FILE_ATTRIBUTE_COMPRESSED),
18 | DEVICE(WinNT.FILE_ATTRIBUTE_DEVICE),
19 | DIRECTORY(WinNT.FILE_ATTRIBUTE_DIRECTORY),
20 | ENCRYPTED(WinNT.FILE_ATTRIBUTE_ENCRYPTED),
21 | HIDDEN(WinNT.FILE_ATTRIBUTE_HIDDEN),
22 | INTEGRITY_STREAM(32768),
23 | NORMAL(WinNT.FILE_ATTRIBUTE_NORMAL),
24 | NOT_CONTENT_INDEXED(WinNT.FILE_ATTRIBUTE_NOT_CONTENT_INDEXED),
25 | NO_SCRUB_DATA(131072),
26 | OFFLINE(WinNT.FILE_ATTRIBUTE_OFFLINE),
27 | READONLY(WinNT.FILE_ATTRIBUTE_READONLY),
28 | REPARSE_POINT(WinNT.FILE_ATTRIBUTE_REPARSE_POINT),
29 | SPARSE_FILE(WinNT.FILE_ATTRIBUTE_SPARSE_FILE),
30 | SYSTEM(WinNT.FILE_ATTRIBUTE_SYSTEM),
31 | TEMPORARY(WinNT.FILE_ATTRIBUTE_TEMPORARY),
32 | VIRTUAL(WinNT.FILE_ATTRIBUTE_VIRTUAL),
33 | RECALL_ON_DATA_ACCESS(4194394),
34 | RECALL_ON_OPEN(262144);
35 |
36 | private final int maskingValue;
37 |
38 | public static MaskValueSet maskValueSet(final int mask) {
39 | return MaskValueSet.maskValueSet(mask, values());
40 | }
41 |
42 | FileAttribute(final int maskingValue) {
43 | this.maskingValue = maskingValue;
44 | }
45 |
46 | @Override
47 | public int intValue() {
48 | return this.maskingValue;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/constants/microsoft/FileSystemFlag.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.constants.microsoft;
2 |
3 | import com.sun.jna.platform.win32.WinNT;
4 | import dev.dokan.dokan_java.DokanOperations;
5 | import dev.dokan.dokan_java.masking.MaskValueSet;
6 | import dev.dokan.dokan_java.masking.MaskValueEnum;
7 |
8 | /**
9 | * Properties which the implemented filesystem supports.
10 | *
11 | *
12 | * Returned in {@link DokanOperations#GetVolumeInformation} to the kernel layer indicating what properties your file system implementation supports.
13 | *
14 | *
15 | * They can be arbitrary combined within an {@link MaskValueSet}. However FILE_FILE_COMPRESSION and FILE_VOL_IS_COMPRESSED are mutually exclusive.
16 | *
17 | * @see Microsoft Documentation of function GetVolumeInformation, Parameter {@code lpFileSystemFlags}
18 | * @see Listing of possible values
19 | */
20 | public enum FileSystemFlag implements MaskValueEnum {
21 | NONE(0),
22 | CASE_PRESERVED_NAMES(WinNT.FILE_CASE_PRESERVED_NAMES),
23 | CASE_SENSITIVE_SEARCH(WinNT.FILE_CASE_SENSITIVE_SEARCH),
24 | DAX_VOLUME(0x20000000),
25 | FILE_COMPRESSION(WinNT.FILE_FILE_COMPRESSION),
26 | NAMED_STREAMS(WinNT.FILE_NAMED_STREAMS),
27 | PERSISTENT_ACLS(WinNT.FILE_PERSISTENT_ACLS),
28 | READ_ONLY_VOLUME(WinNT.FILE_READ_ONLY_VOLUME),
29 | SEQUENTIAL_WRITE_ONCE(WinNT.FILE_SEQUENTIAL_WRITE_ONCE),
30 | SUPPORTS_ENCRYPTION(WinNT.FILE_SUPPORTS_ENCRYPTION),
31 | SUPPORTS_EXTENDED_ATTRIBUTES(WinNT.FILE_SUPPORTS_EXTENDED_ATTRIBUTES),
32 | SUPPORTS_HARD_LINKS(WinNT.FILE_SUPPORTS_HARD_LINKS),
33 | SUPPORTS_OBJECT_IDS(WinNT.FILE_SUPPORTS_OBJECT_IDS),
34 | SUPPORTS_OPEN_BY_FILE_ID(WinNT.FILE_SUPPORTS_OPEN_BY_FILE_ID),
35 | SUPPORTS_REPARSE_POINTS(WinNT.FILE_SUPPORTS_REPARSE_POINTS),
36 | SUPPORTS_SPARSE_FILES(WinNT.FILE_SUPPORTS_SPARSE_FILES),
37 | SUPPORTS_TRANSACTIONS(WinNT.FILE_SUPPORTS_TRANSACTIONS),
38 | SUPPORTS_USN_JOURNAL(WinNT.FILE_SUPPORTS_USN_JOURNAL),
39 | UNICODE_ON_DISK(WinNT.FILE_UNICODE_ON_DISK),
40 | SUPPORTS_REMOTE_STORAGE(WinNT.FILE_SUPPORTS_REMOTE_STORAGE),
41 | VOLUME_IS_COMPRESSED(WinNT.FILE_VOLUME_IS_COMPRESSED),
42 | VOLUME_QUOTAS(WinNT.FILE_VOLUME_QUOTAS);
43 |
44 | private final int maskingValue;
45 |
46 | public static MaskValueSet maskValueSet(final int mask) {
47 | return MaskValueSet.maskValueSet(mask, values());
48 | }
49 |
50 | FileSystemFlag(final int maskingValue) {
51 | this.maskingValue = maskingValue;
52 | }
53 |
54 | @Override
55 | public int intValue() {
56 | return this.maskingValue;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/constants/microsoft/accessmaskflags/BasicAccessMaskFlag.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.constants.microsoft.accessmaskflags;
2 |
3 | import com.sun.jna.platform.win32.WinNT;
4 | import dev.dokan.dokan_java.masking.MaskValueEnum;
5 | import dev.dokan.dokan_java.masking.MaskValueSet;
6 |
7 | /**
8 | * Enumeration of the possible AccessMask options.
9 | * For more info see the Microsoft Developer Documentation or the normal documentation.
10 | */
11 | public enum BasicAccessMaskFlag implements MaskValueEnum {
12 | /**
13 | * GENERIC_READ
14 | * When used in an Access Request operation: When read access to an object is requested, this bit is translated to a combination of bits. These are most often set in the lower 16 bits of the ACCESS_MASK. (Individual protocol specifications MAY specify a different configuration.) The bits that are set are implementation dependent. During this translation, the GENERIC_READ bit is cleared. The resulting ACCESS_MASK bits are the actual permissions that are checked against the ACE structures in the security descriptor that attached to the object.
15 | *
16 | * When used to set the Security Descriptor on an object: When the GENERIC_READ bit is set in an ACE that is to be attached to an object, it is translated into a combination of bits, which are usually set in the lower 16 bits of the ACCESS_MASK. (Individual protocol specifications MAY specify a different configuration.) The bits that are set are implementation dependent. During this translation, the GENERIC_READ bit is cleared. The resulting ACCESS_MASK bits are the actual permissions that are granted by this ACE.
17 | */
18 | GENERIC_READ(WinNT.GENERIC_READ),
19 |
20 |
21 | /**
22 | * GENERIC_WRITE
23 | * When used in an Access Request operation: When write access to an object is requested, this bit is translated to a combination of bits, which are usually set in the lower 16 bits of the ACCESS_MASK. (Individual protocol specifications MAY specify a different configuration.) The bits that are set are implementation dependent. During this translation, the GENERIC_WRITE bit is cleared. The resulting ACCESS_MASK bits are the actual permissions that are checked against the ACE structures in the security descriptor that attached to the object.
24 | *
25 | * When used to set the Security Descriptor on an object: When the GENERIC_WRITE bit is set in an ACE that is to be attached to an object, it is translated into a combination of bits, which are usually set in the lower 16 bits of the ACCESS_MASK. (Individual protocol specifications MAY specify a different configuration.) The bits that are set are implementation dependent. During this translation, the GENERIC_WRITE bit is cleared. The resulting ACCESS_MASK bits are the actual permissions that are granted by this ACE.
26 | */
27 | GENERIC_WRITE(WinNT.GENERIC_WRITE),
28 |
29 |
30 | /**
31 | * GENERIC_EXECUTE
32 | *
33 | * When used in an Access Request operation: When execute access to an object is requested, this bit is translated to a combination of bits, which are usually set in the lower 16 bits of the ACCESS_MASK. (Individual protocol specifications MAY specify a different configuration.) The bits that are set are implementation dependent. During this translation, the GENERIC_EXECUTE bit is cleared. The resulting ACCESS_MASK bits are the actual permissions that are checked against the ACE structures in the security descriptor that attached to the object.
34 | *
35 | * When used to set the Security Descriptor on an object: When the GENERIC_EXECUTE bit is set in an ACE that is to be attached to an object, it is translated into a combination of bits, which are usually set in the lower 16 bits of the ACCESS_MASK. (Individual protocol specifications MAY specify a different configuration.) The bits that are set are implementation dependent. During this translation, the GENERIC_EXECUTE bit is cleared. The resulting ACCESS_MASK bits are the actual permissions that are granted by this ACE.
36 | */
37 | GENERIC_EXECUTE(WinNT.GENERIC_EXECUTE),
38 |
39 |
40 | /**
41 | * GENERIC_ALL
42 | * When used in an Access Request operation: When all access permissions to an object are requested, this bit is translated to a combination of bits, which are usually set in the lower 16 bits of the ACCESS_MASK. (Individual protocol specifications MAY specify a different configuration.) Objects are free to include bits from the upper 16 bits in that translation as required by the objects semantics. The bits that are set are implementation dependent. During this translation, the GENERIC_ALL bit is cleared. The resulting ACCESS_MASK bits are the actual permissions that are checked against the ACE structures in the security descriptor that attached to the object.
43 | *
44 | * When used to set the Security Descriptor on an object: When the GENERIC_ALL bit is set in an ACE that is to be attached to an object, it is translated into a combination of bits, which are usually set in the lower 16 bits of the ACCESS_MASK. (Individual protocol specifications MAY specify a different configuration.) Objects are free to include bits from the upper 16 bits in that translation, if required by the objects semantics. The bits that are set are implementation dependent. During this translation, the GENERIC_ALL bit is cleared. The resulting ACCESS_MASK bits are the actual permissions that are granted by this ACE.
45 | */
46 | GENERIC_ALL(WinNT.GENERIC_ALL),
47 |
48 |
49 | /**
50 | * MAXIMUM_ALLOWED
51 | *
52 | * When used in an Access Request operation: When requested, this bit grants the requestor the maximum permissions allowed to the object through the Access Check Algorithm. This bit can only be requested; it cannot be set in an ACE.
53 | *
54 | * When used to set the Security Descriptor on an object: Specifying the Maximum Allowed bit in the SECURITY_DESCRIPTOR has no meaning. The MAXIMUM_ALLOWED bit SHOULD NOT be set and SHOULD be ignored when part of a SECURITY_DESCRIPTOR structure.
55 | */
56 | MAXIMUM_ALLOWED(0x02000000L),
57 |
58 |
59 | /**
60 | * ACCESS_SYSTEM_SECURITY
61 | * When used in an Access Request operation: When requested, this bit grants the requestor the right to change the SACL of an object. This bit MUST NOT be set in an ACE that is part of a DACL. When set in an ACE that is part of a SACL, this bit controls auditing of accesses to the SACL itself.
62 | */
63 | ACCESS_SYSTEM_SECURITY(WinNT.ACCESS_SYSTEM_SECURITY),
64 |
65 |
66 | /**
67 | * SYNCHRONIZE
68 | *
69 | * Specifies access to the object sufficient to synchronize or wait on the object.
70 | */
71 | SYNCHRONIZE(WinNT.SYNCHRONIZE),
72 |
73 |
74 | /**
75 | * WRITE_OWNER
76 | * Specifies access to change the owner of the object as listed in the security descriptor.
77 | */
78 | WRITE_OWNER(WinNT.WRITE_OWNER),
79 |
80 |
81 | /**
82 | * WRITE_DAC
83 | * Specifies access to change the discretionary access control list of the security descriptor of an object.
84 | */
85 | WRITE_DAC(WinNT.WRITE_DAC),
86 |
87 |
88 | /**
89 | * READ_CONTROL
90 | * Specifies access to read the security descriptor of an object.
91 | */
92 | READ_CONTROL(WinNT.READ_CONTROL),
93 |
94 |
95 | /**
96 | * DELETE
97 | * Specifies access to delete an object.
98 | */
99 | DELETE(WinNT.DELETE);
100 |
101 | private int maskingValue;
102 |
103 | BasicAccessMaskFlag(long maskingValue) {
104 | this.maskingValue = (int) maskingValue;
105 | }
106 |
107 | public static MaskValueSet maskValueSet(final int mask) {
108 | return MaskValueSet.maskValueSet(mask, values());
109 | }
110 |
111 | @Override
112 | public int intValue() {
113 | return this.maskingValue;
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/constants/microsoft/accessmaskflags/DirectoryAccessMaskFlag.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.constants.microsoft.accessmaskflags;
2 |
3 | import com.sun.jna.platform.win32.WinNT;
4 | import dev.dokan.dokan_java.masking.MaskValueEnum;
5 | import dev.dokan.dokan_java.masking.MaskValueSet;
6 |
7 | /**
8 | * Additional {@link BasicAccessMaskFlag} values specific to directories.
9 | *
10 | * @see Microsoft documentation of ZwCreateFile, Section Parameters, Parameter {@code DesiredAccess}
11 | */
12 | public enum DirectoryAccessMaskFlag implements MaskValueEnum {
13 | LIST_DIRECTORY(WinNT.FILE_LIST_DIRECTORY),
14 | TRAVERSE(WinNT.FILE_TRAVERSE);
15 |
16 | private final int maskingValue;
17 |
18 | DirectoryAccessMaskFlag(int maskingValue) {
19 | this.maskingValue = maskingValue;
20 | }
21 |
22 | public static MaskValueSet maskValueSet(final int mask) {
23 | return MaskValueSet.maskValueSet(mask, values());
24 | }
25 |
26 | @Override
27 | public int intValue() {
28 | return this.maskingValue;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/constants/microsoft/accessmaskflags/FileAccessMaskFlag.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.constants.microsoft.accessmaskflags;
2 |
3 | import com.sun.jna.platform.win32.WinNT;
4 | import dev.dokan.dokan_java.masking.MaskValueEnum;
5 | import dev.dokan.dokan_java.masking.MaskValueSet;
6 |
7 | /**
8 | * Additional {@link BasicAccessMaskFlag} values specific to files.
9 | *
10 | * @see Microsoft documentation of ZwCreateFile, Section Parameters, Parameter {@code DesiredAccess}
11 | */
12 | public enum FileAccessMaskFlag implements MaskValueEnum {
13 | READ_DATA(WinNT.FILE_READ_DATA),
14 | READ_ATTRIBUTES(WinNT.FILE_READ_ATTRIBUTES),
15 | READ_EA(WinNT.FILE_READ_EA),
16 | WRITE_DATA(WinNT.FILE_WRITE_DATA),
17 | WRITE_ATTRIBUTES(WinNT.FILE_WRITE_ATTRIBUTES),
18 | WRITE_EA(WinNT.FILE_WRITE_EA),
19 | APPEND_DATA(WinNT.FILE_APPEND_DATA),
20 | EXECUTE(WinNT.FILE_EXECUTE);
21 |
22 | private final int maskingValue;
23 |
24 | FileAccessMaskFlag(int maskingValue) {
25 | this.maskingValue = maskingValue;
26 | }
27 |
28 | public static MaskValueSet maskValueSet(final int mask) {
29 | return MaskValueSet.maskValueSet(mask, values());
30 | }
31 |
32 | @Override
33 | public int intValue() {
34 | return this.maskingValue;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/constants/microsoft/filesecurity/AccessControlEntryFlag.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.constants.microsoft.filesecurity;
2 |
3 | import dev.dokan.dokan_java.masking.MaskValueEnum;
4 | import dev.dokan.dokan_java.masking.MaskValueSet;
5 |
6 | /**
7 | * Enumeration of the different ACE control flags.
8 | *
9 | * From the Microsoft documentation: An unsigned 8-bit integer that specifies a set of ACE type-specific control flags. This field can be a combination of the following values.
10 | */
11 | public enum AccessControlEntryFlag implements MaskValueEnum {
12 |
13 | /**
14 | * Child objects that are containers, such as directories, inherit the ACE as an effective ACE. The inherited ACE is inheritable unless the NO_PROPAGATE_INHERIT_ACE bit flag is also set.
15 | */
16 | CONTAINER_INHERIT_ACE(0x02),
17 |
18 |
19 | /**
20 | * Used with system-audit ACEs in a system access control list (SACL) to generate audit messages for failed access attempts.
21 | */
22 | FAILED_ACCESS_ACE_FLAG(0x80),
23 |
24 |
25 | /**
26 | * Indicates an inherit-only ACE, which does not control access to the object to which it is attached. If this flag is not set, the ACE is an effective ACE that controls access to the object to which it is attached.
27 | * Both effective and inherit-only ACEs can be inherited depending on the state of the other inheritance flags.
28 | */
29 | INHERIT_ONLY_ACE(0x08),
30 |
31 |
32 | /**
33 | * Indicates that the ACE was inherited. The system sets this bit when it propagates an inherited ACE to a child object.
34 | */
35 | INHERITED_ACE(0x10),
36 |
37 |
38 | /**
39 | * If the ACE is inherited by a child object, the system clears the OBJECT_INHERIT_ACE and CONTAINER_INHERIT_ACE flags in the inherited ACE. This prevents the ACE from being inherited by subsequent generations of objects.
40 | */
41 | NO_PROPAGATE_INHERIT_ACE(0x04),
42 |
43 |
44 | /**
45 | * Noncontainer child objects inherit the ACE as an effective ACE.
46 | * For child objects that are containers, the ACE is inherited as an inherit-only ACE unless the NO_PROPAGATE_INHERIT_ACE bit flag is also set.
47 | */
48 | OBJECT_INHERIT_ACE(0x01),
49 |
50 |
51 | /**
52 | * Used with system-audit ACEs in a SACL to generate audit messages for successful access attempts.
53 | */
54 | SUCCESSFUL_ACCESS_ACE_FLAG(0x40);
55 |
56 |
57 | private final int maskingValue;
58 |
59 | AccessControlEntryFlag(int maskingValue) {
60 | this.maskingValue = maskingValue;
61 | }
62 |
63 | public static MaskValueSet maskValueSet(final int mask) {
64 | return MaskValueSet.maskValueSet(mask, values());
65 | }
66 |
67 | @Override
68 | public int intValue() {
69 | return this.maskingValue;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/constants/microsoft/filesecurity/AccessControlEntryType.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.constants.microsoft.filesecurity;
2 |
3 | import dev.dokan.dokan_java.Byteable;
4 |
5 | /**
6 | * Enumeration of possible ACE-Types. See also in the Microsoft documentation.
7 | */
8 | public enum AccessControlEntryType implements Byteable {
9 | /**
10 | * Access-allowed ACE that uses the ACCESS_ALLOWED_ACE (section 2.4.4.2) structure.
11 | */
12 | ACCESS_ALLOWED_ACE_TYPE((byte) 0x00),
13 |
14 | /**
15 | * Access-denied ACE that uses the ACCESS_DENIED_ACE (section 2.4.4.4) structure.
16 | */
17 | ACCESS_DENIED_ACE_TYPE((byte) 0x01),
18 |
19 | /**
20 | * System-audit ACE that uses the SYSTEM_AUDIT_ACE (section 2.4.4.10) structure.
21 | */
22 | SYSTEM_AUDIT_ACE_TYPE((byte) 0x02),
23 |
24 | /**
25 | * Reserved for future use.
26 | */
27 | SYSTEM_ALARM_ACE_TYPE((byte) 0x03),
28 |
29 | /**
30 | * Reserved for future use.
31 | */
32 | ACCESS_ALLOWED_COMPOUND_ACE_TYPE((byte) 0x04),
33 |
34 | /**
35 | * Object-specific access-allowed ACE that uses the ACCESS_ALLOWED_OBJECT_ACE (section 2.4.4.3) structure.
36 | */
37 | ACCESS_ALLOWED_OBJECT_ACE_TYPE((byte) 0x05),
38 |
39 | /**
40 | * Object-specific access-denied ACE that uses the ACCESS_DENIED_OBJECT_ACE (section 2.4.4.5) structure.
41 | */
42 | ACCESS_DENIED_OBJECT_ACE_TYPE((byte) 0x06),
43 |
44 | /**
45 | * Object-specific system-audit ACE that uses the SYSTEM_AUDIT_OBJECT_ACE (section 2.4.4.11) structure.
46 | */
47 | SYSTEM_AUDIT_OBJECT_ACE_TYPE((byte) 0x07),
48 |
49 | /**
50 | * Reserved for future use.
51 | */
52 | SYSTEM_ALARM_OBJECT_ACE_TYPE((byte) 0x08),
53 |
54 | /**
55 | * Access-allowed callback ACE that uses the ACCESS_ALLOWED_CALLBACK_ACE (section 2.4.4.6) structure.
56 | */
57 | ACCESS_ALLOWED_CALLBACK_ACE_TYPE((byte) 0x09),
58 |
59 | /**
60 | * Access-denied callback ACE that uses the ACCESS_DENIED_CALLBACK_ACE (section 2.4.4.7) structure.
61 | */
62 | ACCESS_DENIED_CALLBACK_ACE_TYPE((byte) 0x0A),
63 |
64 | /**
65 | * Object-specific access-allowed callback ACE that uses the ACCESS_ALLOWED_CALLBACK_OBJECT_ACE (section 2.4.4.8) structure.
66 | */
67 | ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE((byte) 0x0B),
68 |
69 | /**
70 | * Object-specific access-denied callback ACE that uses the ACCESS_DENIED_CALLBACK_OBJECT_ACE (section 2.4.4.9) structure.
71 | */
72 | ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE((byte) 0x0C),
73 |
74 | /**
75 | * System-audit callback ACE that uses the SYSTEM_AUDIT_CALLBACK_ACE (section 2.4.4.12) structure.
76 | */
77 | SYSTEM_AUDIT_CALLBACK_ACE_TYPE((byte) 0x0D),
78 |
79 | /**
80 | * Reserved for future use.
81 | */
82 | SYSTEM_ALARM_CALLBACK_ACE_TYPE((byte) 0x0E),
83 |
84 | /**
85 | * Object-specific system-audit callback ACE that uses the SYSTEM_AUDIT_CALLBACK_OBJECT_ACE (section 2.4.4.14) structure.
86 | */
87 | SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE((byte) 0x0F),
88 |
89 | /**
90 | * Reserved for future use.
91 | */
92 | SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE((byte) 0x10),
93 |
94 | /**
95 | * Mandatory label ACE that uses the SYSTEM_MANDATORY_LABEL_ACE (section 2.4.4.13) structure.
96 | */
97 | SYSTEM_MANDATORY_LABEL_ACE_TYPE((byte) 0x11),
98 |
99 | /**
100 | * Resource attribute ACE that uses the SYSTEM_RESOURCE_ATTRIBUTE_ACE (section 2.4.4.15)
101 | */
102 | SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE((byte) 0x12),
103 |
104 | /**
105 | * A central policy ID ACE that uses the SYSTEM_SCOPED_POLICY_ID_ACE (section 2.4.4.16)
106 | */
107 | SYSTEM_SCOPED_POLICY_ID_ACE_TYPE((byte) 0x13);
108 |
109 | private final byte mask;
110 |
111 | AccessControlEntryType(byte mask) {
112 | this.mask = mask;
113 | }
114 |
115 | @Override
116 | public byte[] toByteArray() {
117 | return new byte[]{mask};
118 | }
119 |
120 | @Override
121 | public int sizeOfByteArray() {
122 | return 1;
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/constants/microsoft/filesecurity/SecurityDescriptorControlFlag.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.constants.microsoft.filesecurity;
2 |
3 | import dev.dokan.dokan_java.masking.MaskValueEnum;
4 | import dev.dokan.dokan_java.masking.MaskValueSet;
5 |
6 | /**
7 | * Enumeration of the different control flags that can be set in the header of a security descriptor
8 | * The documentation is taken from the official Microsoft doc.
9 | */
10 | public enum SecurityDescriptorControlFlag implements MaskValueEnum {
11 |
12 | /**
13 | * Self-Relative
14 | * Set when the security descriptor is in self-relative format. Cleared when the security descriptor is in absolute format.
15 | */
16 | SR(1 << 15),
17 |
18 | /**
19 | * RM Control Valid
20 | * Set to 0x1 when the Sbz1 field is to be interpreted as resource manager control bits.
21 | */
22 | RM(1 << 14),
23 |
24 | /**
25 | * SACL Protected
26 | * Set when the SACL will be protected from inherit operations.
27 | */
28 | PS(1 << 13),
29 |
30 | /**
31 | * DACL Protected
32 | * Set when the DACL will be protected from inherit operations.
33 | */
34 | PD(1 << 12),
35 |
36 | /**
37 | * SACL Auto-Inherited
38 | * Set when the SACL was created through inheritance.
39 | */
40 | SI(1 << 11),
41 |
42 | /**
43 | * DACL Auto-Inherited
44 | * Set when the DACL was created through inheritance.
45 | */
46 | DI(1 << 10),
47 |
48 | /**
49 | * SACL Computed Inheritance Required
50 | * Set when the SACL is to be computed through inheritance. When both SC and SI are set, the resulting security descriptor sets SI; the SC setting is not preserved.
51 | */
52 | SC(1 << 9),
53 |
54 | /**
55 | * DACL Computed Inheritance Required
56 | * Set when the DACL is to be computed through inheritance. When both DC and DI are set, the resulting security descriptor sets DI; the DC setting is not preserved.
57 | */
58 | DC(1 << 8),
59 |
60 | /**
61 | * Server Security
62 | * Set when the caller wants the system to create a Server ACL based on the input ACL, regardless of its source (explicit or defaulting).
63 | */
64 | SS(1 << 7),
65 |
66 | /**
67 | * DACL Trusted
68 | * Set when the ACL that is pointed to by the DACL field was provided by a trusted source and does not require any editing of compound ACEs.
69 | */
70 | DT(1 << 6),
71 |
72 | /**
73 | * SACL Defaulted
74 | * Set when the SACL was established by default means.
75 | */
76 | SD(1 << 5),
77 |
78 | /**
79 | * SACL Present
80 | * Set when the SACL is present on the object.
81 | */
82 | SP(1 << 4),
83 |
84 | /**
85 | * DACL Defaulted
86 | * Set when the DACL was established by default means.
87 | */
88 | DD(1 << 3),
89 |
90 | /**
91 | * DACL Present
92 | * Set when the DACL is present on the object.
93 | */
94 | DP(1 << 2),
95 |
96 | /**
97 | * Group Defaulted
98 | * Set when the group was established by default means.
99 | */
100 | GD(1 << 1),
101 |
102 | /**
103 | * Owner Defaulted
104 | * Set when the owner was established by default means.
105 | */
106 | OD(1 << 0);
107 |
108 | private final int maskingValue;
109 |
110 | SecurityDescriptorControlFlag(int maskingValue) {
111 | this.maskingValue = maskingValue;
112 | }
113 |
114 | public static MaskValueSet maskValueSet(final int mask) {
115 | return MaskValueSet.maskValueSet(mask, values());
116 | }
117 | @Override
118 | public int intValue() {
119 | return this.maskingValue;
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/constants/microsoft/filesecurity/SidIdentifierAuthority.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.constants.microsoft.filesecurity;
2 |
3 | import dev.dokan.dokan_java.Byteable;
4 |
5 | /**
6 | * Enumeration of Well known SidIdentifierAuthorities
7 | */
8 | public enum SidIdentifierAuthority implements Byteable {
9 |
10 | /**
11 | * Specifies the NULL SID authority. It defines only the NULL well-known-SID: S-1-0-0.
12 | */
13 | NULL_SID_AUTHORITY(0),
14 |
15 | /**
16 | * Specifies the World SID authority. It only defines the Everyone well-known-SID: S-1-1-0.
17 | */
18 | WORLD_SID_AUTHORITY(1),
19 |
20 | /**
21 | * Specifies the Local SID authority. It defines only the Local well-known-SID: S-1-2-0.
22 | */
23 | LOCAL_SID_AUTHORITY(2),
24 |
25 | /**
26 | * Specifies the Creator SID authority. It defines the Creator Owner, Creator Group, and Creator Owner Server well-known-SIDs: S-1-3-0, S-1-3-1, and S-1-3-2. These SIDs are used as placeholders in an access control list (ACL) and are replaced by the user, group, and machine SIDs of the security principal.
27 | */
28 | CREATOR_SID_AUTHORITY(3),
29 |
30 | /**
31 | * Not used.
32 | */
33 | NON_UNIQUE_AUTHORITY(4),
34 |
35 | /**
36 | * Specifies the Windows NT operating system security subsystem SID authority. It defines all other SIDs in the forest.
37 | */
38 | SECURITY_NT_AUTHORITY(5),
39 |
40 | /**
41 | * Specifies the application package authority. It defines application capability SIDs.
42 | */
43 | SECURITY_APP_PACKAGE_AUTHORITY(15),
44 |
45 | /**
46 | * Specifies the Mandatory label authority. It defines the integrity level SIDs.
47 | */
48 | SECURITY_MANDATORY_LABEL_AUTHORITY(16),
49 |
50 | /**
51 | * Specifies the Scoped Policy Authority. It defines all other scoped policy SIDs in the forest.
52 | */
53 | SECURITY_SCOPED_POLICY_ID_AUTHORITY(17),
54 |
55 | /**
56 | * Specifies the authentication authority asserting the client’s identity. It defines only the following well-known SIDs: S-1-18-1, and S-1-18-2.
57 | */
58 | SECURITY_AUTHENTICATION_AUTHORITY(18);
59 |
60 | private int id;
61 |
62 | SidIdentifierAuthority(int id) {
63 | this.id = id;
64 | }
65 |
66 | @Override
67 | public byte[] toByteArray() {
68 | return new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, (byte) id};
69 | }
70 |
71 | @Override
72 | public int sizeOfByteArray() {
73 | return 6;
74 | }
75 |
76 | /**
77 | * TODO: can be improved by exception handling
78 | *
79 | * @param id
80 | * @return
81 | */
82 | public static SidIdentifierAuthority fromInt(int id) {
83 | for (SidIdentifierAuthority idAuth : values()) {
84 | if (idAuth.id == id) {
85 | return idAuth;
86 | }
87 | }
88 | return null;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/examples/DirListingFileSystem.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.examples;
2 |
3 |
4 | import com.sun.jna.Pointer;
5 | import com.sun.jna.WString;
6 | import com.sun.jna.platform.win32.WinNT;
7 | import com.sun.jna.ptr.IntByReference;
8 | import com.sun.jna.ptr.LongByReference;
9 | import dev.dokan.dokan_java.DokanFileSystemStub;
10 | import dev.dokan.dokan_java.DokanOperations;
11 | import dev.dokan.dokan_java.DokanUtils;
12 | import dev.dokan.dokan_java.FileSystemInformation;
13 | import dev.dokan.dokan_java.Unsigned;
14 | import dev.dokan.dokan_java.constants.microsoft.CreateDisposition;
15 | import dev.dokan.dokan_java.constants.microsoft.CreateOption;
16 | import dev.dokan.dokan_java.constants.microsoft.NtStatuses;
17 | import dev.dokan.dokan_java.masking.EnumInteger;
18 | import dev.dokan.dokan_java.masking.MaskValueSet;
19 | import dev.dokan.dokan_java.structure.ByHandleFileInformation;
20 | import dev.dokan.dokan_java.structure.DokanFileInfo;
21 | import dev.dokan.dokan_java.structure.DokanIOSecurityContext;
22 |
23 | import java.io.IOException;
24 | import java.nio.file.FileStore;
25 | import java.nio.file.Files;
26 | import java.nio.file.Path;
27 | import java.nio.file.attribute.DosFileAttributes;
28 | import java.util.concurrent.atomic.AtomicLong;
29 | import java.util.stream.Stream;
30 |
31 | /**
32 | * This filesystem shows the content of a given directory and it sub directories
33 | */
34 | public class DirListingFileSystem extends DokanFileSystemStub {
35 |
36 | private final AtomicLong handleHandler;
37 | private final FileStore fileStore;
38 | private final Path root;
39 |
40 | public DirListingFileSystem(Path root, FileSystemInformation fileSystemInformation) {
41 | super(fileSystemInformation);
42 | this.root = root;
43 | this.handleHandler = new AtomicLong(0);
44 | FileStore tmp = null;
45 | try {
46 | tmp = Files.getFileStore(this.root);
47 | } catch (IOException e) {
48 | //Log message
49 | }
50 | this.fileStore = tmp;
51 | }
52 |
53 | @Override
54 | public int zwCreateFile(WString rawPath, DokanIOSecurityContext securityContext, int rawDesiredAccess, int rawFileAttributes, int rawShareAccess, int rawCreateDisposition, int rawCreateOptions, DokanFileInfo dokanFileInfo) {
55 | Path p = getrootedPath(rawPath);
56 |
57 | //the files must exist and we are read only here
58 | CreateDisposition openOption = EnumInteger.enumFromInt(rawCreateDisposition, CreateDisposition.values());
59 | if (Files.exists(p)) {
60 | switch (openOption) {
61 | case FILE_CREATE:
62 | return NtStatuses.STATUS_OBJECT_NAME_COLLISION;
63 | case FILE_OPEN:
64 | case FILE_OPEN_IF:
65 | break;
66 | case FILE_OVERWRITE:
67 | case FILE_OVERWRITE_IF:
68 | case FILE_SUPERSEDE:
69 | return NtStatuses.STATUS_ACCESS_DENIED;
70 | default:
71 | return NtStatuses.STATUS_UNSUCCESSFUL;
72 | }
73 | } else {
74 | switch (openOption) {
75 | case FILE_CREATE:
76 | case FILE_OPEN_IF:
77 | case FILE_OVERWRITE_IF:
78 | case FILE_SUPERSEDE:
79 | return NtStatuses.STATUS_ACCESS_DENIED;
80 | case FILE_OPEN:
81 | case FILE_OVERWRITE:
82 | return NtStatuses.STATUS_OBJECT_NAME_NOT_FOUND;
83 | default:
84 | return NtStatuses.STATUS_UNSUCCESSFUL;
85 | }
86 |
87 | }
88 |
89 | if (Files.isDirectory(p)) {
90 | if (MaskValueSet.maskValueSet(rawCreateOptions, CreateOption.values()).contains(CreateOption.FILE_NON_DIRECTORY_FILE)) {
91 | return NtStatuses.STATUS_FILE_IS_A_DIRECTORY;
92 | } else {
93 | dokanFileInfo.IsDirectory = 1;
94 | }
95 | }
96 |
97 | @Unsigned long val = this.handleHandler.incrementAndGet();
98 | if (val == 0) {
99 | val = this.handleHandler.incrementAndGet();
100 | }
101 |
102 | dokanFileInfo.Context = val;
103 |
104 | return NtStatuses.STATUS_SUCCESS;
105 | }
106 |
107 | @Override
108 | public void cleanup(WString rawPath, DokanFileInfo dokanFileInfo) {
109 | Path p = getrootedPath(rawPath);
110 | //nothing to do
111 | }
112 |
113 | @Override
114 | public void closeFile(WString rawPath, DokanFileInfo dokanFileInfo) {
115 | Path p = getrootedPath(rawPath);
116 | dokanFileInfo.Context = 0;
117 | }
118 |
119 | @Override
120 | public int getFileInformation(WString rawPath, ByHandleFileInformation handleFileInfo, DokanFileInfo dokanFileInfo) {
121 | Path p = getrootedPath(rawPath);
122 | if (dokanFileInfo.Context == 0) {
123 | return NtStatuses.STATUS_INVALID_HANDLE;
124 | }
125 | try {
126 | getFileInformation(p).copyTo(handleFileInfo);
127 | return NtStatuses.STATUS_SUCCESS;
128 | } catch (IOException e) {
129 | return NtStatuses.STATUS_IO_DEVICE_ERROR;
130 | }
131 | }
132 |
133 | private ByHandleFileInformation getFileInformation(Path p) throws IOException {
134 | DosFileAttributes attr = Files.readAttributes(p, DosFileAttributes.class);
135 | long index = 0;
136 | if (attr.fileKey() != null) {
137 | index = (long) attr.fileKey();
138 | }
139 | @Unsigned int fileAttr = 0;
140 | fileAttr |= attr.isArchive() ? WinNT.FILE_ATTRIBUTE_ARCHIVE : 0;
141 | fileAttr |= attr.isSystem() ? WinNT.FILE_ATTRIBUTE_SYSTEM : 0;
142 | fileAttr |= attr.isHidden() ? WinNT.FILE_ATTRIBUTE_HIDDEN : 0;
143 | fileAttr |= attr.isReadOnly() ? WinNT.FILE_ATTRIBUTE_READONLY : 0;
144 | fileAttr |= attr.isDirectory() ? WinNT.FILE_ATTRIBUTE_DIRECTORY : 0;
145 | fileAttr |= attr.isSymbolicLink() ? WinNT.FILE_ATTRIBUTE_REPARSE_POINT : 0;
146 |
147 | if (fileAttr == 0) {
148 | fileAttr |= WinNT.FILE_ATTRIBUTE_NORMAL;
149 | }
150 |
151 | return new ByHandleFileInformation(p.getFileName(), fileAttr, attr.creationTime(), attr.lastAccessTime(), attr.lastModifiedTime(), this.volumeSerialnumber, attr.size(), index);
152 | }
153 |
154 | @Override
155 | public int findFiles(WString rawPath, DokanOperations.FillWin32FindData rawFillFindData, DokanFileInfo dokanFileInfo) {
156 | Path path = getrootedPath(rawPath);
157 | if (dokanFileInfo.Context == 0) {
158 | return NtStatuses.STATUS_INVALID_HANDLE;
159 | }
160 | try (Stream stream = Files.list(path)) {
161 | stream.map(p -> {
162 | try {
163 | return getFileInformation(path.resolve(p)).toWin32FindData();
164 | } catch (IOException e) {
165 | return null;
166 | }
167 | }).forEach(file -> {
168 | if (file != null) {
169 | rawFillFindData.fillWin32FindData(file, dokanFileInfo);
170 | }
171 | });
172 | return NtStatuses.STATUS_SUCCESS;
173 | } catch (IOException e) {
174 | return NtStatuses.STATUS_IO_DEVICE_ERROR;
175 | }
176 | }
177 |
178 | @Override
179 | public int getDiskFreeSpace(LongByReference freeBytesAvailable, LongByReference totalNumberOfBytes, LongByReference totalNumberOfFreeBytes, DokanFileInfo dokanFileInfo) {
180 | if (this.fileStore == null) {
181 | return NtStatuses.STATUS_UNSUCCESSFUL;
182 | } else {
183 | try {
184 | freeBytesAvailable.setValue(fileStore.getUsableSpace());
185 | totalNumberOfBytes.setValue(fileStore.getTotalSpace());
186 | totalNumberOfFreeBytes.setValue(fileStore.getUnallocatedSpace());
187 | return NtStatuses.STATUS_SUCCESS;
188 | } catch (IOException e) {
189 | return NtStatuses.STATUS_IO_DEVICE_ERROR;
190 | }
191 | }
192 | }
193 |
194 | @Override
195 | public int getVolumeInformation(Pointer rawVolumeNameBuffer, int rawVolumeNameSize, IntByReference rawVolumeSerialNumber, IntByReference rawMaximumComponentLength, IntByReference rawFileSystemFlags, Pointer rawFileSystemNameBuffer, int rawFileSystemNameSize, DokanFileInfo dokanFileInfo) {
196 | rawVolumeNameBuffer.setWideString(0L, DokanUtils.trimStrToSize(this.volumeName, rawVolumeNameSize));
197 | rawVolumeSerialNumber.setValue(this.volumeSerialnumber);
198 | rawMaximumComponentLength.setValue(this.fileSystemInformation.getMaxComponentLength());
199 | rawFileSystemFlags.setValue(this.fileSystemInformation.getFileSystemFeatures().intValue());
200 | rawFileSystemNameBuffer.setWideString(0L, DokanUtils.trimStrToSize(this.fileSystemInformation.getFileSystemName(), rawFileSystemNameSize));
201 | return NtStatuses.STATUS_SUCCESS;
202 | }
203 |
204 | private Path getrootedPath(WString rawPath) {
205 | String unixPath = rawPath.toString().replace('\\', '/');
206 | String relativeUnixPath = unixPath;
207 | if(unixPath.startsWith("/"))
208 | relativeUnixPath = unixPath.length()==1?"":unixPath.substring(1); // if it is already the root, we return the empty string
209 | return root.resolve(relativeUnixPath);
210 | }
211 | }
212 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/examples/MirrorFileSystem.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.examples;
2 |
3 | /**
4 | * Mirrors a given directory.
5 | *
6 | * This class is currently a stub. Help to implement it (;
7 | */
8 | public class MirrorFileSystem {
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/masking/EnumInteger.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.masking;
2 |
3 | /**
4 | * An EnumInteger is an enum that is represented by an 32bit integer value.
5 | */
6 | public interface EnumInteger extends IntegerConvertible {
7 |
8 | /**
9 | * Converts an 32bit integer into an object.
10 | *
11 | * The class of the object implements this interface and all whished/possible values to check for a match are given as parameter.
12 | *
13 | * @param value The 32bit integer value to be converted
14 | * @param enumValues Array of possible objects which can be represented as a 32bit integer
15 | * @param Class which implements the EnumInteger interface.
16 | * @return Object which has the same bitmask as value and is a subclass of EnumInteger
17 | *
18 | * @throws IllegalArgumentException if none of the EnumIntegers equals the given value
19 | */
20 | static & EnumInteger> T enumFromInt(final int value, final T[] enumValues) {
21 | for (final T current : enumValues) {
22 | if (value == current.intValue()) {
23 | return current;
24 | }
25 | }
26 |
27 | throw new IllegalArgumentException("Invalid int value: " + value);
28 | }
29 |
30 | static & EnumInteger> T enumFromInt(final int value, final Class type) {
31 | return enumFromInt(value, type.getEnumConstants());
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/masking/IntegerConvertible.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.masking;
2 |
3 | /**
4 | * An IntegerConvertible is an object that is represented by an 32bit integer value.
5 | */
6 | public interface IntegerConvertible {
7 |
8 | /**
9 | * Returns the 32bit integer value which represents this object.
10 | *
11 | * @return the value representing this object.
12 | */
13 | int intValue();
14 |
15 | }
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/masking/MaskValueEnum.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.masking;
2 |
3 |
4 | public interface MaskValueEnum extends EnumInteger {
5 |
6 | default int maskingValue() {
7 | return intValue();
8 | }
9 | }
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/masking/MaskValueSet.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.masking;
2 |
3 | import dev.dokan.dokan_java.constants.dokany.MountOption;
4 | import dev.dokan.dokan_java.constants.microsoft.FileSystemFlag;
5 |
6 | import java.util.Collection;
7 | import java.util.EnumSet;
8 | import java.util.Set;
9 |
10 | /**
11 | * Used to store multiple {@link MaskValueEnum} values such as {@link FileSystemFlag} and {@link MountOption}.
12 | *
13 | * @param Type of {@link EnumInteger}
14 | */
15 | public interface MaskValueSet & MaskValueEnum> extends Set, IntegerConvertible {
16 |
17 | static & MaskValueEnum> MaskValueSet emptySet(Class clazz) {
18 | return new MaskValueSetImpl<>(clazz);
19 | }
20 |
21 | static & MaskValueEnum> MaskValueSet allOf(Class clazz) {
22 | return new MaskValueSetImpl(clazz.getEnumConstants());
23 | }
24 |
25 | static & MaskValueEnum> MaskValueSet copyOf(Collection collection) {
26 | if(collection instanceof MaskValueSet) {
27 | return new MaskValueSetImpl((MaskValueSet) collection);
28 | }
29 | return new MaskValueSetImpl(collection);
30 | }
31 |
32 | static & MaskValueEnum> MaskValueSet of(T[] values) {
33 | return new MaskValueSetImpl(values);
34 | }
35 |
36 | @SafeVarargs
37 | static & MaskValueEnum> MaskValueSet of(T first, T... others) {
38 | return new MaskValueSetImpl<>(first, others);
39 | }
40 |
41 | /**
42 | * Creates a set of MaskValueEnums which corresponds to the bit flag given as an 32bit integer.
43 | *
44 | * The type of the set is the enum class of the input array
45 | *
46 | * @param intValue the integer value of the combined bitflag
47 | * @param allEnumValues all possible values of this MaskValueEnum
48 | * @param enum type of the array implementing the MaskValueEnum interface
49 | * @return a set of MaskValueEnum values whose mask were set in the intValue
50 | */
51 | static & MaskValueEnum> MaskValueSet maskValueSet(final int intValue, final T[] allEnumValues) {
52 | MaskValueSet elements = new MaskValueSetImpl<>(allEnumValues[0].getDeclaringClass());
53 | int remainingValues = intValue;
54 | for (T current : allEnumValues) {
55 | int mask = current.intValue();
56 |
57 | if ((remainingValues & mask) == mask) {
58 | elements.add(current);
59 | remainingValues -= mask;
60 | }
61 | }
62 | return elements;
63 | }
64 |
65 | static & MaskValueEnum> MaskValueSet maskValueSet(final int intValue, final Class type) {
66 | return maskValueSet(intValue, type.getEnumConstants());
67 | }
68 |
69 | void add(T... items);
70 |
71 | EnumSet elements();
72 |
73 | MaskValueSet clone();
74 |
75 | }
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/masking/MaskValueSetImpl.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.masking;
2 |
3 |
4 | import java.util.AbstractSet;
5 | import java.util.Arrays;
6 | import java.util.Collection;
7 | import java.util.EnumSet;
8 | import java.util.Iterator;
9 | import java.util.Objects;
10 |
11 |
12 | public class MaskValueSetImpl & MaskValueEnum> extends AbstractSet implements MaskValueSet, Cloneable {
13 |
14 | private final EnumSet elements;
15 |
16 | public MaskValueSetImpl(Class clazz) {
17 | this.elements = EnumSet.noneOf(clazz);
18 | }
19 |
20 | public MaskValueSetImpl(MaskValueSet set) {
21 | this.elements = EnumSet.copyOf(set.elements());
22 | }
23 |
24 | public MaskValueSetImpl(Collection collection) {
25 | this.elements = EnumSet.copyOf(collection);
26 | }
27 |
28 | @SuppressWarnings("unchecked")
29 | public MaskValueSetImpl(T[] values) {
30 | this((Class) values.getClass().getComponentType());
31 |
32 | add(values);
33 | }
34 |
35 | @SafeVarargs
36 | public MaskValueSetImpl(T first, T... others) {
37 | this.elements = EnumSet.of(first, others);
38 | }
39 |
40 | @SafeVarargs
41 | @Override
42 | public final void add(T... items) {
43 | if (items == null) {
44 | throw new IllegalArgumentException("Adding null is not allowed.");
45 | }
46 |
47 | Arrays.stream(items).filter(Objects::nonNull).forEach(this::add);
48 | }
49 |
50 | @Override
51 | public EnumSet elements() {
52 | return this.elements.clone();
53 | }
54 |
55 | @SuppressWarnings("MethodDoesntCallSuperMethod") //Yes, that's okay here
56 | @Override
57 | public MaskValueSet clone() {
58 | return new MaskValueSetImpl<>(this.elements);
59 | }
60 |
61 | @Override
62 | public int intValue() {
63 | int toReturn = 0;
64 | for (final T current : this.elements) {
65 | toReturn |= current.intValue();
66 | }
67 | return toReturn;
68 | }
69 |
70 | @Override
71 | public boolean add(final T e) {
72 | return this.elements.add(e);
73 | }
74 |
75 | @Override
76 | public Iterator iterator() {
77 | return this.elements.iterator();
78 | }
79 |
80 | @Override
81 | public int size() {
82 | return this.elements.size();
83 | }
84 |
85 | @Override
86 | public String toString() {
87 | return "EnumIntegerSet(elements=" + this.elements + ")";
88 | }
89 |
90 | }
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/structure/DokanAccessState.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.structure;
2 |
3 |
4 | import com.sun.jna.Structure;
5 | import com.sun.jna.platform.win32.WinNT;
6 | import dev.dokan.dokan_java.Unsigned;
7 |
8 | import java.util.Arrays;
9 | import java.util.List;
10 |
11 |
12 | /**
13 | * This is a Dokan specific implementation of the ACCESS_STATE structure of the windows kernel.
14 | *
15 | * @see Microsoft Documentation
16 | * @see Check for Traverse Privilege on IRP_MJ_CREATE.
48 | * A driver can also check for the TOKEN_IS_RESTRICTED flag.
49 | * These flags are defined in ntifs.h.
50 | */
51 | @Unsigned
52 | public int Flags;
53 |
54 | /**
55 | * An ACCESS_MASK type that describes the access rights that have not yet been granted to the caller.
56 | * A driver uses this member to determine if the Windows security system can grant access.
57 | * If access can be granted, the driver updates the PreviouslyGrantedAccess and RemainingDesiredAccess members accordingly.
58 | */
59 | @Unsigned
60 | public int RemainingDesiredAccess;
61 |
62 | /**
63 | * An ACCESS_MASK type that specifies the information about access that has already been granted to the caller of one of the Security Reference Monitor Routines
64 | * The Windows security system grants certain rights based on the privileges of the caller, such as traverse right (the ability to traverse through a directory as part of opening a subdirectory or file).
65 | */
66 | @Unsigned
67 | public int PreviouslyGrantedAccess;
68 |
69 | /**
70 | * An ACCESS_MASK type that contains the original access rights that were requested by the caller.
71 | */
72 | @Unsigned
73 | public int OriginalDesiredAccess;
74 |
75 | /**
76 | * A self relative security descriptor that contains security information for the object that this access relates to.
77 | */
78 | public WinNT.SECURITY_DESCRIPTOR_RELATIVE SecurityDescriptor;
79 |
80 | /**
81 | * A UNICODE_STRING structure that contains the object name string for the access. This member is used for auditing.
82 | */
83 | public UnicodeString ObjectName;
84 |
85 | /**
86 | * A UNICODE_STRING structure that contains the object type name string for the access. This member is used for auditing.
87 | */
88 | public UnicodeString ObjectType;
89 |
90 | @Override
91 | protected List getFieldOrder() {
92 | return Arrays.asList(new String[]{"SecurityEvaluated",
93 | "GenerateAudit",
94 | "GenerateOnClose",
95 | "AuditPrivileges",
96 | "Flags",
97 | "RemainingDesiredAccess",
98 | "PreviouslyGrantedAccess",
99 | "OriginalDesiredAccess",
100 | "SecurityDescriptor",
101 | "ObjectName",
102 | "ObjectType"});
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/structure/DokanControl.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.structure;
2 |
3 |
4 | import com.sun.jna.Native;
5 | import com.sun.jna.NativeLong;
6 | import com.sun.jna.Pointer;
7 | import com.sun.jna.Structure;
8 | import com.sun.jna.platform.win32.WinNT;
9 | import dev.dokan.dokan_java.Unsigned;
10 |
11 | import java.util.ArrayList;
12 | import java.util.Arrays;
13 | import java.util.List;
14 |
15 | /**
16 | * Structure containing information for a single Dokan device.
17 | *
18 | * Used by dev.dokan.dokan_java.NativeMethods#DokanGetMountPointList(boolean, LongByReference)} and dev.dokan.dokan_java.NativeMethods#DokanReleaseMountPointList(Pointer).
19 | */
20 | public class DokanControl extends Structure implements Structure.ByReference {
21 |
22 | /**
23 | * File System Type
24 | */
25 | @Unsigned
26 | public int Type;
27 |
28 | /**
29 | * Mount point. Can be "M:\" (drive letter) or "C:\mount\dokan" (path in NTFS)
30 | */
31 | public char[] MountPoint = new char[WinNT.MAX_PATH];
32 |
33 | /**
34 | * UNC name used for network volume
35 | */
36 | public char[] UNCName = new char[64];
37 |
38 | /**
39 | * Disk Device Name
40 | */
41 | public char[] DeviceName = new char[64];
42 |
43 | /**
44 | * Volume Device Object
45 | */
46 | public Pointer DeviceObject;
47 |
48 | /**
49 | * Session ID of calling process
50 | */
51 | @Unsigned
52 | public int SessionId;
53 |
54 | public DokanControl(Pointer p) {
55 | this(p, 0);
56 | }
57 |
58 | public DokanControl(Pointer p, long currentOffset) {
59 | super(p);
60 | this.Type = p.getInt(currentOffset);
61 | currentOffset += NativeLong.SIZE;
62 | this.MountPoint = p.getCharArray(currentOffset, WinNT.MAX_PATH);
63 | currentOffset += WinNT.MAX_PATH * 2;
64 | this.UNCName = p.getCharArray(currentOffset, 64);
65 | currentOffset += 64 * 2;
66 | this.DeviceName = p.getCharArray(currentOffset, 64);
67 | currentOffset += 64 * 2;
68 | this.DeviceObject = new Pointer(p.getLong(currentOffset));
69 | currentOffset += Native.POINTER_SIZE;
70 | this.SessionId = p.getInt(currentOffset);
71 | }
72 |
73 |
74 | @Override
75 | protected List getFieldOrder() {
76 | return Arrays.asList("Type", "MountPoint", "UNCName", "DeviceName", "DeviceObject", "SessionId");
77 | }
78 |
79 | /**
80 | * Creates a java {@link List} of {@link DokanControl} structures given the pointer returned by NativeMethods#DokanGetMountPointList(boolean, LongByReference).
81 | *
82 | * Implementation note:
83 | * Length is an unsigned 32-bit int. Java only supports arrays and lists up to an index size of 231-1 ({@link Integer#MAX_VALUE Integer.MAX_VALUE}).
84 | * A list that exceeds this size is unrealistic (it would need at least 2 GB of space, even if it only stored unique Byte-Objects).
85 | * Any value that exceeds {@link Integer#MAX_VALUE Integer.MAX_VALUE}, has it's 32nd bit set (at least when using Two's complement for storing it).
86 | * The 32nd bit defines the sign of a java int, therefore a {@code length < 0} indicates that this threshold has been reached.
87 | * In this case the application crashes ("fail-fast") to allow someone to take care of this issue.
88 | *
89 | * @param start the initial pointer returned by the native method
90 | * @param length the number of elements in the array. Also acquired with the native method call.
91 | * @return a list of DokanControl structures
92 | */
93 | public static List getDokanControlList(Pointer start, @Unsigned int length) { //TODO Relocate
94 | List list = new ArrayList<>();
95 |
96 | if(length < 0) {
97 | throw new AssertionError(String.format("Illegal length: %s (%d)", Integer.toUnsignedString(length), length));
98 | }
99 | if (length != 0) {
100 | long offset = 0;
101 | for(int i = 0; i < length; i++) {
102 | DokanControl control = new DokanControl(start, offset);
103 | list.add(control);
104 | offset += control.size();
105 | }
106 | }
107 | return list;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/structure/DokanFileInfo.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.structure;
2 |
3 |
4 | import com.sun.jna.Structure;
5 | import dev.dokan.dokan_java.DokanNativeMethods;
6 | import dev.dokan.dokan_java.DokanOperations;
7 | import dev.dokan.dokan_java.Unsigned;
8 | import dev.dokan.dokan_java.UnsignedNumbers;
9 |
10 | import java.util.Arrays;
11 | import java.util.List;
12 |
13 | /**
14 | * Dokan file information on the current operation.
15 | *
16 | * @see Dokany Documentation of PDOKAN_FILE_INFO
17 | */
18 | public class DokanFileInfo extends Structure implements Structure.ByReference {
19 |
20 | /**
21 | * Context that can be used to carry information between operation. The context can carry whatever type like {@link com.sun.jna.platform.win32.WinNT.HANDLE}, {@link Structure}, {@link com.sun.jna.ptr.IntByReference},
22 | * {@link com.sun.jna.Pointer} that will help the implementation understand the request context of the event.
23 | */
24 | @Unsigned
25 | public long Context;
26 |
27 | /**
28 | * Flag if the file has to be delete during {@link DokanOperations#Cleanup} event.
29 | */
30 | public byte DeleteOnClose;
31 |
32 | /**
33 | * Reserved. Used internally by Dokan library. Never modify.
34 | */
35 | @Unsigned
36 | public long DokanContext;
37 |
38 | /**
39 | * A pointer to {@link DokanOptions} which was passed to {@link DokanNativeMethods#DokanMain}.
40 | */
41 | public DokanOptions DokanOpts;
42 |
43 | /**
44 | * Requesting a directory file. Must be set in {@link DokanOperations#ZwCreateFile} if the file object appears to be a directory.
45 | */
46 | public byte IsDirectory;
47 |
48 | /**
49 | * Read or write directly from data source without cache.
50 | */
51 | public byte Nocache;
52 |
53 | /**
54 | * Read or write is paging IO.
55 | */
56 | public byte PagingIo;
57 |
58 | /**
59 | * Process ID for the thread that originally requested a given I/O operation.
60 | */
61 | @Unsigned
62 | public int ProcessId;
63 |
64 | /**
65 | * Read or write is synchronous IO.
66 | */
67 | public byte SynchronousIo;
68 |
69 | /**
70 | * If true, write to the current end of file instead of using the Offset parameter.
71 | */
72 | public byte WriteToEndOfFile;
73 |
74 | public DokanFileInfo() {
75 | }
76 |
77 | @Override
78 | protected List getFieldOrder() {
79 | return Arrays.asList("Context", "DokanContext", "DokanOpts", "ProcessId", "IsDirectory", "DeleteOnClose", "PagingIo", "SynchronousIo", "Nocache", "WriteToEndOfFile");
80 | }
81 |
82 | public final boolean isDirectory() {
83 | return IsDirectory != 0;
84 | }
85 |
86 | public final boolean deleteOnClose() {
87 | return DeleteOnClose != 0;
88 | }
89 |
90 | public final boolean pagingIo() {
91 | return PagingIo != 0;
92 | }
93 |
94 | public final boolean synchronousIo() {
95 | return SynchronousIo != 0;
96 | }
97 |
98 | public final boolean noCache() {
99 | return Nocache != 0;
100 | }
101 |
102 | public final boolean writeToEndOfFile() {
103 | return WriteToEndOfFile != 0;
104 | }
105 |
106 | @Override
107 | public String toString() {
108 | return String.format("DokanFileInfo(Context=%s, DokanContext=%s, DokanOpts=%s, ProcessId=%s, IsDirectory=%s/%s, DeleteOnClose=%s/%s, PagingIo=%s/%s, SynchronousIo=%s/%s, Nocache=%s/%s, WriteToEndOfFile=%s/%s)",
109 | UnsignedNumbers.toUnsignedString(this.Context),
110 | UnsignedNumbers.toUnsignedString(this.DokanContext),
111 | this.DokanOpts,
112 | UnsignedNumbers.toUnsignedString(this.ProcessId),
113 | this.IsDirectory, isDirectory(),
114 | this.DeleteOnClose, deleteOnClose(),
115 | this.PagingIo, pagingIo(),
116 | this.SynchronousIo, synchronousIo(),
117 | this.Nocache, noCache(),
118 | this.WriteToEndOfFile, writeToEndOfFile());
119 | }
120 |
121 | }
122 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/structure/DokanIOSecurityContext.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.structure;
2 |
3 |
4 | import com.sun.jna.Structure;
5 | import com.sun.jna.WString;
6 | import dev.dokan.dokan_java.Unsigned;
7 |
8 |
9 | /**
10 | * The DokanIOSecurityContext contains the Dokan specific security context of the Windows kernel create request.
11 | * It is a parameter in the {@link dev.dokan.dokan_java.DokanFileSystem#zwCreateFile(WString, DokanIOSecurityContext, int, int, int, int, int, DokanFileInfo)} function.
12 | *
13 | * @see Microsoft documentation of the original structure
14 | * @see Dokany Documentation of PDOKAN_OPTIONS
20 | */
21 | public class DokanOptions extends Structure implements Structure.ByReference {
22 |
23 | /**
24 | * Version of the Dokan features requested (version "123" is equal to Dokan version 1.2.3).
25 | */
26 | @Unsigned
27 | public short Version = DokanNativeMethods.getMinimumRequiredDokanVersion();
28 |
29 | /**
30 | * Number of threads to be used internally by Dokan library. More thread will handle more events at the same time.
31 | */
32 | @Unsigned
33 | public short ThreadCount;
34 |
35 | /**
36 | * Features enable for the mount. It is a combination of {@link MountOption} masks.
37 | */
38 | @Unsigned
39 | public int Options;
40 |
41 | /**
42 | * FileSystem can store anything here
43 | */
44 | @Unsigned
45 | public long GlobalContext = 0L;
46 |
47 | /**
48 | * Mount point. It can be a drive letter like \"M:\\\" or a folder path \"C:\\mount\\dokany\" on a NTFS partition.
49 | */
50 | public WString MountPoint;
51 |
52 | /**
53 | * UNC name used for the Network Redirector.
54 | *
55 | * @see Support for UNC Naming
56 | */
57 | public WString UNCName;
58 |
59 | /**
60 | * Max timeout in milliseconds of each request before Dokan gives up to wait events to complete.
61 | */
62 | @Unsigned
63 | public int Timeout;
64 |
65 | /**
66 | * Allocation Unit Size of the volume. This will affect the file size.
67 | */
68 | @Unsigned
69 | public int AllocationUnitSize;
70 |
71 | /**
72 | * Sector Size of the volume. This will affect then file size.
73 | */
74 | @Unsigned
75 | public int SectorSize;
76 |
77 | public DokanOptions() {
78 |
79 | }
80 |
81 | public DokanOptions(String mountPoint, @Unsigned short threadCount, MaskValueSet mountOptions, String uncName, @Unsigned int timeout, @Unsigned int allocationUnitSize, @Unsigned int sectorSize) {
82 | MountPoint = new WString(mountPoint);
83 | ThreadCount = threadCount;
84 | Options = mountOptions.intValue();
85 | if (uncName != null) {
86 | UNCName = new WString(uncName);
87 | } else {
88 | UNCName = null;
89 | }
90 | Timeout = timeout;
91 | AllocationUnitSize = allocationUnitSize;
92 | SectorSize = sectorSize;
93 | }
94 |
95 | public MaskValueSet getMountOptions() {
96 | return MaskValueSet.maskValueSet(this.Options, MountOption.values());
97 | }
98 |
99 | @Override
100 | protected List getFieldOrder() {
101 | return Arrays.asList("Version", "ThreadCount", "Options", "GlobalContext", "MountPoint", "UNCName", "Timeout", "AllocationUnitSize", "SectorSize");
102 | }
103 |
104 | @Override
105 | public String toString() {
106 | return String.format("DeviceOptions(Version=%s, ThreadCount=%s, Options=%s, mountOptions=%s, GlobalContext=%s, MountPoint=%s, UNCName=%s, Timeout=%s, AllocationUnitSize=%s, SectorSize=%s)",
107 | UnsignedNumbers.toUnsignedString(this.Version),
108 | UnsignedNumbers.toUnsignedString(this.ThreadCount),
109 | UnsignedNumbers.toUnsignedString(this.Options),
110 | this.getMountOptions(),
111 | UnsignedNumbers.toUnsignedString(this.GlobalContext),
112 | this.MountPoint,
113 | this.UNCName,
114 | UnsignedNumbers.toUnsignedString(this.Timeout),
115 | UnsignedNumbers.toUnsignedString(this.AllocationUnitSize),
116 | UnsignedNumbers.toUnsignedString(this.SectorSize));
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/structure/UnicodeString.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.structure;
2 |
3 |
4 | import com.sun.jna.Pointer;
5 | import com.sun.jna.Structure;
6 | import dev.dokan.dokan_java.Unsigned;
7 |
8 |
9 | /**
10 | * Supplemental class used to define Unicode Strings.
11 | *
12 | * This class is needed to fully implement {@link DokanAccessState}.
13 | * It is defined in fileinfo.h in the dokan module of the Dokany project.
14 | */
15 | @Structure.FieldOrder({"Length", "MaximumLength", "Buffer"})
16 | public class UnicodeString extends Structure {
17 |
18 | /**
19 | * The length, in bytes, of the string stored in {@link UnicodeString#Buffer}.
20 | */
21 | @Unsigned
22 | public short Length;
23 |
24 | /**
25 | * The length, in bytes, of {@link UnicodeString#Buffer}.
26 | */
27 | @Unsigned
28 | public short MaximumLength;
29 |
30 | /**
31 | * Pointer to a buffer used to contain a string of wide characters.
32 | */
33 | public Pointer Buffer;
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/structure/filesecurity/AccessAllowedACE.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.structure.filesecurity;
2 |
3 | import dev.dokan.dokan_java.masking.MaskValueSet;
4 | import dev.dokan.dokan_java.constants.microsoft.accessmaskflags.BasicAccessMaskFlag;
5 | import dev.dokan.dokan_java.constants.microsoft.filesecurity.AccessControlEntryFlag;
6 | import dev.dokan.dokan_java.constants.microsoft.filesecurity.AccessControlEntryType;
7 |
8 | import java.nio.ByteBuffer;
9 |
10 | public class AccessAllowedACE extends AccessControlEntry {
11 |
12 | MaskValueSet rights;
13 |
14 | SecurityIdentifier sid;
15 |
16 | public AccessAllowedACE(MaskValueSet flags, SecurityIdentifier sid, MaskValueSet rights) {
17 | super(AccessControlEntryType.ACCESS_ALLOWED_ACE_TYPE, flags);
18 | this.rights = rights;
19 | this.sid = sid;
20 | }
21 |
22 | @Override
23 | public byte[] toByteArray() {
24 | ByteBuffer buf = ByteBuffer.allocate(sizeOfByteArray());
25 | buf.put(type.toByteArray());
26 | buf.put((byte) flags.intValue());
27 | buf.putShort(Short.reverseBytes((short) sizeOfByteArray()));
28 | buf.putInt(Integer.reverseBytes(rights.intValue()));
29 | buf.put(sid.toByteArray());
30 | return buf.array();
31 | }
32 |
33 | @Override
34 | public int sizeOfByteArray() {
35 | return 1 //type
36 | + 1 //flags
37 | + 2 //size
38 | + 4 //mask
39 | + sid.sizeOfByteArray(); //size of sid
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/structure/filesecurity/AccessControlEntry.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.structure.filesecurity;
2 |
3 | import dev.dokan.dokan_java.Byteable;
4 | import dev.dokan.dokan_java.constants.microsoft.filesecurity.AccessControlEntryFlag;
5 | import dev.dokan.dokan_java.constants.microsoft.filesecurity.AccessControlEntryType;
6 | import dev.dokan.dokan_java.masking.MaskValueSet;
7 |
8 | public abstract class AccessControlEntry implements Byteable {
9 |
10 | protected final AccessControlEntryType type;
11 |
12 | protected final MaskValueSet flags;
13 |
14 | protected AccessControlEntry(AccessControlEntryType type, MaskValueSet flags) {
15 | this.type = type;
16 | this.flags = flags;
17 | }
18 |
19 | @Override
20 | public abstract byte[] toByteArray();
21 |
22 | @Override
23 | public abstract int sizeOfByteArray();
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/structure/filesecurity/AccessControlList.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.structure.filesecurity;
2 |
3 | import dev.dokan.dokan_java.Byteable;
4 | import dev.dokan.dokan_java.constants.microsoft.filesecurity.AccessControlEntryType;
5 |
6 | import java.nio.ByteBuffer;
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | /**
11 | * Object-oriented implementation of the ACL-structure used in a {@link SelfRelativeSecurityDescriptor}.
12 | * For more information, please read the official Microsoft documentation.
13 | */
14 | public class AccessControlList implements Byteable {
15 |
16 | private enum ACLType {
17 | DACL,
18 | SACL;
19 | }
20 |
21 | private final ACLType aclType;
22 |
23 | private static final AccessControlEntryType[] allowedACEsForDaclRev2 = new AccessControlEntryType[]{
24 | AccessControlEntryType.ACCESS_ALLOWED_ACE_TYPE, AccessControlEntryType.ACCESS_DENIED_ACE_TYPE
25 | };
26 |
27 | private static final AccessControlEntryType[] allowedACEsForDaclRev4 = new AccessControlEntryType[]{
28 | AccessControlEntryType.ACCESS_ALLOWED_COMPOUND_ACE_TYPE, AccessControlEntryType.ACCESS_ALLOWED_OBJECT_ACE_TYPE, AccessControlEntryType.ACCESS_DENIED_OBJECT_ACE_TYPE
29 | };
30 |
31 | private static final AccessControlEntryType[] allowedACEsForSaclRev2 = new AccessControlEntryType[]{
32 | AccessControlEntryType.SYSTEM_AUDIT_ACE_TYPE, AccessControlEntryType.SYSTEM_ALARM_ACE_TYPE, AccessControlEntryType.SYSTEM_MANDATORY_LABEL_ACE_TYPE, AccessControlEntryType.SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE, AccessControlEntryType.SYSTEM_SCOPED_POLICY_ID_ACE_TYPE
33 | };
34 |
35 | private static final AccessControlEntryType[] allowedACEsForSaclRev4 = new AccessControlEntryType[]{
36 | AccessControlEntryType.SYSTEM_AUDIT_OBJECT_ACE_TYPE, AccessControlEntryType.SYSTEM_ALARM_OBJECT_ACE_TYPE, AccessControlEntryType.SYSTEM_MANDATORY_LABEL_ACE_TYPE
37 | };
38 |
39 | /**
40 | * AclRevision
41 | * An unsigned 8-bit value that specifies the revision of the ACL. The only two legitimate forms of ACLs supported for on-the-wire management or manipulation are type 2 and type 4. No other form is valid for manipulation on the wire.
42 | */
43 | private final byte aclRevision;
44 |
45 | /**
46 | * Sbz1
47 | * An unsigned 8-bit value. This field is reserved and MUST be set to zero.
48 | */
49 | private final byte sbz1 = 0;
50 |
51 | /**
52 | * Sbz2
53 | * An unsigned 16-bit integer. This field is reserved and MUST be set to zero.
54 | */
55 | private final short sbz2 = 0;
56 |
57 | /**
58 | * List of AccessControlEntries in this ACL
59 | */
60 | private List aces;
61 |
62 | private AccessControlList(ACLType aclType, byte aclRevision, List extends AccessControlEntry> aces) {
63 | this.aclType = aclType;
64 | this.aclRevision = aclRevision;
65 | this.aces = new ArrayList<>(15);
66 | this.aces.addAll(aces);
67 | }
68 |
69 |
70 | @Override
71 | public byte[] toByteArray() {
72 | ByteBuffer buf = ByteBuffer.allocate(sizeOfByteArray());
73 | buf.put(aclRevision);
74 | buf.put(sbz1);
75 | buf.putShort(Short.reverseBytes((short) sizeOfByteArray()));
76 | buf.putShort(Short.reverseBytes((short) aces.size()));
77 | buf.putShort(Short.reverseBytes(sbz2));
78 | for (AccessControlEntry ace : aces) {
79 | buf.put(ace.toByteArray());
80 | }
81 | return buf.array();
82 | }
83 |
84 | @Override
85 | public int sizeOfByteArray() {
86 | return 1 // aclRevision
87 | + 1 //sbz1
88 | + 2 // aclSize
89 | + 2 //ace count
90 | + 2 //sbz2
91 | + aces.stream().reduce(0, (sum, ace) -> sum + ace.sizeOfByteArray(), Integer::sum);
92 | }
93 |
94 | public static AccessControlList createDaclRevision2(List extends AccessControlEntry> aces) {
95 | for (AccessControlEntry ace : aces) {
96 | if (!isValidAce(ace.type, allowedACEsForDaclRev2)) {
97 | //we found an invalid ace
98 | //aborting
99 | return null;
100 | }
101 | }
102 | return new AccessControlList(ACLType.DACL, (byte) 0x02, aces);
103 | }
104 |
105 | public static AccessControlList createDaclRevision4(List extends AccessControlEntry> aces) {
106 | for (AccessControlEntry ace : aces) {
107 | if (!isValidAce(ace.type, allowedACEsForDaclRev4)) {
108 | //we found an invalid ace
109 | //aborting
110 | return null;
111 | }
112 | }
113 | return new AccessControlList(ACLType.DACL, (byte) 0x04, aces);
114 | }
115 |
116 | public static AccessControlList createSaclRevision2(List extends AccessControlEntry> aces) {
117 | for (AccessControlEntry ace : aces) {
118 | if (!isValidAce(ace.type, allowedACEsForSaclRev2)) {
119 | //we found an invalid ace
120 | //aborting
121 | return null;
122 | }
123 | }
124 | return new AccessControlList(ACLType.SACL, (byte) 0x02, aces);
125 | }
126 |
127 | public static AccessControlList createSaclRevision4(List extends AccessControlEntry> aces) {
128 | for (AccessControlEntry ace : aces) {
129 | if (!isValidAce(ace.type, allowedACEsForSaclRev4)) {
130 | //we found an invalid ace
131 | //aborting
132 | return null;
133 | }
134 | }
135 | return new AccessControlList(ACLType.SACL, (byte) 0x04, aces);
136 | }
137 |
138 | private static boolean isValidAce(AccessControlEntryType aceType, AccessControlEntryType[] validACEs) {
139 | for (AccessControlEntryType validAceType : validACEs) {
140 | if (aceType.ordinal() == validAceType.ordinal()) {
141 | return true;
142 | }
143 | }
144 | return false;
145 | }
146 |
147 | }
148 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/structure/filesecurity/SecurityIdentifier.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.structure.filesecurity;
2 |
3 | import dev.dokan.dokan_java.Byteable;
4 | import dev.dokan.dokan_java.DokanException;
5 | import dev.dokan.dokan_java.constants.microsoft.filesecurity.SidIdentifierAuthority;
6 |
7 | import java.nio.ByteBuffer;
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | /**
12 | * Object-oriented implementation of the SID structure.
13 | * See also the microsoft doc.
14 | * TODO: implement add() method to add subAuthorities
15 | */
16 | public class SecurityIdentifier implements Byteable {
17 |
18 | private final byte revision = 0x01;
19 |
20 | private final static int MAX_SUB_AUTHORITIES =15 ;
21 |
22 | private SidIdentifierAuthority sidAuth;
23 |
24 | private List subAuthorities;
25 |
26 | /**
27 | * Creates a SecurityIdentifier with the given Authority and Subauthorities.
28 | * The number of Subauthorities is bounded from above by 15 and if the List of subauthorities exceeds this limit only the first 15 will be taken.
29 | *
30 | * @param sidAuth
31 | * @param subAuthorities
32 | */
33 | public SecurityIdentifier(SidIdentifierAuthority sidAuth, List subAuthorities) {
34 | this.sidAuth = sidAuth;
35 | this.subAuthorities = new ArrayList<>(MAX_SUB_AUTHORITIES);
36 | if (subAuthorities != null) {
37 | if (subAuthorities.size() <= MAX_SUB_AUTHORITIES) {
38 | this.subAuthorities.addAll(subAuthorities);
39 | } else {
40 | throw new DokanException("Number of sub-authorities exceeds the limit of "+MAX_SUB_AUTHORITIES+", it is: "+subAuthorities.size());
41 | }
42 | }
43 | }
44 |
45 | @Override
46 | public byte[] toByteArray() {
47 | ByteBuffer buf = ByteBuffer.allocate(sizeOfByteArray());
48 | buf.put(revision);
49 | buf.put((byte) subAuthorities.size());
50 | //dont reverse the authority
51 | buf.put(sidAuth.toByteArray());
52 | for (Integer subAuth : subAuthorities) {
53 | //but reverse the subauthorities
54 | buf.putInt(Integer.reverseBytes(subAuth));
55 | }
56 | return buf.array();
57 | }
58 |
59 | @Override
60 | public int sizeOfByteArray() {
61 | return 1 //revision
62 | + 1 //subauthority count
63 | + 6 //6 bytes of of the id authority
64 | + 4 * subAuthorities.size();//each subauthority consists of 4 bytes
65 | }
66 |
67 | /**
68 | * @param stringSid
69 | * @return
70 | */
71 | public static SecurityIdentifier fromString(String stringSid) {
72 | String[] sidTokenized = stringSid.split("-");
73 | List subAuths = new ArrayList<>(sidTokenized.length - 3); // the first tokens are S-[Revision]-[IdAuthority]-...
74 | SidIdentifierAuthority idAuth = SidIdentifierAuthority.fromInt(Integer.parseUnsignedInt(sidTokenized[2]));
75 | for (int i = 3; i < sidTokenized.length; i++) {
76 | subAuths.add(Integer.parseUnsignedInt(sidTokenized[i]));
77 | }
78 | return new SecurityIdentifier(idAuth, subAuths);
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/main/java/dev/dokan/dokan_java/structure/filesecurity/SelfRelativeSecurityDescriptor.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.structure.filesecurity;
2 |
3 | import dev.dokan.dokan_java.Byteable;
4 | import dev.dokan.dokan_java.constants.microsoft.filesecurity.SecurityDescriptorControlFlag;
5 | import dev.dokan.dokan_java.masking.MaskValueSet;
6 |
7 | import java.nio.ByteBuffer;
8 | import java.util.Optional;
9 |
10 | /**
11 | * Class implementing the self-relative SelfRelativeSecurityDescriptor structure described in the Microsoft documentation. The following is a copy of the documentation:
12 | *
13 | * The SECURITY_DESCRIPTOR structure defines the security attributes of an object. These attributes specify who owns the object; who can access the object and what they can do with it; what level of audit logging can be applied to the object; and what kind of restrictions apply to the use of the security descriptor.
14 | *
15 | * Security descriptors appear in one of two forms, absolute or self-relative.
16 | *
17 | * A security descriptor is said to be in absolute format if it stores all of its security information via pointer fields, as specified in the RPC representation in section 2.4.6.1.
18 | *
19 | * A security descriptor is said to be in self-relative format if it stores all of its security information in a contiguous block of memory and expresses all of its pointer fields as offsets from its beginning. The order of appearance of pointer target fields is not required to be in any particular order; the location of the OwnerSid, GroupSid, Sacl, and/or Dacl is only based on OffsetOwner, OffsetGroup, OffsetSacl, and/or OffsetDacl pointers found in the fixed portion of the relative security descriptor.
20 | *
21 | * The self-relative form of the security descriptor is required if one wants to transmit the SECURITY_DESCRIPTOR structure as an opaque data structure for transmission in communication protocols over a wire, or for storage on secondary media; the absolute form cannot be transmitted because it contains pointers to objects that are generally not accessible to the recipient.
22 | *
23 | * When a self-relative security descriptor is transmitted over a wire, it is sent in little-endian format and requires no padding.
24 | */
25 | public class SelfRelativeSecurityDescriptor implements Byteable {
26 |
27 | private static final byte[] EMPTY = new byte[0];
28 |
29 | /**
30 | * Revision
31 | * An unsigned 8-bit value that specifies the revision of the SECURITY_DESCRIPTOR structure. This field MUST be set to one.
32 | */
33 | private final byte revision = (byte) 0x01;
34 |
35 | /**
36 | * Sbz1
37 | * An unsigned 8-bit value with no meaning unless the Control RM bit is set to 0x1. If the RM bit is set to 0x1, Sbz1 is interpreted as the resource manager control bits that contain specific information<70> for the specific resource manager that is accessing the structure. The permissible values and meanings of these bits are determined by the implementation of the resource manager.
38 | */
39 | private final byte sbz1 = (byte) 0x00;
40 |
41 | /**
42 | * Control
43 | * An unsigned 16-bit field that specifies control access bit flags. The Self Relative (SR) bit MUST be set when the security descriptor is in self-relative format, represented by a EnumIntegerSet of ControlFlags
44 | *
45 | * @see MaskValueSet
46 | * @see SecurityDescriptorControlFlag
47 | */
48 | private MaskValueSet control;
49 |
50 | /**
51 | * OwnerSid
52 | * The SID of the owner of the object. The length of the SID MUST be a multiple of 4. This field MUST be present if the OffsetOwner field is not zero.
53 | *
54 | * This implementation guarantees the existence of a SID if the offset is not zero by only writing an offset greater zero if this structure is present.
55 | *
56 | * @see SecurityIdentifier
57 | */
58 | private Optional ownerSid;
59 |
60 |
61 | /**
62 | * GroupSid
63 | * The SID of the group of the object. The length of the SID MUST be a multiple of 4. This field MUST be present if the OffsetOwner field is not zero.
64 | *
65 | * This implementation guarantees the existence of a SID if the offset is not zero by only writing an offset greater zero if this structure is present.
66 | *
67 | * @see SecurityIdentifier
68 | */
69 | private Optional groupSid;
70 |
71 | /**
72 | * Sacl
73 | * The SACL of the object. The length of the SID MUST be a multiple of 4. This field MUST be present if the SP flag is set.
74 | *
75 | * This implementation guarantees the existence of a SACL if SP-flag is set by only writing the flag if this structure is present.
76 | */
77 | private Optional sacl;
78 |
79 | /**
80 | * Dacl
81 | * The DACL of the object. The length of the SID MUST be a multiple of 4. This field MUST be present if the DP flag is set.
82 | *
83 | * This implementation guarantees the existence of a DACL if DP-flag is set by only writing the flag if this structure is present.
84 | */
85 | private Optional dacl;
86 |
87 | /**
88 | * Creates an empty SecurityDescriptor.
89 | *
90 | * @param control
91 | */
92 | private SelfRelativeSecurityDescriptor(MaskValueSet control) {
93 | this.control = control;
94 | this.ownerSid = Optional.empty();
95 | this.groupSid = Optional.empty();
96 | this.sacl = Optional.empty();
97 | this.dacl = Optional.empty();
98 | }
99 |
100 | private SelfRelativeSecurityDescriptor(MaskValueSet control, SecurityIdentifier ownerSid, SecurityIdentifier groupSid, AccessControlList sacl, AccessControlList dacl) {
101 | this.control = control;
102 | if (ownerSid != null) {
103 | this.ownerSid = Optional.of(ownerSid);
104 | } else {
105 | this.ownerSid = Optional.empty();
106 | }
107 | if (groupSid != null) {
108 | this.groupSid = Optional.of(groupSid);
109 | } else {
110 | this.groupSid = Optional.empty();
111 | }
112 | if (sacl != null) {
113 | this.sacl = Optional.of(sacl);
114 | } else {
115 | this.sacl = Optional.empty();
116 | }
117 | if (dacl != null) {
118 | this.dacl = Optional.of(dacl);
119 | } else {
120 | this.dacl = Optional.empty();
121 | }
122 | }
123 |
124 | @Override
125 | public byte[] toByteArray() {
126 | int offset = 20; // basic header offset: revision, sbz1, controlmask and the four offsets
127 | int offsetOwner = 0, offsetGroup = 0, offsetSacl = 0, offsetDacl = 0;
128 | if (ownerSid.isPresent()) {
129 | offsetOwner = offset;
130 | offset += ownerSid.get().sizeOfByteArray();
131 | }
132 | if (groupSid.isPresent()) {
133 | offsetGroup = offset;
134 | offset += groupSid.get().sizeOfByteArray();
135 | }
136 | if (sacl.isPresent()) {
137 | offsetSacl = offset;
138 | offset += sacl.get().sizeOfByteArray();
139 | }
140 | if (dacl.isPresent()) {
141 | offsetDacl = offset;
142 | offset += dacl.get().sizeOfByteArray(); // not really necessary
143 | }
144 | //do some computations of the size
145 | ByteBuffer buf = ByteBuffer.allocate(sizeOfByteArray());
146 | buf.put(revision);
147 | buf.put(sbz1);
148 | buf.putShort(Short.reverseBytes((short) control.intValue()));
149 | buf.putInt(Integer.reverseBytes(offsetOwner));
150 | buf.putInt(Integer.reverseBytes(offsetGroup));
151 | buf.putInt(Integer.reverseBytes(offsetSacl));
152 | buf.putInt(Integer.reverseBytes(offsetDacl));
153 | buf.put(ownerSid.map(SecurityIdentifier::toByteArray).orElse(EMPTY));
154 | buf.put(groupSid.map(SecurityIdentifier::toByteArray).orElse(EMPTY));
155 | buf.put(sacl.map(AccessControlList::toByteArray).orElse(EMPTY));
156 | buf.put(dacl.map(AccessControlList::toByteArray).orElse(EMPTY));
157 | return buf.array();
158 | }
159 |
160 | @Override
161 | public int sizeOfByteArray() {
162 | return 2 // the first fixed bytes (revision and sbz1)
163 | + 2 // the 16bit big control mask
164 | + 4 * 4 // the 4 32bit integer offset values indicating the offset to the following variable length data fields
165 | + ownerSid.map(SecurityIdentifier::sizeOfByteArray).orElse(0)
166 | + groupSid.map(SecurityIdentifier::sizeOfByteArray).orElse(0)
167 | + sacl.map(AccessControlList::sizeOfByteArray).orElse(0)
168 | + dacl.map(AccessControlList::sizeOfByteArray).orElse(0);
169 | }
170 |
171 | public static SelfRelativeSecurityDescriptor createEmptySD(MaskValueSet flags) {
172 | if ((flags.intValue() & (SecurityDescriptorControlFlag.DP.intValue() | SecurityDescriptorControlFlag.SP.intValue())) == 0) {
173 | flags.add(SecurityDescriptorControlFlag.SR);
174 | return new SelfRelativeSecurityDescriptor(flags);
175 | } else {
176 | //wrong control flags, abort
177 | return null;
178 | }
179 | }
180 |
181 | public static SelfRelativeSecurityDescriptor createSD(MaskValueSet flags, SecurityIdentifier owner, SecurityIdentifier group, AccessControlList sacl, AccessControlList dacl) {
182 | int controlMask = flags.intValue();
183 | if ((controlMask & SecurityDescriptorControlFlag.DP.intValue()) != 0 && dacl == null) {
184 | //abort
185 | return null;
186 | }
187 | if ((controlMask & SecurityDescriptorControlFlag.SP.intValue()) != 0 && sacl == null) {
188 | //abort
189 | return null;
190 | }
191 | flags.add(SecurityDescriptorControlFlag.SR);
192 | return new SelfRelativeSecurityDescriptor(flags, owner, group, sacl, dacl);
193 |
194 |
195 | }
196 |
197 | }
198 |
--------------------------------------------------------------------------------
/src/test/java/dev/dokan/dokan_java/examples/DirListingFSTest.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.examples;
2 |
3 | import dev.dokan.dokan_java.FileSystemInformation;
4 | import dev.dokan.dokan_java.constants.dokany.MountOption;
5 | import dev.dokan.dokan_java.constants.microsoft.FileSystemFlag;
6 | import dev.dokan.dokan_java.masking.MaskValueSet;
7 |
8 |
9 | import java.io.IOException;
10 | import java.nio.file.Path;
11 | import java.nio.file.Paths;
12 |
13 | public class DirListingFSTest {
14 |
15 |
16 | public static void main(String[] args) {
17 | System.out.println("Starting Dokany MirrorFS");
18 |
19 | Path mountPoint = Path.of("M:\\mnt\\");
20 | MaskValueSet mountOptions = MaskValueSet.of(MountOption.STD_ERR_OUTPUT, MountOption.WRITE_PROTECTION, MountOption.CURRENT_SESSION);
21 |
22 | MaskValueSet fsFeatures = MaskValueSet.of(FileSystemFlag.READ_ONLY_VOLUME, FileSystemFlag.CASE_PRESERVED_NAMES);
23 | FileSystemInformation fsInfo = new FileSystemInformation(fsFeatures);
24 | try (DirListingFileSystem fs = new DirListingFileSystem(Paths.get("M:\\test"), fsInfo)) {
25 | fs.mount(mountPoint, mountOptions);
26 | System.in.read();
27 | } catch (IOException e) {
28 | e.printStackTrace();
29 | }
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/test/java/dev/dokan/dokan_java/masking/EnumIntegerTest.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.masking;
2 |
3 | import org.junit.jupiter.api.Assertions;
4 | import org.junit.jupiter.api.Test;
5 |
6 | public class EnumIntegerTest {
7 |
8 | @Test
9 | void FromIntegerOverEnumToInteger() {
10 | int val = 4096;
11 | Assertions.assertEquals(val, EnumInteger.enumFromInt(val, TestEnum.values()).intValue());
12 | }
13 |
14 | @Test
15 | void NotExistingIntThrowsException() {
16 | int val = 2;
17 | Assertions.assertThrows(IllegalArgumentException.class, () -> EnumInteger.enumFromInt(val, TestEnum.values()));
18 | }
19 |
20 | enum TestEnum implements EnumInteger {
21 | A(1),
22 | B(4096);
23 |
24 | private final int val;
25 |
26 | TestEnum(int val) {
27 | this.val = val;
28 | }
29 |
30 | @Override
31 | public int intValue() {
32 | return val;
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/test/java/dev/dokan/dokan_java/masking/MaskValueSetTest.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.masking;
2 |
3 | import org.junit.jupiter.api.Assertions;
4 | import org.junit.jupiter.api.Test;
5 |
6 | public class MaskValueSetTest {
7 |
8 | @Test
9 | void fromIntOverEnumIntegerSetToInt() {
10 | int val = TestEnum.A.intValue() | TestEnum.B.intValue() | TestEnum.C.intValue();
11 | MaskValueSet testSet = MaskValueSet.maskValueSet(val, TestEnum.values());
12 | Assertions.assertFalse(testSet.contains(TestEnum.D));
13 | Assertions.assertTrue(testSet.contains(TestEnum.A));
14 | Assertions.assertTrue(testSet.contains(TestEnum.B));
15 | Assertions.assertTrue(testSet.contains(TestEnum.C));
16 | Assertions.assertEquals(val, testSet.intValue());
17 | }
18 |
19 | enum TestEnum implements MaskValueEnum {
20 | A(0x01),
21 | B(0x08),
22 | C(0x110),
23 | D(0x200);
24 |
25 | private final int val;
26 |
27 | TestEnum(int val) {
28 | this.val = val;
29 | }
30 |
31 | @Override
32 | public int intValue() {
33 | return val;
34 | }
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/test/java/dev/dokan/dokan_java/structure/filesecurity/SelfRelativeSecurityDescriptorTest.java:
--------------------------------------------------------------------------------
1 | package dev.dokan.dokan_java.structure.filesecurity;
2 |
3 |
4 | import dev.dokan.dokan_java.constants.microsoft.accessmaskflags.BasicAccessMaskFlag;
5 | import dev.dokan.dokan_java.constants.microsoft.filesecurity.AccessControlEntryFlag;
6 | import dev.dokan.dokan_java.constants.microsoft.filesecurity.SecurityDescriptorControlFlag;
7 | import dev.dokan.dokan_java.constants.microsoft.filesecurity.SidIdentifierAuthority;
8 | import dev.dokan.dokan_java.masking.MaskValueSet;
9 | import org.junit.jupiter.api.Assertions;
10 | import org.junit.jupiter.api.Test;
11 |
12 | import java.nio.ByteBuffer;
13 | import java.util.ArrayList;
14 | import java.util.Arrays;
15 | import java.util.Collections;
16 |
17 | /**
18 | * TODO: the harcoded byte arrays are not directly bind to the methods, so changing some sid constant or something like this can lead to failures!
19 | * TODO: combine the selection of sid/whatever and the hardcoded array in one method
20 | *
21 | * TODO: refactor in suhc a way, that all test methods for a specific class has an own test class
22 | */
23 | public class SelfRelativeSecurityDescriptorTest {
24 |
25 | @Test
26 | public void testControlField() {
27 | MaskValueSet control = MaskValueSet.of(SecurityDescriptorControlFlag.GD, SecurityDescriptorControlFlag.OD, SecurityDescriptorControlFlag.DD, SecurityDescriptorControlFlag.SD);
28 | ByteBuffer buf = ByteBuffer.allocate(2);
29 |
30 | Assertions.assertEquals((43 << 8 + 0) << 16, Integer.reverseBytes(control.intValue()));
31 | Assertions.assertEquals((43 << 8 + 0), Short.reverseBytes((short) control.intValue()));
32 | Assertions.assertArrayEquals(new byte[]{43, 0}, buf.putShort(Short.reverseBytes((short) control.intValue())).array());
33 | }
34 |
35 | @Test
36 | public void testSidWithoutSubAuthorities() {
37 | SecurityIdentifier sid = new SecurityIdentifier(SidIdentifierAuthority.WORLD_SID_AUTHORITY, null);
38 | byte[] expected = new byte[]{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
39 | Assertions.assertArrayEquals(expected, sid.toByteArray());
40 | }
41 |
42 | @Test
43 | public void testValidEveryoneSid() {
44 | SecurityIdentifier sid = new SecurityIdentifier(SidIdentifierAuthority.WORLD_SID_AUTHORITY, Collections.singletonList(0));
45 | Assertions.assertArrayEquals(getEveryoneSid(), sid.toByteArray());
46 | }
47 |
48 | @Test
49 | public void testValidBuiltinAdminitstratorsSid() {
50 | ArrayList subAuths = new ArrayList<>(2);
51 | subAuths.add(32);
52 | subAuths.add(544);
53 | SecurityIdentifier sid = new SecurityIdentifier(SidIdentifierAuthority.SECURITY_NT_AUTHORITY, subAuths);
54 | Assertions.assertArrayEquals(new byte[]{0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00}, sid.toByteArray());
55 | }
56 |
57 | @Test
58 | public void testSidFromString() {
59 | SecurityIdentifier sid = SecurityIdentifier.fromString("S-1-5-32-544");
60 | Assertions.assertArrayEquals(new byte[]{0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00}, sid.toByteArray());
61 | }
62 |
63 | @Test
64 | public void testAccessAllowedACE() {
65 | //set the flag
66 | MaskValueSet flags = MaskValueSet.of(AccessControlEntryFlag.CONTAINER_INHERIT_ACE, AccessControlEntryFlag.OBJECT_INHERIT_ACE);
67 | //set the mask
68 | MaskValueSet mask = MaskValueSet.of(BasicAccessMaskFlag.GENERIC_ALL);
69 | //set the sid to world sid resp. everyone
70 | SecurityIdentifier sid = SecurityIdentifier.fromString("S-1-1-0");// everyone sid
71 | //create ace
72 | AccessAllowedACE allowedACE = new AccessAllowedACE(flags, sid, mask);
73 | Assertions.assertArrayEquals(getAllowedAccessACE(), allowedACE.toByteArray());
74 | }
75 |
76 | @Test
77 | public void testACL() {
78 | //test empty DACL rev2
79 | AccessControlList emptyDaclRev2 = AccessControlList.createDaclRevision2(new ArrayList<>(0));
80 | Assertions.assertArrayEquals(new byte[]{0x02, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}, emptyDaclRev2.toByteArray());
81 | //test empty DACL rev4
82 | AccessControlList emptyDaclRev4 = AccessControlList.createDaclRevision4(new ArrayList<>(0));
83 | Assertions.assertArrayEquals(new byte[]{0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}, emptyDaclRev4.toByteArray());
84 | //test empty SACL rev2
85 | AccessControlList emptySaclRev2 = AccessControlList.createSaclRevision2(new ArrayList<>(0));
86 | Assertions.assertArrayEquals(new byte[]{0x02, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}, emptySaclRev2.toByteArray());
87 | //test empty SACL rev4
88 | AccessControlList emptySaclRev4 = AccessControlList.createSaclRevision4(new ArrayList<>(0));
89 | Assertions.assertArrayEquals(new byte[]{0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}, emptySaclRev4.toByteArray());
90 |
91 | //test DACL rev2 with accessAllowACE
92 | MaskValueSet flags = MaskValueSet.of(AccessControlEntryFlag.CONTAINER_INHERIT_ACE, AccessControlEntryFlag.OBJECT_INHERIT_ACE);
93 | //set the mask
94 | MaskValueSet mask = MaskValueSet.of(BasicAccessMaskFlag.GENERIC_ALL);
95 | //set the sid to world sid resp. everyone
96 | SecurityIdentifier sid = SecurityIdentifier.fromString("S-1-1-0");
97 | //create ace
98 | AccessControlList daclRev2WithAccessAllow = AccessControlList.createDaclRevision2(Collections.singletonList(new AccessAllowedACE(flags, sid, mask)));
99 |
100 | Assertions.assertArrayEquals(getAclWithAAAce(), daclRev2WithAccessAllow.toByteArray());
101 | }
102 |
103 | @Test
104 | public void testEmptySecurityDescriptor() {
105 | MaskValueSet flags = MaskValueSet.of(SecurityDescriptorControlFlag.GD, SecurityDescriptorControlFlag.OD, SecurityDescriptorControlFlag.DD, SecurityDescriptorControlFlag.SD);
106 | byte[] expected = getEmptySelfRelativeSecurityDescriptorWithEmptyFlags();
107 | expected[2] = 43;
108 | Assertions.assertArrayEquals(expected, SelfRelativeSecurityDescriptor.createEmptySD(flags).toByteArray());
109 | }
110 |
111 | @Test
112 | public void testOwnerAndGroupSD() {
113 | //control
114 | MaskValueSet control = MaskValueSet.of(SecurityDescriptorControlFlag.SR, SecurityDescriptorControlFlag.SD, SecurityDescriptorControlFlag.DD);
115 | //owner
116 | SecurityIdentifier oSid = SecurityIdentifier.fromString("S-1-1-0");
117 | //group
118 | SecurityIdentifier gSid = SecurityIdentifier.fromString("S-1-1-0");
119 | //security descriptor
120 | SelfRelativeSecurityDescriptor sd = SelfRelativeSecurityDescriptor.createSD(control, oSid, gSid, null, null);
121 |
122 | //our expected stuff
123 | byte[] emptySD = getEmptySelfRelativeSecurityDescriptorWithEmptyFlags();
124 | emptySD[2] = 40; //we defaulting only sacl and dacl
125 | emptySD[4] = 20; //offsets
126 | emptySD[8] = 32;
127 | byte[] sid = getEveryoneSid();
128 | byte[] expected = concat(concat(emptySD, sid), sid);
129 |
130 | Assertions.assertArrayEquals(expected, sd.toByteArray());
131 | }
132 |
133 | @Test
134 | public void testSDWithOwnerGroupAndDacl() {
135 | //control
136 | MaskValueSet control = MaskValueSet.of(SecurityDescriptorControlFlag.SR, SecurityDescriptorControlFlag.DP, SecurityDescriptorControlFlag.SD);
137 | //owner
138 | SecurityIdentifier oSid = SecurityIdentifier.fromString("S-1-1-0");
139 | SecurityIdentifier gSid = SecurityIdentifier.fromString("S-1-1-0");
140 | //ace
141 | //ace control
142 | MaskValueSet flags = MaskValueSet.of(AccessControlEntryFlag.CONTAINER_INHERIT_ACE, AccessControlEntryFlag.OBJECT_INHERIT_ACE);
143 | //set the mask
144 | MaskValueSet mask = MaskValueSet.of(BasicAccessMaskFlag.GENERIC_ALL);
145 | //create ace
146 | AccessControlList daclRev2WithAccessAllow = AccessControlList.createDaclRevision2(Collections.singletonList(new AccessAllowedACE(flags, oSid, mask)));
147 |
148 | SelfRelativeSecurityDescriptor sd = SelfRelativeSecurityDescriptor.createSD(control, oSid, gSid, null, daclRev2WithAccessAllow);
149 |
150 | //our expected stuff
151 | byte[] emptySD = getEmptySelfRelativeSecurityDescriptorWithEmptyFlags();
152 | emptySD[2] = 36; //we defaulting only sacl and dacl is present!
153 | emptySD[4] = 20; //offsets
154 | emptySD[8] = 32;
155 | emptySD[16] = 44;
156 | byte[] sid = getEveryoneSid();
157 | byte[] acl = new byte[]{
158 | 0x02, //revision
159 | 0x00, //sbz1
160 | 0x1C,
161 | 0x00, //size
162 | 0x01, 0x00, //count
163 | 0x00, 0x00, //sbz2
164 | 0x00, // ace Type
165 | 0x03, //ace flags
166 | 0x14, 0x00, //ace size
167 | 0x00, 0x00, 0x00, 0x10, //access mask
168 | 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00};
169 | byte[] expected = concat(concat(concat(emptySD, sid), sid), acl);
170 | Assertions.assertArrayEquals(expected, sd.toByteArray());
171 | }
172 |
173 | private static byte[] getEmptySelfRelativeSecurityDescriptorWithEmptyFlags() {
174 | return new byte[]{
175 | 0x01, //revision
176 | 0x00, //sbz1
177 | 0x00,// second half of control flag
178 | -128, //first half indicating a self relative sec. desc.
179 | 0x00,
180 | 0x00,
181 | 0x00,
182 | 0x00, //owner offset
183 | 0x00,
184 | 0x00,
185 | 0x00,
186 | 0x00, //group offset
187 | 0x00,
188 | 0x00,
189 | 0x00,
190 | 0x00, //sacl offset
191 | 0x00,
192 | 0x00,
193 | 0x00,
194 | 0x00, //dacl offset
195 | };
196 | }
197 |
198 | private static byte[] getEveryoneSid() {
199 | return new byte[]{0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00};
200 | }
201 |
202 | private static byte[] getAllowedAccessACE() {
203 | return new byte[]{
204 | 0x00, // ace Type
205 | 0x03, //ace flags
206 | 0x14, 0x00, //ace size
207 | 0x00, 0x00, 0x00, 0x10, //access mask
208 | 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00};
209 | }
210 |
211 | private static byte[] getAclWithAAAce() {
212 | return concat(new byte[]{
213 | 0x02, //revision
214 | 0x00, //sbz1
215 | 0x1C,
216 | 0x00, //size
217 | 0x01, 0x00, //count
218 | 0x00, 0x00 //sbz2
219 | }, getAllowedAccessACE());
220 | }
221 |
222 | private static byte[] getSDWithDaclWithAAAce() {
223 | byte[] tmp = concat(new byte[]{
224 | 0x01, //revision
225 | 0x00, //sbz1
226 | 0x01,// first half of control flag indicating a self relative sec. desc.
227 | 36, //second half indicating sacl defaulted and dacl present
228 | 0x10,
229 | 0x00,
230 | 0x00,
231 | 0x00, //owner offset (this header = 16 Byte)
232 | 0x18,
233 | 0x00,
234 | 0x00,
235 | 0x00, //group offset (header + owner = 16 + 8)
236 | 0x00,
237 | 0x00,
238 | 0x00,
239 | 0x00, //sacl offset (zero)
240 | 0x20,
241 | 0x00,
242 | 0x00,
243 | 0x00, //dacl offset (header +owner +group = 16+8+8)
244 | }, getEveryoneSid());
245 | tmp = concat(tmp, getEveryoneSid());
246 | return concat(tmp, getAclWithAAAce());
247 | }
248 |
249 | private static byte[] concat(byte[] arr1, byte[] arr2) {
250 | byte[] arr3 = Arrays.copyOf(arr1, arr1.length + arr2.length);
251 | for (int i = 0; i < arr2.length; i++) {
252 | arr3[i + arr1.length] = arr2[i];
253 | }
254 | return arr3;
255 | }
256 |
257 | }
258 |
--------------------------------------------------------------------------------