1 |
1275 |
phoenix |
/*
|
2 |
|
|
* linux/fs/msdos/namei.c
|
3 |
|
|
*
|
4 |
|
|
* Written 1992,1993 by Werner Almesberger
|
5 |
|
|
* Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
|
6 |
|
|
* Rewritten for constant inumbers 1999 by Al Viro
|
7 |
|
|
*/
|
8 |
|
|
|
9 |
|
|
|
10 |
|
|
#define __NO_VERSION__
|
11 |
|
|
#include <linux/module.h>
|
12 |
|
|
|
13 |
|
|
#include <linux/sched.h>
|
14 |
|
|
#include <linux/msdos_fs.h>
|
15 |
|
|
#include <linux/errno.h>
|
16 |
|
|
#include <linux/string.h>
|
17 |
|
|
|
18 |
|
|
#include <asm/uaccess.h>
|
19 |
|
|
|
20 |
|
|
#define MSDOS_DEBUG 0
|
21 |
|
|
#define PRINTK(x)
|
22 |
|
|
|
23 |
|
|
/* MS-DOS "device special files" */
|
24 |
|
|
|
25 |
|
|
static const char *reserved_names[] = {
|
26 |
|
|
"CON ","PRN ","NUL ","AUX ",
|
27 |
|
|
"LPT1 ","LPT2 ","LPT3 ","LPT4 ",
|
28 |
|
|
"COM1 ","COM2 ","COM3 ","COM4 ",
|
29 |
|
|
NULL };
|
30 |
|
|
|
31 |
|
|
|
32 |
|
|
/* Characters that are undesirable in an MS-DOS file name */
|
33 |
|
|
|
34 |
|
|
static char bad_chars[] = "*?<>|\"";
|
35 |
|
|
static char bad_if_strict_pc[] = "+=,; ";
|
36 |
|
|
static char bad_if_strict_atari[] = " "; /* GEMDOS is less restrictive */
|
37 |
|
|
#define bad_if_strict(opts) ((opts)->atari ? bad_if_strict_atari : bad_if_strict_pc)
|
38 |
|
|
|
39 |
|
|
/* Must die */
|
40 |
|
|
void msdos_put_super(struct super_block *sb)
|
41 |
|
|
{
|
42 |
|
|
fat_put_super(sb);
|
43 |
|
|
}
|
44 |
|
|
|
45 |
|
|
/***** Formats an MS-DOS file name. Rejects invalid names. */
|
46 |
|
|
static int msdos_format_name(const char *name,int len,
|
47 |
|
|
char *res,struct fat_mount_options *opts)
|
48 |
|
|
/* name is the proposed name, len is its length, res is
|
49 |
|
|
* the resulting name, opts->name_check is either (r)elaxed,
|
50 |
|
|
* (n)ormal or (s)trict, opts->dotsOK allows dots at the
|
51 |
|
|
* beginning of name (for hidden files)
|
52 |
|
|
*/
|
53 |
|
|
{
|
54 |
|
|
char *walk;
|
55 |
|
|
const char **reserved;
|
56 |
|
|
unsigned char c;
|
57 |
|
|
int space;
|
58 |
|
|
|
59 |
|
|
if (name[0] == '.') { /* dotfile because . and .. already done */
|
60 |
|
|
if (opts->dotsOK) {
|
61 |
|
|
/* Get rid of dot - test for it elsewhere */
|
62 |
|
|
name++; len--;
|
63 |
|
|
}
|
64 |
|
|
else if (!opts->atari) return -EINVAL;
|
65 |
|
|
}
|
66 |
|
|
/* disallow names that _really_ start with a dot for MS-DOS, GEMDOS does
|
67 |
|
|
* not care */
|
68 |
|
|
space = !opts->atari;
|
69 |
|
|
c = 0;
|
70 |
|
|
for (walk = res; len && walk-res < 8; walk++) {
|
71 |
|
|
c = *name++;
|
72 |
|
|
len--;
|
73 |
|
|
if (opts->name_check != 'r' && strchr(bad_chars,c))
|
74 |
|
|
return -EINVAL;
|
75 |
|
|
if (opts->name_check == 's' && strchr(bad_if_strict(opts),c))
|
76 |
|
|
return -EINVAL;
|
77 |
|
|
if (c >= 'A' && c <= 'Z' && opts->name_check == 's')
|
78 |
|
|
return -EINVAL;
|
79 |
|
|
if (c < ' ' || c == ':' || c == '\\') return -EINVAL;
|
80 |
|
|
/* 0xE5 is legal as a first character, but we must substitute 0x05 */
|
81 |
|
|
/* because 0xE5 marks deleted files. Yes, DOS really does this. */
|
82 |
|
|
/* It seems that Microsoft hacked DOS to support non-US characters */
|
83 |
|
|
/* after the 0xE5 character was already in use to mark deleted files. */
|
84 |
|
|
if((res==walk) && (c==0xE5)) c=0x05;
|
85 |
|
|
if (c == '.') break;
|
86 |
|
|
space = (c == ' ');
|
87 |
|
|
*walk = (!opts->nocase && c >= 'a' && c <= 'z') ? c-32 : c;
|
88 |
|
|
}
|
89 |
|
|
if (space) return -EINVAL;
|
90 |
|
|
if (opts->name_check == 's' && len && c != '.') {
|
91 |
|
|
c = *name++;
|
92 |
|
|
len--;
|
93 |
|
|
if (c != '.') return -EINVAL;
|
94 |
|
|
}
|
95 |
|
|
while (c != '.' && len--) c = *name++;
|
96 |
|
|
if (c == '.') {
|
97 |
|
|
while (walk-res < 8) *walk++ = ' ';
|
98 |
|
|
while (len > 0 && walk-res < MSDOS_NAME) {
|
99 |
|
|
c = *name++;
|
100 |
|
|
len--;
|
101 |
|
|
if (opts->name_check != 'r' && strchr(bad_chars,c))
|
102 |
|
|
return -EINVAL;
|
103 |
|
|
if (opts->name_check == 's' &&
|
104 |
|
|
strchr(bad_if_strict(opts),c))
|
105 |
|
|
return -EINVAL;
|
106 |
|
|
if (c < ' ' || c == ':' || c == '\\')
|
107 |
|
|
return -EINVAL;
|
108 |
|
|
if (c == '.') {
|
109 |
|
|
if (opts->name_check == 's')
|
110 |
|
|
return -EINVAL;
|
111 |
|
|
break;
|
112 |
|
|
}
|
113 |
|
|
if (c >= 'A' && c <= 'Z' && opts->name_check == 's')
|
114 |
|
|
return -EINVAL;
|
115 |
|
|
space = c == ' ';
|
116 |
|
|
*walk++ = (!opts->nocase && c >= 'a' && c <= 'z') ? c-32 : c;
|
117 |
|
|
}
|
118 |
|
|
if (space) return -EINVAL;
|
119 |
|
|
if (opts->name_check == 's' && len) return -EINVAL;
|
120 |
|
|
}
|
121 |
|
|
while (walk-res < MSDOS_NAME) *walk++ = ' ';
|
122 |
|
|
if (!opts->atari)
|
123 |
|
|
/* GEMDOS is less stupid and has no reserved names */
|
124 |
|
|
for (reserved = reserved_names; *reserved; reserved++)
|
125 |
|
|
if (!strncmp(res,*reserved,8)) return -EINVAL;
|
126 |
|
|
return 0;
|
127 |
|
|
}
|
128 |
|
|
|
129 |
|
|
/***** Locates a directory entry. Uses unformatted name. */
|
130 |
|
|
static int msdos_find(struct inode *dir, const char *name, int len,
|
131 |
|
|
struct buffer_head **bh, struct msdos_dir_entry **de,
|
132 |
|
|
loff_t *i_pos)
|
133 |
|
|
{
|
134 |
|
|
int res;
|
135 |
|
|
char dotsOK;
|
136 |
|
|
char msdos_name[MSDOS_NAME];
|
137 |
|
|
|
138 |
|
|
dotsOK = MSDOS_SB(dir->i_sb)->options.dotsOK;
|
139 |
|
|
res = msdos_format_name(name,len, msdos_name,&MSDOS_SB(dir->i_sb)->options);
|
140 |
|
|
if (res < 0)
|
141 |
|
|
return -ENOENT;
|
142 |
|
|
res = fat_scan(dir, msdos_name, bh, de, i_pos);
|
143 |
|
|
if (!res && dotsOK) {
|
144 |
|
|
if (name[0]=='.') {
|
145 |
|
|
if (!((*de)->attr & ATTR_HIDDEN))
|
146 |
|
|
res = -ENOENT;
|
147 |
|
|
} else {
|
148 |
|
|
if ((*de)->attr & ATTR_HIDDEN)
|
149 |
|
|
res = -ENOENT;
|
150 |
|
|
}
|
151 |
|
|
}
|
152 |
|
|
return res;
|
153 |
|
|
}
|
154 |
|
|
|
155 |
|
|
/*
|
156 |
|
|
* Compute the hash for the msdos name corresponding to the dentry.
|
157 |
|
|
* Note: if the name is invalid, we leave the hash code unchanged so
|
158 |
|
|
* that the existing dentry can be used. The msdos fs routines will
|
159 |
|
|
* return ENOENT or EINVAL as appropriate.
|
160 |
|
|
*/
|
161 |
|
|
static int msdos_hash(struct dentry *dentry, struct qstr *qstr)
|
162 |
|
|
{
|
163 |
|
|
struct fat_mount_options *options = & (MSDOS_SB(dentry->d_sb)->options);
|
164 |
|
|
int error;
|
165 |
|
|
char msdos_name[MSDOS_NAME];
|
166 |
|
|
|
167 |
|
|
error = msdos_format_name(qstr->name, qstr->len, msdos_name, options);
|
168 |
|
|
if (!error)
|
169 |
|
|
qstr->hash = full_name_hash(msdos_name, MSDOS_NAME);
|
170 |
|
|
return 0;
|
171 |
|
|
}
|
172 |
|
|
|
173 |
|
|
/*
|
174 |
|
|
* Compare two msdos names. If either of the names are invalid,
|
175 |
|
|
* we fall back to doing the standard name comparison.
|
176 |
|
|
*/
|
177 |
|
|
static int msdos_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
|
178 |
|
|
{
|
179 |
|
|
struct fat_mount_options *options = & (MSDOS_SB(dentry->d_sb)->options);
|
180 |
|
|
int error;
|
181 |
|
|
char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME];
|
182 |
|
|
|
183 |
|
|
error = msdos_format_name(a->name, a->len, a_msdos_name, options);
|
184 |
|
|
if (error)
|
185 |
|
|
goto old_compare;
|
186 |
|
|
error = msdos_format_name(b->name, b->len, b_msdos_name, options);
|
187 |
|
|
if (error)
|
188 |
|
|
goto old_compare;
|
189 |
|
|
error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME);
|
190 |
|
|
out:
|
191 |
|
|
return error;
|
192 |
|
|
|
193 |
|
|
old_compare:
|
194 |
|
|
error = 1;
|
195 |
|
|
if (a->len == b->len)
|
196 |
|
|
error = memcmp(a->name, b->name, a->len);
|
197 |
|
|
goto out;
|
198 |
|
|
}
|
199 |
|
|
|
200 |
|
|
|
201 |
|
|
static struct dentry_operations msdos_dentry_operations = {
|
202 |
|
|
d_hash: msdos_hash,
|
203 |
|
|
d_compare: msdos_cmp,
|
204 |
|
|
};
|
205 |
|
|
|
206 |
|
|
/*
|
207 |
|
|
* AV. Wrappers for FAT sb operations. Is it wise?
|
208 |
|
|
*/
|
209 |
|
|
|
210 |
|
|
/***** Get inode using directory and name */
|
211 |
|
|
struct dentry *msdos_lookup(struct inode *dir,struct dentry *dentry)
|
212 |
|
|
{
|
213 |
|
|
struct super_block *sb = dir->i_sb;
|
214 |
|
|
struct inode *inode = NULL;
|
215 |
|
|
struct msdos_dir_entry *de;
|
216 |
|
|
struct buffer_head *bh = NULL;
|
217 |
|
|
loff_t i_pos;
|
218 |
|
|
int res;
|
219 |
|
|
|
220 |
|
|
PRINTK (("msdos_lookup\n"));
|
221 |
|
|
|
222 |
|
|
dentry->d_op = &msdos_dentry_operations;
|
223 |
|
|
|
224 |
|
|
res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &bh,
|
225 |
|
|
&de, &i_pos);
|
226 |
|
|
if (res == -ENOENT)
|
227 |
|
|
goto add;
|
228 |
|
|
if (res < 0)
|
229 |
|
|
goto out;
|
230 |
|
|
inode = fat_build_inode(sb, de, i_pos, &res);
|
231 |
|
|
if (res)
|
232 |
|
|
goto out;
|
233 |
|
|
add:
|
234 |
|
|
d_add(dentry, inode);
|
235 |
|
|
res = 0;
|
236 |
|
|
out:
|
237 |
|
|
if (bh)
|
238 |
|
|
fat_brelse(sb, bh);
|
239 |
|
|
return ERR_PTR(res);
|
240 |
|
|
}
|
241 |
|
|
|
242 |
|
|
/***** Creates a directory entry (name is already formatted). */
|
243 |
|
|
static int msdos_add_entry(struct inode *dir, const char *name,
|
244 |
|
|
struct buffer_head **bh,
|
245 |
|
|
struct msdos_dir_entry **de,
|
246 |
|
|
loff_t *i_pos, int is_dir, int is_hid)
|
247 |
|
|
{
|
248 |
|
|
struct super_block *sb = dir->i_sb;
|
249 |
|
|
int res;
|
250 |
|
|
|
251 |
|
|
res = fat_add_entries(dir, 1, bh, de, i_pos);
|
252 |
|
|
if (res < 0)
|
253 |
|
|
return res;
|
254 |
|
|
/*
|
255 |
|
|
* XXX all times should be set by caller upon successful completion.
|
256 |
|
|
*/
|
257 |
|
|
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
|
258 |
|
|
mark_inode_dirty(dir);
|
259 |
|
|
memcpy((*de)->name,name,MSDOS_NAME);
|
260 |
|
|
(*de)->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
|
261 |
|
|
if (is_hid)
|
262 |
|
|
(*de)->attr |= ATTR_HIDDEN;
|
263 |
|
|
(*de)->start = 0;
|
264 |
|
|
(*de)->starthi = 0;
|
265 |
|
|
fat_date_unix2dos(dir->i_mtime,&(*de)->time,&(*de)->date);
|
266 |
|
|
(*de)->size = 0;
|
267 |
|
|
fat_mark_buffer_dirty(sb, *bh);
|
268 |
|
|
return 0;
|
269 |
|
|
}
|
270 |
|
|
|
271 |
|
|
/*
|
272 |
|
|
* AV. Huh??? It's exported. Oughtta check usage.
|
273 |
|
|
*/
|
274 |
|
|
|
275 |
|
|
/***** Create a file */
|
276 |
|
|
int msdos_create(struct inode *dir,struct dentry *dentry,int mode)
|
277 |
|
|
{
|
278 |
|
|
struct super_block *sb = dir->i_sb;
|
279 |
|
|
struct buffer_head *bh;
|
280 |
|
|
struct msdos_dir_entry *de;
|
281 |
|
|
struct inode *inode;
|
282 |
|
|
loff_t i_pos;
|
283 |
|
|
int res, is_hid;
|
284 |
|
|
char msdos_name[MSDOS_NAME];
|
285 |
|
|
|
286 |
|
|
res = msdos_format_name(dentry->d_name.name,dentry->d_name.len,
|
287 |
|
|
msdos_name, &MSDOS_SB(sb)->options);
|
288 |
|
|
if (res < 0)
|
289 |
|
|
return res;
|
290 |
|
|
is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
|
291 |
|
|
/* Have to do it due to foo vs. .foo conflicts */
|
292 |
|
|
if (fat_scan(dir, msdos_name, &bh, &de, &i_pos) >= 0) {
|
293 |
|
|
fat_brelse(sb, bh);
|
294 |
|
|
return -EINVAL;
|
295 |
|
|
}
|
296 |
|
|
inode = NULL;
|
297 |
|
|
res = msdos_add_entry(dir, msdos_name, &bh, &de, &i_pos, 0, is_hid);
|
298 |
|
|
if (res)
|
299 |
|
|
return res;
|
300 |
|
|
inode = fat_build_inode(dir->i_sb, de, i_pos, &res);
|
301 |
|
|
fat_brelse(sb, bh);
|
302 |
|
|
if (!inode)
|
303 |
|
|
return res;
|
304 |
|
|
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
|
305 |
|
|
mark_inode_dirty(inode);
|
306 |
|
|
d_instantiate(dentry, inode);
|
307 |
|
|
return 0;
|
308 |
|
|
}
|
309 |
|
|
|
310 |
|
|
/***** Remove a directory */
|
311 |
|
|
int msdos_rmdir(struct inode *dir, struct dentry *dentry)
|
312 |
|
|
{
|
313 |
|
|
struct super_block *sb = dir->i_sb;
|
314 |
|
|
struct inode *inode = dentry->d_inode;
|
315 |
|
|
loff_t i_pos;
|
316 |
|
|
int res;
|
317 |
|
|
struct buffer_head *bh;
|
318 |
|
|
struct msdos_dir_entry *de;
|
319 |
|
|
|
320 |
|
|
bh = NULL;
|
321 |
|
|
res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len,
|
322 |
|
|
&bh, &de, &i_pos);
|
323 |
|
|
if (res < 0)
|
324 |
|
|
goto rmdir_done;
|
325 |
|
|
/*
|
326 |
|
|
* Check whether the directory is not in use, then check
|
327 |
|
|
* whether it is empty.
|
328 |
|
|
*/
|
329 |
|
|
res = fat_dir_empty(inode);
|
330 |
|
|
if (res)
|
331 |
|
|
goto rmdir_done;
|
332 |
|
|
|
333 |
|
|
de->name[0] = DELETED_FLAG;
|
334 |
|
|
fat_mark_buffer_dirty(sb, bh);
|
335 |
|
|
fat_detach(inode);
|
336 |
|
|
inode->i_nlink = 0;
|
337 |
|
|
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
|
338 |
|
|
dir->i_nlink--;
|
339 |
|
|
mark_inode_dirty(inode);
|
340 |
|
|
mark_inode_dirty(dir);
|
341 |
|
|
res = 0;
|
342 |
|
|
|
343 |
|
|
rmdir_done:
|
344 |
|
|
fat_brelse(sb, bh);
|
345 |
|
|
return res;
|
346 |
|
|
}
|
347 |
|
|
|
348 |
|
|
/***** Make a directory */
|
349 |
|
|
int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode)
|
350 |
|
|
{
|
351 |
|
|
struct super_block *sb = dir->i_sb;
|
352 |
|
|
struct buffer_head *bh;
|
353 |
|
|
struct msdos_dir_entry *de;
|
354 |
|
|
struct inode *inode;
|
355 |
|
|
int res,is_hid;
|
356 |
|
|
char msdos_name[MSDOS_NAME];
|
357 |
|
|
loff_t i_pos;
|
358 |
|
|
|
359 |
|
|
res = msdos_format_name(dentry->d_name.name,dentry->d_name.len,
|
360 |
|
|
msdos_name, &MSDOS_SB(sb)->options);
|
361 |
|
|
if (res < 0)
|
362 |
|
|
return res;
|
363 |
|
|
is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
|
364 |
|
|
/* foo vs .foo situation */
|
365 |
|
|
if (fat_scan(dir, msdos_name, &bh, &de, &i_pos) >= 0)
|
366 |
|
|
goto out_exist;
|
367 |
|
|
|
368 |
|
|
res = msdos_add_entry(dir, msdos_name, &bh, &de, &i_pos, 1, is_hid);
|
369 |
|
|
if (res)
|
370 |
|
|
goto out_unlock;
|
371 |
|
|
inode = fat_build_inode(dir->i_sb, de, i_pos, &res);
|
372 |
|
|
if (!inode) {
|
373 |
|
|
fat_brelse(sb, bh);
|
374 |
|
|
goto out_unlock;
|
375 |
|
|
}
|
376 |
|
|
res = 0;
|
377 |
|
|
|
378 |
|
|
dir->i_nlink++;
|
379 |
|
|
inode->i_nlink = 2; /* no need to mark them dirty */
|
380 |
|
|
|
381 |
|
|
res = fat_new_dir(inode, dir, 0);
|
382 |
|
|
if (res)
|
383 |
|
|
goto mkdir_error;
|
384 |
|
|
|
385 |
|
|
fat_brelse(sb, bh);
|
386 |
|
|
d_instantiate(dentry, inode);
|
387 |
|
|
res = 0;
|
388 |
|
|
|
389 |
|
|
out_unlock:
|
390 |
|
|
return res;
|
391 |
|
|
|
392 |
|
|
mkdir_error:
|
393 |
|
|
printk(KERN_WARNING "msdos_mkdir: error=%d, attempting cleanup\n", res);
|
394 |
|
|
inode->i_nlink = 0;
|
395 |
|
|
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
|
396 |
|
|
dir->i_nlink--;
|
397 |
|
|
mark_inode_dirty(inode);
|
398 |
|
|
mark_inode_dirty(dir);
|
399 |
|
|
de->name[0] = DELETED_FLAG;
|
400 |
|
|
fat_mark_buffer_dirty(sb, bh);
|
401 |
|
|
fat_brelse(sb, bh);
|
402 |
|
|
fat_detach(inode);
|
403 |
|
|
iput(inode);
|
404 |
|
|
goto out_unlock;
|
405 |
|
|
|
406 |
|
|
out_exist:
|
407 |
|
|
fat_brelse(sb, bh);
|
408 |
|
|
res = -EINVAL;
|
409 |
|
|
goto out_unlock;
|
410 |
|
|
}
|
411 |
|
|
|
412 |
|
|
/***** Unlink a file */
|
413 |
|
|
int msdos_unlink( struct inode *dir, struct dentry *dentry)
|
414 |
|
|
{
|
415 |
|
|
struct super_block *sb = dir->i_sb;
|
416 |
|
|
struct inode *inode = dentry->d_inode;
|
417 |
|
|
loff_t i_pos;
|
418 |
|
|
int res;
|
419 |
|
|
struct buffer_head *bh;
|
420 |
|
|
struct msdos_dir_entry *de;
|
421 |
|
|
|
422 |
|
|
bh = NULL;
|
423 |
|
|
res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len,
|
424 |
|
|
&bh, &de, &i_pos);
|
425 |
|
|
if (res < 0)
|
426 |
|
|
goto unlink_done;
|
427 |
|
|
|
428 |
|
|
de->name[0] = DELETED_FLAG;
|
429 |
|
|
fat_mark_buffer_dirty(sb, bh);
|
430 |
|
|
fat_detach(inode);
|
431 |
|
|
fat_brelse(sb, bh);
|
432 |
|
|
inode->i_nlink = 0;
|
433 |
|
|
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
|
434 |
|
|
mark_inode_dirty(inode);
|
435 |
|
|
mark_inode_dirty(dir);
|
436 |
|
|
res = 0;
|
437 |
|
|
unlink_done:
|
438 |
|
|
return res;
|
439 |
|
|
}
|
440 |
|
|
|
441 |
|
|
static int do_msdos_rename(struct inode *old_dir, char *old_name,
|
442 |
|
|
struct dentry *old_dentry,
|
443 |
|
|
struct inode *new_dir,char *new_name, struct dentry *new_dentry,
|
444 |
|
|
struct buffer_head *old_bh,
|
445 |
|
|
struct msdos_dir_entry *old_de, loff_t old_i_pos, int is_hid)
|
446 |
|
|
{
|
447 |
|
|
struct super_block *sb = old_dir->i_sb;
|
448 |
|
|
struct buffer_head *new_bh=NULL,*dotdot_bh=NULL;
|
449 |
|
|
struct msdos_dir_entry *new_de,*dotdot_de;
|
450 |
|
|
struct inode *old_inode,*new_inode;
|
451 |
|
|
loff_t new_i_pos, dotdot_i_pos;
|
452 |
|
|
int error;
|
453 |
|
|
int is_dir;
|
454 |
|
|
|
455 |
|
|
old_inode = old_dentry->d_inode;
|
456 |
|
|
new_inode = new_dentry->d_inode;
|
457 |
|
|
is_dir = S_ISDIR(old_inode->i_mode);
|
458 |
|
|
|
459 |
|
|
if (fat_scan(new_dir, new_name, &new_bh, &new_de, &new_i_pos) >= 0
|
460 |
|
|
&& !new_inode)
|
461 |
|
|
goto degenerate_case;
|
462 |
|
|
if (is_dir) {
|
463 |
|
|
if (new_inode) {
|
464 |
|
|
error = fat_dir_empty(new_inode);
|
465 |
|
|
if (error)
|
466 |
|
|
goto out;
|
467 |
|
|
}
|
468 |
|
|
error = fat_scan(old_inode, MSDOS_DOTDOT, &dotdot_bh,
|
469 |
|
|
&dotdot_de, &dotdot_i_pos);
|
470 |
|
|
if (error < 0) {
|
471 |
|
|
printk(KERN_WARNING
|
472 |
|
|
"MSDOS: %s/%s, get dotdot failed, ret=%d\n",
|
473 |
|
|
old_dentry->d_parent->d_name.name,
|
474 |
|
|
old_dentry->d_name.name, error);
|
475 |
|
|
goto out;
|
476 |
|
|
}
|
477 |
|
|
}
|
478 |
|
|
if (!new_bh) {
|
479 |
|
|
error = msdos_add_entry(new_dir, new_name, &new_bh, &new_de,
|
480 |
|
|
&new_i_pos, is_dir, is_hid);
|
481 |
|
|
if (error)
|
482 |
|
|
goto out;
|
483 |
|
|
}
|
484 |
|
|
new_dir->i_version = ++event;
|
485 |
|
|
|
486 |
|
|
/* There we go */
|
487 |
|
|
|
488 |
|
|
if (new_inode)
|
489 |
|
|
fat_detach(new_inode);
|
490 |
|
|
old_de->name[0] = DELETED_FLAG;
|
491 |
|
|
fat_mark_buffer_dirty(sb, old_bh);
|
492 |
|
|
fat_detach(old_inode);
|
493 |
|
|
fat_attach(old_inode, new_i_pos);
|
494 |
|
|
if (is_hid)
|
495 |
|
|
MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
|
496 |
|
|
else
|
497 |
|
|
MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
|
498 |
|
|
mark_inode_dirty(old_inode);
|
499 |
|
|
old_dir->i_version = ++event;
|
500 |
|
|
old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
|
501 |
|
|
mark_inode_dirty(old_dir);
|
502 |
|
|
if (new_inode) {
|
503 |
|
|
new_inode->i_nlink--;
|
504 |
|
|
new_inode->i_ctime = CURRENT_TIME;
|
505 |
|
|
mark_inode_dirty(new_inode);
|
506 |
|
|
}
|
507 |
|
|
if (dotdot_bh) {
|
508 |
|
|
dotdot_de->start = CT_LE_W(MSDOS_I(new_dir)->i_logstart);
|
509 |
|
|
dotdot_de->starthi = CT_LE_W((MSDOS_I(new_dir)->i_logstart) >> 16);
|
510 |
|
|
fat_mark_buffer_dirty(sb, dotdot_bh);
|
511 |
|
|
old_dir->i_nlink--;
|
512 |
|
|
mark_inode_dirty(old_dir);
|
513 |
|
|
if (new_inode) {
|
514 |
|
|
new_inode->i_nlink--;
|
515 |
|
|
mark_inode_dirty(new_inode);
|
516 |
|
|
} else {
|
517 |
|
|
new_dir->i_nlink++;
|
518 |
|
|
mark_inode_dirty(new_dir);
|
519 |
|
|
}
|
520 |
|
|
}
|
521 |
|
|
error = 0;
|
522 |
|
|
out:
|
523 |
|
|
fat_brelse(sb, new_bh);
|
524 |
|
|
fat_brelse(sb, dotdot_bh);
|
525 |
|
|
return error;
|
526 |
|
|
|
527 |
|
|
degenerate_case:
|
528 |
|
|
error = -EINVAL;
|
529 |
|
|
if (new_de!=old_de)
|
530 |
|
|
goto out;
|
531 |
|
|
if (is_hid)
|
532 |
|
|
MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
|
533 |
|
|
else
|
534 |
|
|
MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
|
535 |
|
|
mark_inode_dirty(old_inode);
|
536 |
|
|
old_dir->i_version = ++event;
|
537 |
|
|
old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
|
538 |
|
|
mark_inode_dirty(old_dir);
|
539 |
|
|
return 0;
|
540 |
|
|
}
|
541 |
|
|
|
542 |
|
|
/***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
|
543 |
|
|
int msdos_rename(struct inode *old_dir,struct dentry *old_dentry,
|
544 |
|
|
struct inode *new_dir,struct dentry *new_dentry)
|
545 |
|
|
{
|
546 |
|
|
struct super_block *sb = old_dir->i_sb;
|
547 |
|
|
struct buffer_head *old_bh;
|
548 |
|
|
struct msdos_dir_entry *old_de;
|
549 |
|
|
loff_t old_i_pos;
|
550 |
|
|
int error, is_hid, old_hid; /* if new file and old file are hidden */
|
551 |
|
|
char old_msdos_name[MSDOS_NAME], new_msdos_name[MSDOS_NAME];
|
552 |
|
|
|
553 |
|
|
error = msdos_format_name(old_dentry->d_name.name,
|
554 |
|
|
old_dentry->d_name.len,old_msdos_name,
|
555 |
|
|
&MSDOS_SB(old_dir->i_sb)->options);
|
556 |
|
|
if (error < 0)
|
557 |
|
|
goto rename_done;
|
558 |
|
|
error = msdos_format_name(new_dentry->d_name.name,
|
559 |
|
|
new_dentry->d_name.len,new_msdos_name,
|
560 |
|
|
&MSDOS_SB(new_dir->i_sb)->options);
|
561 |
|
|
if (error < 0)
|
562 |
|
|
goto rename_done;
|
563 |
|
|
|
564 |
|
|
is_hid = (new_dentry->d_name.name[0]=='.') && (new_msdos_name[0]!='.');
|
565 |
|
|
old_hid = (old_dentry->d_name.name[0]=='.') && (old_msdos_name[0]!='.');
|
566 |
|
|
error = fat_scan(old_dir, old_msdos_name, &old_bh, &old_de, &old_i_pos);
|
567 |
|
|
if (error < 0)
|
568 |
|
|
goto rename_done;
|
569 |
|
|
|
570 |
|
|
error = do_msdos_rename(old_dir, old_msdos_name, old_dentry,
|
571 |
|
|
new_dir, new_msdos_name, new_dentry,
|
572 |
|
|
old_bh, old_de, old_i_pos, is_hid);
|
573 |
|
|
fat_brelse(sb, old_bh);
|
574 |
|
|
|
575 |
|
|
rename_done:
|
576 |
|
|
return error;
|
577 |
|
|
}
|
578 |
|
|
|
579 |
|
|
|
580 |
|
|
/* The public inode operations for the msdos fs */
|
581 |
|
|
struct inode_operations msdos_dir_inode_operations = {
|
582 |
|
|
create: msdos_create,
|
583 |
|
|
lookup: msdos_lookup,
|
584 |
|
|
unlink: msdos_unlink,
|
585 |
|
|
mkdir: msdos_mkdir,
|
586 |
|
|
rmdir: msdos_rmdir,
|
587 |
|
|
rename: msdos_rename,
|
588 |
|
|
setattr: fat_notify_change,
|
589 |
|
|
};
|
590 |
|
|
|
591 |
|
|
struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent)
|
592 |
|
|
{
|
593 |
|
|
struct super_block *res;
|
594 |
|
|
|
595 |
|
|
MSDOS_SB(sb)->options.isvfat = 0;
|
596 |
|
|
res = fat_read_super(sb, data, silent, &msdos_dir_inode_operations);
|
597 |
|
|
if (res)
|
598 |
|
|
sb->s_root->d_op = &msdos_dentry_operations;
|
599 |
|
|
return res;
|
600 |
|
|
}
|