aboutsummaryrefslogtreecommitdiffstats
path: root/features/vfat/FAT-Add-CONFIG_VFAT_FS_NO_DUALNAMES-option.patch
blob: 7662b04490fe372290f1a5212991f8e0bad07f32 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
From 90c504b1911b52eb917860195f849eddb8fff7c4 Mon Sep 17 00:00:00 2001
From: Andrew Tridgell <tridge@samba.org>
Date: Tue, 21 Jul 2009 09:59:29 +1000
Subject: [PATCH] FAT: Add CONFIG_VFAT_FS_NO_DUALNAMES option

This patch adds a new config option VFAT_FS_NO_DUALNAMES. When
VFAT_FS_NO_DUALNAMES is enabled, VFAT will never create a file with
both a long and a short filename. If a long filename is needed, then
dummy bytes are stored in the 8.3 entry, and the 8.3 entry is marked
as having no short name using the FAT_NO_83NAME flag.

Signed-off-by: Andrew Tridgell <tridge@samba.org>
---
 fs/fat/Kconfig      | 21 ++++++++++++++
 fs/fat/namei_vfat.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 102 insertions(+), 2 deletions(-)

diff --git a/fs/fat/Kconfig b/fs/fat/Kconfig
index 3ff1772f612e..1f103d835e8c 100644
--- a/fs/fat/Kconfig
+++ b/fs/fat/Kconfig
@@ -74,6 +74,27 @@ config VFAT_FS
 	  To compile this as a module, choose M here: the module will be called
 	  vfat.
 
+config VFAT_FS_NO_DUALNAMES
+	bool "disable VFAT dual names support (patent workaround)"
+	depends on VFAT_FS
+	help
+	  This option disables support for dual filenames on VFAT filesystems.
+	  If this option is enabled then file creation will either put
+	  a short (8.3) name or a long name on the file, but never both.
+	  The field where a shortname would normally go is filled with
+	  invalid characters such that it cannot be considered a valid
+	  short filename.
+
+	  That means that long filenames created with this option
+	  disabled will not be accessible at all to operating systems
+	  that do not understand the FAT long filename extensions.
+
+	  Users considering disabling this option should consider the
+	  implications of any patents that may exist on dual filenames
+	  in VFAT.
+
+	  If unsure, say N
+
 config FAT_DEFAULT_CODEPAGE
 	int "Default codepage for FAT"
 	depends on MSDOS_FS || VFAT_FS
diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
index 2649759c478a..a5c3b6cd435a 100644
--- a/fs/fat/namei_vfat.c
+++ b/fs/fat/namei_vfat.c
@@ -21,6 +21,7 @@
 #include <linux/namei.h>
 #include <linux/kernel.h>
 #include <linux/iversion.h>
+#include <linux/random.h>
 #include "fat.h"
 
 static inline unsigned long vfat_d_version(struct dentry *dentry)
@@ -335,6 +336,17 @@ static int vfat_create_shortname(struct inode *dir, struct nls_table *nls,
 	int sz = 0, extlen, baselen, i, numtail_baselen, numtail2_baselen;
 	int is_shortname;
 	struct shortname_info base_info, ext_info;
+	unsigned opts_shortname = opts->shortname;
+
+#ifdef CONFIG_VFAT_FS_NO_DUALNAMES
+	/*
+	 * When we do not have dualnames, we want to maximise the
+	 * chance that a file will be able to be represented with just
+	 * a 8.3 entry. We can do that by using the WINNT case
+	 * handling extensions to FAT.
+	 */
+	opts_shortname = VFAT_SFN_CREATE_WINNT;
+#endif
 
 	is_shortname = 1;
 	INIT_SHORTNAME_INFO(&base_info);
@@ -447,9 +459,9 @@ static int vfat_create_shortname(struct inode *dir, struct nls_table *nls,
 		if (vfat_find_form(dir, name_res) == 0)
 			return -EEXIST;
 
-		if (opts->shortname & VFAT_SFN_CREATE_WIN95) {
+		if (opts_shortname & VFAT_SFN_CREATE_WIN95) {
 			return (base_info.upper && ext_info.upper);
-		} else if (opts->shortname & VFAT_SFN_CREATE_WINNT) {
+		} else if (opts_shortname & VFAT_SFN_CREATE_WINNT) {
 			if ((base_info.upper || base_info.lower) &&
 			    (ext_info.upper || ext_info.lower)) {
 				if (!base_info.upper && base_info.lower)
@@ -575,6 +587,66 @@ xlate_to_uni(const unsigned char *name, int len, unsigned char *outname,
 	return 0;
 }
 
+#ifdef CONFIG_VFAT_FS_NO_DUALNAMES
+/*
+ * This function creates a dummy 8.3 entry which is as compatible as
+ * possible with existing FAT devices, while not being a valid
+ * filename under windows or Linux
+ */
+static void vfat_build_dummy_83_buffer(struct inode *dir, char *msdos_name,
+				       int is_dir)
+{
+	/*
+	 * These characters are all invalid in 8.3 names, plus have
+	 * been shown to be harmless on all tested devices
+	 */
+	const char invalidchar[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x0B,
+				     0x0C, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13,
+				     0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A,
+				     0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x22, 0x2a,
+				     0x3a, 0x3c, 0x3e, 0x3f, 0x5b, 0x5d, 0x7c };
+	int i, tilde_pos, slash_pos;
+	u32 rand_num = random32();
+
+	/* We need a '~' in the prefix to make Win98 happy. */
+	tilde_pos = rand_num % 8;
+	rand_num >>= 3;
+
+	/*
+	 * the '/' makes sure that even unpatched Linux systems can't
+	 * get at files by the 8.3 entry. Don't put in a / in
+	 * directories as it can cause problems with some
+	 * photo frames
+	 */
+	if (is_dir)
+		slash_pos = -1;
+	else {
+		slash_pos = (tilde_pos + 1 + rand_num % 7) % 8;
+		rand_num >>= 3;
+	}
+
+	/*
+	 * fill in the first 8 bytes with invalid characters. Note
+	 * that we need to be careful not to run out of randomness. We
+	 * leave the 3 byte extension in place as some cheap MP3
+	 * players need them.
+	 */
+	for (i = 0; i < 8; i++) {
+		if (i == tilde_pos)
+			msdos_name[i] = '~';
+		else if (i == slash_pos)
+			msdos_name[i] = '/';
+		else {
+			msdos_name[i] =
+				invalidchar[rand_num % sizeof(invalidchar)];
+			rand_num /= sizeof(invalidchar);
+			if (rand_num < sizeof(invalidchar))
+				rand_num = random32();
+		}
+	}
+}
+#endif
+
 static int vfat_build_slots(struct inode *dir, const unsigned char *name,
 			    int len, int is_dir, int cluster,
 			    struct timespec *ts,
@@ -617,6 +689,13 @@ static int vfat_build_slots(struct inode *dir, const unsigned char *name,
 		goto shortname;
 	}
 
+#ifdef CONFIG_VFAT_FS_NO_DUALNAMES
+	printk_once(KERN_INFO
+		    "VFAT: not creating 8.3 short filenames for long names\n");
+	vfat_build_dummy_83_buffer(dir, msdos_name, is_dir);
+	lcase = FAT_NO_83NAME;
+#endif
+
 	/* build the entry of long file name */
 	cksum = fat_checksum(msdos_name);
 
-- 
2.5.0