| Start/ | End/ | |||
| True | False | - | Line | Source |
| 1 | /* | |||
| 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 | #include <linux/module.h> | |||
| 10 | #include <linux/time.h> | |||
| 11 | #include <linux/buffer_head.h> | |||
| 12 | #include <linux/msdos_fs.h> | |||
| 13 | #include <linux/smp_lock.h> | |||
| 14 | ||||
| 15 | /* MS-DOS "device special files" */ | |||
| 16 | static const unsigned char *reserved_names[] = { | |||
| 17 | "CON ", "PRN ", "NUL ", "AUX ", | |||
| 18 | "LPT1 ", "LPT2 ", "LPT3 ", "LPT4 ", | |||
| 19 | "COM1 ", "COM2 ", "COM3 ", "COM4 ", | |||
| 20 | NULL | |||
| 21 | }; | |||
| 22 | ||||
| 23 | /* Characters that are undesirable in an MS-DOS file name */ | |||
| 24 | static unsigned char bad_chars[] = "*?<>|\""; | |||
| 25 | static unsigned char bad_if_strict_pc[] = "+=,; "; | |||
| 26 | /* GEMDOS is less restrictive */ | |||
| 27 | static unsigned char bad_if_strict_atari[] = " "; | |||
| 28 | ||||
| 29 | #define bad_if_strict(opts) \ | |||
| 30 | ((opts)->atari ? bad_if_strict_atari : bad_if_strict_pc) | |||
| 31 | ||||
| 32 | /***** Formats an MS-DOS file name. Rejects invalid names. */ | |||
| 0 | 0 | - | 33 | static int msdos_format_name(const unsigned char *name, int len, |
| 34 | unsigned char *res, struct fat_mount_options *opts) | |||
| 35 | /* | |||
| 36 | * name is the proposed name, len is its length, res is | |||
| 37 | * the resulting name, opts->name_check is either (r)elaxed, | |||
| 38 | * (n)ormal or (s)trict, opts->dotsOK allows dots at the | |||
| 39 | * beginning of name (for hidden files) | |||
| 40 | */ | |||
| 41 | { | |||
| 42 | unsigned char *walk; | |||
| 43 | const unsigned char **reserved; | |||
| 44 | unsigned char c; | |||
| 45 | int space; | |||
| 46 | ||||
| 0 | 0 | - | 47 | if (name[0] == '.') { /* dotfile because . and .. already done */ |
| 0 | 0 | - | 48 | if (opts->dotsOK) { |
| 49 | /* Get rid of dot - test for it elsewhere */ | |||
| 50 | name++; | |||
| 51 | len--; | |||
| 0 | 0 | - | 52 | } else if (!opts->atari) |
| 0 | - | 53 | return -EINVAL; | |
| 54 | } | |||
| 55 | /* | |||
| 56 | * disallow names that _really_ start with a dot for MS-DOS, | |||
| 57 | * GEMDOS does not care | |||
| 58 | */ | |||
| 59 | space = !opts->atari; | |||
| 60 | c = 0; | |||
| 0 | 0 | - | 61 | for (walk = res; len && walk - res < 8; walk++) { |
| 0 | - | 61 | T && T | |
| 0 | - | 61 | T && F | |
| 0 | - | 61 | F && _ | |
| 62 | c = *name++; | |||
| 63 | len--; | |||
| 0 | 0 | - | 64 | if (opts->name_check != 'r' && strchr(bad_chars, c)) |
| 0 | - | 64 | T && T | |
| 0 | - | 64 | T && F | |
| 0 | - | 64 | F && _ | |
| 0 | - | 65 | return -EINVAL; | |
| 0 | 0 | - | 66 | if (opts->name_check == 's' && strchr(bad_if_strict(opts), c)) |
| 0 | - | 66 | T && T | |
| 0 | - | 66 | T && F | |
| 0 | - | 66 | F && _ | |
| 0 | 0 | - | 66 | ternary-?: ( opts ) -> atari |
| 0 | - | 67 | return -EINVAL; | |
| 0 | 0 | - | 68 | if (c >= 'A' && c <= 'Z' && opts->name_check == 's') |
| 0 | - | 68 | T && T && T | |
| 0 | - | 68 | T && T && F | |
| 0 | - | 68 | T && F && _ | |
| 0 | - | 68 | F && _ && _ | |
| 0 | - | 69 | return -EINVAL; | |
| 0 | 0 | - | 70 | if (c < ' ' || c == ':' || c == '\\') |
| 0 | - | 70 | T || _ || _ | |
| 0 | - | 70 | F || T || _ | |
| 0 | - | 70 | F || F || T | |
| 0 | - | 70 | F || F || F | |
| 0 | - | 71 | return -EINVAL; | |
| 72 | /* | |||
| 73 | * 0xE5 is legal as a first character, but we must substitute | |||
| 74 | * 0x05 because 0xE5 marks deleted files. Yes, DOS really | |||
| 75 | * does this. | |||
| 76 | * It seems that Microsoft hacked DOS to support non-US | |||
| 77 | * characters after the 0xE5 character was already in use to | |||
| 78 | * mark deleted files. | |||
| 79 | */ | |||
| 0 | 0 | - | 80 | if ((res == walk) && (c == 0xE5)) |
| 0 | - | 80 | (T) && (T) | |
| 0 | - | 80 | (T) && (F) | |
| 0 | - | 80 | (F) && (_) | |
| 81 | c = 0x05; | |||
| 0 | 0 | - | 82 | if (c == '.') |
| 0 | - | 83 | break; | |
| 84 | space = (c == ' '); | |||
| 85 | *walk = (!opts->nocase && c >= 'a' && c <= 'z') ? c - 32 : c; | |||
| 0 | 0 | - | 85 | ternary-?: ( ! opts -> nocase && c >= 'a' &&.. |
| 86 | } | |||
| 0 | 0 | - | 87 | if (space) |
| 0 | - | 88 | return -EINVAL; | |
| 0 | 0 | - | 89 | if (opts->name_check == 's' && len && c != '.') { |
| 0 | - | 89 | T && T && T | |
| 0 | - | 89 | T && T && F | |
| 0 | - | 89 | T && F && _ | |
| 0 | - | 89 | F && _ && _ | |
| 90 | c = *name++; | |||
| 91 | len--; | |||
| 0 | 0 | - | 92 | if (c != '.') |
| 0 | - | 93 | return -EINVAL; | |
| 94 | } | |||
| 0 | 0 | - | 95 | while (c != '.' && len--) |
| 0 | - | 95 | T && T | |
| 0 | - | 95 | T && F | |
| 0 | - | 95 | F && _ | |
| 96 | c = *name++; | |||
| 0 | 0 | - | 97 | if (c == '.') { |
| 0 | 0 | - | 98 | while (walk - res < 8) |
| 99 | *walk++ = ' '; | |||
| 0 | 0 | - | 100 | while (len > 0 && walk - res < MSDOS_NAME) { |
| 0 | - | 100 | T && T | |
| 0 | - | 100 | T && F | |
| 0 | - | 100 | F && _ | |
| 101 | c = *name++; | |||
| 102 | len--; | |||
| 0 | 0 | - | 103 | if (opts->name_check != 'r' && strchr(bad_chars, c)) |
| 0 | - | 103 | T && T | |
| 0 | - | 103 | T && F | |
| 0 | - | 103 | F && _ | |
| 0 | - | 104 | return -EINVAL; | |
| 105 | if (opts->name_check == 's' && | |||
| 0 | 0 | - | 106 | strchr(bad_if_strict(opts), c)) |
| 0 | - | 106 | T && T | |
| 0 | - | 106 | T && F | |
| 0 | - | 106 | F && _ | |
| 0 | 0 | - | 106 | ternary-?: ( opts ) -> atari |
| 0 | - | 107 | return -EINVAL; | |
| 0 | 0 | - | 108 | if (c < ' ' || c == ':' || c == '\\') |
| 0 | - | 108 | T || _ || _ | |
| 0 | - | 108 | F || T || _ | |
| 0 | - | 108 | F || F || T | |
| 0 | - | 108 | F || F || F | |
| 0 | - | 109 | return -EINVAL; | |
| 0 | 0 | - | 110 | if (c == '.') { |
| 0 | 0 | - | 111 | if (opts->name_check == 's') |
| 0 | - | 112 | return -EINVAL; | |
| 0 | - | 113 | break; | |
| 114 | } | |||
| 0 | 0 | - | 115 | if (c >= 'A' && c <= 'Z' && opts->name_check == 's') |
| 0 | - | 115 | T && T && T | |
| 0 | - | 115 | T && T && F | |
| 0 | - | 115 | T && F && _ | |
| 0 | - | 115 | F && _ && _ | |
| 0 | - | 116 | return -EINVAL; | |
| 117 | space = c == ' '; | |||
| 0 | 0 | - | 118 | if (!opts->nocase && c >= 'a' && c <= 'z') |
| 0 | - | 118 | T && T && T | |
| 0 | - | 118 | T && T && F | |
| 0 | - | 118 | T && F && _ | |
| 0 | - | 118 | F && _ && _ | |
| 119 | *walk++ = c - 32; | |||
| 120 | else | |||
| 121 | *walk++ = c; | |||
| 122 | } | |||
| 0 | 0 | - | 123 | if (space) |
| 0 | - | 124 | return -EINVAL; | |
| 0 | 0 | - | 125 | if (opts->name_check == 's' && len) |
| 0 | - | 125 | T && T | |
| 0 | - | 125 | T && F | |
| 0 | - | 125 | F && _ | |
| 0 | - | 126 | return -EINVAL; | |
| 127 | } | |||
| 0 | 0 | - | 128 | while (walk - res < MSDOS_NAME) |
| 129 | *walk++ = ' '; | |||
| 0 | 0 | - | 130 | if (!opts->atari) |
| 131 | /* GEMDOS is less stupid and has no reserved names */ | |||
| 0 | 0 | - | 132 | for (reserved = reserved_names; *reserved; reserved++) |
| 0 | 0 | - | 133 | if (!strncmp(res, *reserved, 8)) |
| 0 | - | 134 | return -EINVAL; | |
| 0 | - | 135 | return 0; | |
| 136 | } | |||
| 137 | ||||
| 138 | /***** Locates a directory entry. Uses unformatted name. */ | |||
| 0 | 0 | - | 139 | static int msdos_find(struct inode *dir, const unsigned char *name, int len, |
| 140 | struct fat_slot_info *sinfo) | |||
| 141 | { | |||
| 142 | struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb); | |||
| 143 | unsigned char msdos_name[MSDOS_NAME]; | |||
| 144 | int err; | |||
| 145 | ||||
| 146 | err = msdos_format_name(name, len, msdos_name, &sbi->options); | |||
| 0 | 0 | - | 147 | if (err) |
| 0 | - | 148 | return -ENOENT; | |
| 149 | ||||
| 150 | err = fat_scan(dir, msdos_name, sinfo); | |||
| 0 | 0 | - | 151 | if (!err && sbi->options.dotsOK) { |
| 0 | - | 151 | T && T | |
| 0 | - | 151 | T && F | |
| 0 | - | 151 | F && _ | |
| 0 | 0 | - | 152 | if (name[0] == '.') { |
| 0 | 0 | - | 153 | if (!(sinfo->de->attr & ATTR_HIDDEN)) |
| 154 | err = -ENOENT; | |||
| 155 | } else { | |||
| 0 | 0 | - | 156 | if (sinfo->de->attr & ATTR_HIDDEN) |
| 157 | err = -ENOENT; | |||
| 158 | } | |||
| 0 | 0 | - | 159 | if (err) |
| 160 | brelse(sinfo->bh); | |||
| 161 | } | |||
| 0 | - | 162 | return err; | |
| 163 | } | |||
| 164 | ||||
| 165 | /* | |||
| 166 | * Compute the hash for the msdos name corresponding to the dentry. | |||
| 167 | * Note: if the name is invalid, we leave the hash code unchanged so | |||
| 168 | * that the existing dentry can be used. The msdos fs routines will | |||
| 169 | * return ENOENT or EINVAL as appropriate. | |||
| 170 | */ | |||
| 0 | 0 | - | 171 | static int msdos_hash(struct dentry *dentry, struct qstr *qstr) |
| 172 | { | |||
| 173 | struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options; | |||
| 174 | unsigned char msdos_name[MSDOS_NAME]; | |||
| 175 | int error; | |||
| 176 | ||||
| 177 | error = msdos_format_name(qstr->name, qstr->len, msdos_name, options); | |||
| 0 | 0 | - | 178 | if (!error) |
| 179 | qstr->hash = full_name_hash(msdos_name, MSDOS_NAME); | |||
| 0 | - | 180 | return 0; | |
| 181 | } | |||
| 182 | ||||
| 183 | /* | |||
| 184 | * Compare two msdos names. If either of the names are invalid, | |||
| 185 | * we fall back to doing the standard name comparison. | |||
| 186 | */ | |||
| 0 | 0 | - | 187 | static int msdos_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b) |
| 188 | { | |||
| 189 | struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options; | |||
| 190 | unsigned char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME]; | |||
| 191 | int error; | |||
| 192 | ||||
| 193 | error = msdos_format_name(a->name, a->len, a_msdos_name, options); | |||
| 0 | 0 | - | 194 | if (error) |
| 0 | - | 195 | goto old_compare; | |
| 196 | error = msdos_format_name(b->name, b->len, b_msdos_name, options); | |||
| 0 | 0 | - | 197 | if (error) |
| 0 | - | 198 | goto old_compare; | |
| 199 | error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME); | |||
| 200 | out: | |||
| 0 | - | 201 | return error; | |
| 202 | ||||
| 203 | old_compare: | |||
| 204 | error = 1; | |||
| 0 | 0 | - | 205 | if (a->len == b->len) |
| 206 | error = memcmp(a->name, b->name, a->len); | |||
| 0 | - | 207 | goto out; | |
| 208 | } | |||
| 209 | ||||
| 210 | static struct dentry_operations msdos_dentry_operations = { | |||
| 211 | .d_hash = msdos_hash, | |||
| 212 | .d_compare = msdos_cmp, | |||
| 213 | }; | |||
| 214 | ||||
| 215 | /* | |||
| 216 | * AV. Wrappers for FAT sb operations. Is it wise? | |||
| 217 | */ | |||
| 218 | ||||
| 219 | /***** Get inode using directory and name */ | |||
| 0 | 0 | - | 220 | static struct dentry *msdos_lookup(struct inode *dir, struct dentry *dentry, |
| 221 | struct nameidata *nd) | |||
| 222 | { | |||
| 223 | struct super_block *sb = dir->i_sb; | |||
| 224 | struct fat_slot_info sinfo; | |||
| 225 | struct inode *inode = NULL; | |||
| 226 | int res; | |||
| 227 | ||||
| 228 | dentry->d_op = &msdos_dentry_operations; | |||
| 229 | ||||
| 230 | lock_kernel(); | |||
| 0 | 0 | - | 230 | do-while (0) |
| 231 | res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo); | |||
| 0 | 0 | - | 232 | if (res == -ENOENT) |
| 0 | - | 233 | goto add; | |
| 0 | 0 | - | 234 | if (res < 0) |
| 0 | - | 235 | goto out; | |
| 236 | inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos); | |||
| 237 | brelse(sinfo.bh); | |||
| 0 | 0 | - | 238 | if (IS_ERR(inode)) { |
| 239 | res = PTR_ERR(inode); | |||
| 0 | - | 240 | goto out; | |
| 241 | } | |||
| 242 | add: | |||
| 243 | res = 0; | |||
| 244 | dentry = d_splice_alias(inode, dentry); | |||
| 0 | 0 | - | 245 | if (dentry) |
| 246 | dentry->d_op = &msdos_dentry_operations; | |||
| 247 | out: | |||
| 248 | unlock_kernel(); | |||
| 0 | 0 | - | 248 | do-while (0) |
| 0 | 0 | - | 249 | if (!res) |
| 0 | - | 250 | return dentry; | |
| 0 | - | 251 | return ERR_PTR(res); | |
| 252 | } | |||
| 253 | ||||
| 254 | /***** Creates a directory entry (name is already formatted). */ | |||
| 0 | 0 | - | 255 | static int msdos_add_entry(struct inode *dir, const unsigned char *name, |
| 256 | int is_dir, int is_hid, int cluster, | |||
| 257 | struct timespec *ts, struct fat_slot_info *sinfo) | |||
| 258 | { | |||
| 259 | struct msdos_dir_entry de; | |||
| 260 | __le16 time, date; | |||
| 261 | int err; | |||
| 262 | ||||
| 263 | memcpy(de.name, name, MSDOS_NAME); | |||
| 264 | de.attr = is_dir ? ATTR_DIR : ATTR_ARCH; | |||
| 0 | 0 | - | 264 | ternary-?: is_dir |
| 0 | 0 | - | 265 | if (is_hid) |
| 266 | de.attr |= ATTR_HIDDEN; | |||
| 267 | de.lcase = 0; | |||
| 268 | fat_date_unix2dos(ts->tv_sec, &time, &date); | |||
| 269 | de.cdate = de.adate = 0; | |||
| 270 | de.ctime = 0; | |||
| 271 | de.ctime_cs = 0; | |||
| 272 | de.time = time; | |||
| 273 | de.date = date; | |||
| 274 | de.start = cpu_to_le16(cluster); | |||
| 275 | de.starthi = cpu_to_le16(cluster >> 16); | |||
| 276 | de.size = 0; | |||
| 277 | ||||
| 278 | err = fat_add_entries(dir, &de, 1, sinfo); | |||
| 0 | 0 | - | 279 | if (err) |
| 0 | - | 280 | return err; | |
| 281 | ||||
| 282 | dir->i_ctime = dir->i_mtime = *ts; | |||
| 0 | 0 | - | 283 | if (IS_DIRSYNC(dir)) |
| 0 | - | 283 | ((T) || (_)) | |
| 0 | - | 283 | ((F) || (T)) | |
| 0 | - | 283 | ((F) || (F)) | |
| 284 | (void)fat_sync_inode(dir); | |||
| 285 | else | |||
| 286 | mark_inode_dirty(dir); | |||
| 287 | ||||
| 0 | - | 288 | return 0; | |
| 289 | } | |||
| 290 | ||||
| 291 | /***** Create a file */ | |||
| 0 | 0 | - | 292 | static int msdos_create(struct inode *dir, struct dentry *dentry, int mode, |
| 293 | struct nameidata *nd) | |||
| 294 | { | |||
| 295 | struct super_block *sb = dir->i_sb; | |||
| 296 | struct inode *inode; | |||
| 297 | struct fat_slot_info sinfo; | |||
| 298 | struct timespec ts; | |||
| 299 | unsigned char msdos_name[MSDOS_NAME]; | |||
| 300 | int err, is_hid; | |||
| 301 | ||||
| 302 | lock_kernel(); | |||
| 0 | 0 | - | 302 | do-while (0) |
| 303 | ||||
| 304 | err = msdos_format_name(dentry->d_name.name, dentry->d_name.len, | |||
| 305 | msdos_name, &MSDOS_SB(sb)->options); | |||
| 0 | 0 | - | 306 | if (err) |
| 0 | - | 307 | goto out; | |
| 308 | is_hid = (dentry->d_name.name[0] == '.') && (msdos_name[0] != '.'); | |||
| 309 | /* Have to do it due to foo vs. .foo conflicts */ | |||
| 0 | 0 | - | 310 | if (!fat_scan(dir, msdos_name, &sinfo)) { |
| 311 | brelse(sinfo.bh); | |||
| 312 | err = -EINVAL; | |||
| 0 | - | 313 | goto out; | |
| 314 | } | |||
| 315 | ||||
| 316 | ts = CURRENT_TIME_SEC; | |||
| 317 | err = msdos_add_entry(dir, msdos_name, 0, is_hid, 0, &ts, &sinfo); | |||
| 0 | 0 | - | 318 | if (err) |
| 0 | - | 319 | goto out; | |
| 320 | inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos); | |||
| 321 | brelse(sinfo.bh); | |||
| 0 | 0 | - | 322 | if (IS_ERR(inode)) { |
| 323 | err = PTR_ERR(inode); | |||
| 0 | - | 324 | goto out; | |
| 325 | } | |||
| 326 | inode->i_mtime = inode->i_atime = inode->i_ctime = ts; | |||
| 327 | /* timestamp is already written, so mark_inode_dirty() is unneeded. */ | |||
| 328 | ||||
| 329 | d_instantiate(dentry, inode); | |||
| 330 | out: | |||
| 331 | unlock_kernel(); | |||
| 0 | 0 | - | 331 | do-while (0) |
| 0 | - | 332 | return err; | |
| 333 | } | |||
| 334 | ||||
| 335 | /***** Remove a directory */ | |||
| 0 | 0 | - | 336 | static int msdos_rmdir(struct inode *dir, struct dentry *dentry) |
| 337 | { | |||
| 338 | struct inode *inode = dentry->d_inode; | |||
| 339 | struct fat_slot_info sinfo; | |||
| 340 | int err; | |||
| 341 | ||||
| 342 | lock_kernel(); | |||
| 0 | 0 | - | 342 | do-while (0) |
| 343 | /* | |||
| 344 | * Check whether the directory is not in use, then check | |||
| 345 | * whether it is empty. | |||
| 346 | */ | |||
| 347 | err = fat_dir_empty(inode); | |||
| 0 | 0 | - | 348 | if (err) |
| 0 | - | 349 | goto out; | |
| 350 | err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo); | |||
| 0 | 0 | - | 351 | if (err) |
| 0 | - | 352 | goto out; | |
| 353 | ||||
| 354 | err = fat_remove_entries(dir, &sinfo); /* and releases bh */ | |||
| 0 | 0 | - | 355 | if (err) |
| 0 | - | 356 | goto out; | |
| 357 | dir->i_nlink--; | |||
| 358 | ||||
| 359 | inode->i_nlink = 0; | |||
| 360 | inode->i_ctime = CURRENT_TIME_SEC; | |||
| 361 | fat_detach(inode); | |||
| 362 | out: | |||
| 363 | unlock_kernel(); | |||
| 0 | 0 | - | 363 | do-while (0) |
| 364 | ||||
| 0 | - | 365 | return err; | |
| 366 | } | |||
| 367 | ||||
| 368 | /***** Make a directory */ | |||
| 0 | 0 | - | 369 | static int msdos_mkdir(struct inode *dir, struct dentry *dentry, int mode) |
| 370 | { | |||
| 371 | struct super_block *sb = dir->i_sb; | |||
| 372 | struct fat_slot_info sinfo; | |||
| 373 | struct inode *inode; | |||
| 374 | unsigned char msdos_name[MSDOS_NAME]; | |||
| 375 | struct timespec ts; | |||
| 376 | int err, is_hid, cluster; | |||
| 377 | ||||
| 378 | lock_kernel(); | |||
| 0 | 0 | - | 378 | do-while (0) |
| 379 | ||||
| 380 | err = msdos_format_name(dentry->d_name.name, dentry->d_name.len, | |||
| 381 | msdos_name, &MSDOS_SB(sb)->options); | |||
| 0 | 0 | - | 382 | if (err) |
| 0 | - | 383 | goto out; | |
| 384 | is_hid = (dentry->d_name.name[0] == '.') && (msdos_name[0] != '.'); | |||
| 385 | /* foo vs .foo situation */ | |||
| 0 | 0 | - | 386 | if (!fat_scan(dir, msdos_name, &sinfo)) { |
| 387 | brelse(sinfo.bh); | |||
| 388 | err = -EINVAL; | |||
| 0 | - | 389 | goto out; | |
| 390 | } | |||
| 391 | ||||
| 392 | ts = CURRENT_TIME_SEC; | |||
| 393 | cluster = fat_alloc_new_dir(dir, &ts); | |||
| 0 | 0 | - | 394 | if (cluster < 0) { |
| 395 | err = cluster; | |||
| 0 | - | 396 | goto out; | |
| 397 | } | |||
| 398 | err = msdos_add_entry(dir, msdos_name, 1, is_hid, cluster, &ts, &sinfo); | |||
| 0 | 0 | - | 399 | if (err) |
| 0 | - | 400 | goto out_free; | |
| 401 | dir->i_nlink++; | |||
| 402 | ||||
| 403 | inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos); | |||
| 404 | brelse(sinfo.bh); | |||
| 0 | 0 | - | 405 | if (IS_ERR(inode)) { |
| 406 | err = PTR_ERR(inode); | |||
| 407 | /* the directory was completed, just return a error */ | |||
| 0 | - | 408 | goto out; | |
| 409 | } | |||
| 410 | inode->i_nlink = 2; | |||
| 411 | inode->i_mtime = inode->i_atime = inode->i_ctime = ts; | |||
| 412 | /* timestamp is already written, so mark_inode_dirty() is unneeded. */ | |||
| 413 | ||||
| 414 | d_instantiate(dentry, inode); | |||
| 415 | ||||
| 416 | unlock_kernel(); | |||
| 0 | 0 | - | 416 | do-while (0) |
| 0 | - | 417 | return 0; | |
| 418 | ||||
| 419&nb | ||||