1
2
3
4
5
6
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
24
25static const char *reserved_names[] = {
26 "CON ","PRN ","NUL ","AUX ",
27 "LPT1 ","LPT2 ","LPT3 ","LPT4 ",
28 "COM1 ","COM2 ","COM3 ","COM4 ",
29 NULL };
30
31
32
33
34static char bad_chars[] = "*?<>|\"";
35static char bad_if_strict_pc[] = "+=,; ";
36static char bad_if_strict_atari[] = " ";
37#define bad_if_strict(opts) ((opts)->atari ? bad_if_strict_atari : bad_if_strict_pc)
38
39
40void msdos_put_super(struct super_block *sb)
41{
42 fat_put_super(sb);
43}
44
45
46static int msdos_format_name(const char *name,int len,
47 char *res,struct fat_mount_options *opts)
48
49
50
51
52
53{
54 char *walk;
55 const char **reserved;
56 unsigned char c;
57 int space;
58
59 if (name[0] == '.') {
60 if (opts->dotsOK) {
61
62 name++; len--;
63 }
64 else if (!opts->atari) return -EINVAL;
65 }
66
67
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
81
82
83
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
124 for (reserved = reserved_names; *reserved; reserved++)
125 if (!strncmp(res,*reserved,8)) return -EINVAL;
126 return 0;
127}
128
129
130static 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
157
158
159
160
161static 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
175
176
177static 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);
190out:
191 return error;
192
193old_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
201static struct dentry_operations msdos_dentry_operations = {
202 d_hash: msdos_hash,
203 d_compare: msdos_cmp,
204};
205
206
207
208
209
210
211struct 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;
233add:
234 d_add(dentry, inode);
235 res = 0;
236out:
237 if (bh)
238 fat_brelse(sb, bh);
239 return ERR_PTR(res);
240}
241
242
243static 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
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
273
274
275
276int 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
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
311int 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
327
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
343rmdir_done:
344 fat_brelse(sb, bh);
345 return res;
346}
347
348
349int 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
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;
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
389out_unlock:
390 return res;
391
392mkdir_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
406out_exist:
407 fat_brelse(sb, bh);
408 res = -EINVAL;
409 goto out_unlock;
410}
411
412
413int 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;
437unlink_done:
438 return res;
439}
440
441static 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
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;
522out:
523 fat_brelse(sb, new_bh);
524 fat_brelse(sb, dotdot_bh);
525 return error;
526
527degenerate_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
543int 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;
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
575rename_done:
576 return error;
577}
578
579
580
581struct 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
591struct 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}
601