|
8 | 8 | #include <linux/fs.h>
|
9 | 9 | #include <linux/nls.h>
|
10 | 10 | #include <linux/ctype.h>
|
| 11 | +#include <linux/posix_acl.h> |
11 | 12 |
|
12 | 13 | #include "debug.h"
|
13 | 14 | #include "ntfs.h"
|
@@ -334,6 +335,104 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *dir,
|
334 | 335 | return err;
|
335 | 336 | }
|
336 | 337 |
|
| 338 | +/* |
| 339 | + * ntfs_atomic_open |
| 340 | + * |
| 341 | + * inode_operations::atomic_open |
| 342 | + */ |
| 343 | +static int ntfs_atomic_open(struct inode *dir, struct dentry *dentry, |
| 344 | + struct file *file, u32 flags, umode_t mode) |
| 345 | +{ |
| 346 | + int err; |
| 347 | + struct inode *inode; |
| 348 | + struct ntfs_fnd *fnd = NULL; |
| 349 | + struct ntfs_inode *ni = ntfs_i(dir); |
| 350 | + struct dentry *d = NULL; |
| 351 | + struct cpu_str *uni = __getname(); |
| 352 | + bool locked = false; |
| 353 | + |
| 354 | + if (!uni) |
| 355 | + return -ENOMEM; |
| 356 | + |
| 357 | + err = ntfs_nls_to_utf16(ni->mi.sbi, dentry->d_name.name, |
| 358 | + dentry->d_name.len, uni, NTFS_NAME_LEN, |
| 359 | + UTF16_HOST_ENDIAN); |
| 360 | + if (err < 0) |
| 361 | + goto out; |
| 362 | + |
| 363 | +#ifdef CONFIG_NTFS3_FS_POSIX_ACL |
| 364 | + if (IS_POSIXACL(dir)) { |
| 365 | + /* |
| 366 | + * Load in cache current acl to avoid ni_lock(dir): |
| 367 | + * ntfs_create_inode -> ntfs_init_acl -> posix_acl_create -> |
| 368 | + * ntfs_get_acl -> ntfs_get_acl_ex -> ni_lock |
| 369 | + */ |
| 370 | + struct posix_acl *p = get_acl(dir, ACL_TYPE_DEFAULT); |
| 371 | + |
| 372 | + if (IS_ERR(p)) { |
| 373 | + err = PTR_ERR(p); |
| 374 | + goto out; |
| 375 | + } |
| 376 | + posix_acl_release(p); |
| 377 | + } |
| 378 | +#endif |
| 379 | + |
| 380 | + if (d_in_lookup(dentry)) { |
| 381 | + ni_lock_dir(ni); |
| 382 | + locked = true; |
| 383 | + fnd = fnd_get(); |
| 384 | + if (!fnd) { |
| 385 | + err = -ENOMEM; |
| 386 | + goto out1; |
| 387 | + } |
| 388 | + |
| 389 | + d = d_splice_alias(dir_search_u(dir, uni, fnd), dentry); |
| 390 | + if (IS_ERR(d)) { |
| 391 | + err = PTR_ERR(d); |
| 392 | + d = NULL; |
| 393 | + goto out2; |
| 394 | + } |
| 395 | + |
| 396 | + if (d) |
| 397 | + dentry = d; |
| 398 | + } |
| 399 | + |
| 400 | + if (!(flags & O_CREAT) || d_really_is_positive(dentry)) { |
| 401 | + err = finish_no_open(file, d); |
| 402 | + goto out2; |
| 403 | + } |
| 404 | + |
| 405 | + file->f_mode |= FMODE_CREATED; |
| 406 | + |
| 407 | + /* |
| 408 | + * fnd contains tree's path to insert to. |
| 409 | + * If fnd is not NULL then dir is locked. |
| 410 | + */ |
| 411 | + |
| 412 | + /* |
| 413 | + * Unfortunately I don't know how to get here correct 'struct nameidata *nd' |
| 414 | + * or 'struct user_namespace *mnt_userns'. |
| 415 | + * See atomic_open in fs/namei.c. |
| 416 | + * This is why xfstest/633 failed. |
| 417 | + * Looks like ntfs_atomic_open must accept 'struct user_namespace *mnt_userns' as argument. |
| 418 | + */ |
| 419 | + |
| 420 | + inode = ntfs_create_inode(&init_user_ns, dir, dentry, uni, mode, 0, |
| 421 | + NULL, 0, fnd); |
| 422 | + err = IS_ERR(inode) ? PTR_ERR(inode) |
| 423 | + : finish_open(file, dentry, ntfs_file_open); |
| 424 | + dput(d); |
| 425 | + |
| 426 | +out2: |
| 427 | + fnd_put(fnd); |
| 428 | +out1: |
| 429 | + if (locked) |
| 430 | + ni_unlock(ni); |
| 431 | +out: |
| 432 | + __putname(uni); |
| 433 | + return err; |
| 434 | +} |
| 435 | + |
337 | 436 | struct dentry *ntfs3_get_parent(struct dentry *child)
|
338 | 437 | {
|
339 | 438 | struct inode *inode = d_inode(child);
|
@@ -500,6 +599,7 @@ const struct inode_operations ntfs_dir_inode_operations = {
|
500 | 599 | .setattr = ntfs3_setattr,
|
501 | 600 | .getattr = ntfs_getattr,
|
502 | 601 | .listxattr = ntfs_listxattr,
|
| 602 | + .atomic_open = ntfs_atomic_open, |
503 | 603 | .fiemap = ntfs_fiemap,
|
504 | 604 | };
|
505 | 605 |
|
|
0 commit comments