diff options
-rw-r--r-- | fs/gfs2/dir.c | 5 | ||||
-rw-r--r-- | fs/gfs2/dir.h | 2 | ||||
-rw-r--r-- | fs/gfs2/ops_inode.c | 221 |
3 files changed, 71 insertions, 157 deletions
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c index f7a31374ff82..410265151ad1 100644 --- a/fs/gfs2/dir.c +++ b/fs/gfs2/dir.c | |||
@@ -1669,8 +1669,9 @@ int gfs2_dir_add(struct inode *inode, const struct qstr *name, | |||
1669 | * Returns: 0 on success, error code on failure | 1669 | * Returns: 0 on success, error code on failure |
1670 | */ | 1670 | */ |
1671 | 1671 | ||
1672 | int gfs2_dir_del(struct gfs2_inode *dip, const struct qstr *name) | 1672 | int gfs2_dir_del(struct gfs2_inode *dip, const struct dentry *dentry) |
1673 | { | 1673 | { |
1674 | const struct qstr *name = &dentry->d_name; | ||
1674 | struct gfs2_dirent *dent, *prev = NULL; | 1675 | struct gfs2_dirent *dent, *prev = NULL; |
1675 | struct buffer_head *bh; | 1676 | struct buffer_head *bh; |
1676 | int error; | 1677 | int error; |
@@ -1711,6 +1712,8 @@ int gfs2_dir_del(struct gfs2_inode *dip, const struct qstr *name) | |||
1711 | gfs2_trans_add_bh(dip->i_gl, bh, 1); | 1712 | gfs2_trans_add_bh(dip->i_gl, bh, 1); |
1712 | dip->i_entries--; | 1713 | dip->i_entries--; |
1713 | dip->i_inode.i_mtime = dip->i_inode.i_ctime = CURRENT_TIME; | 1714 | dip->i_inode.i_mtime = dip->i_inode.i_ctime = CURRENT_TIME; |
1715 | if (S_ISDIR(dentry->d_inode->i_mode)) | ||
1716 | drop_nlink(&dip->i_inode); | ||
1714 | gfs2_dinode_out(dip, bh->b_data); | 1717 | gfs2_dinode_out(dip, bh->b_data); |
1715 | brelse(bh); | 1718 | brelse(bh); |
1716 | mark_inode_dirty(&dip->i_inode); | 1719 | mark_inode_dirty(&dip->i_inode); |
diff --git a/fs/gfs2/dir.h b/fs/gfs2/dir.h index a98f644bd3df..66831f1d23ea 100644 --- a/fs/gfs2/dir.h +++ b/fs/gfs2/dir.h | |||
@@ -23,7 +23,7 @@ extern int gfs2_dir_check(struct inode *dir, const struct qstr *filename, | |||
23 | const struct gfs2_inode *ip); | 23 | const struct gfs2_inode *ip); |
24 | extern int gfs2_dir_add(struct inode *inode, const struct qstr *filename, | 24 | extern int gfs2_dir_add(struct inode *inode, const struct qstr *filename, |
25 | const struct gfs2_inode *ip, unsigned int type); | 25 | const struct gfs2_inode *ip, unsigned int type); |
26 | extern int gfs2_dir_del(struct gfs2_inode *dip, const struct qstr *filename); | 26 | extern int gfs2_dir_del(struct gfs2_inode *dip, const struct dentry *dentry); |
27 | extern int gfs2_dir_read(struct inode *inode, u64 *offset, void *opaque, | 27 | extern int gfs2_dir_read(struct inode *inode, u64 *offset, void *opaque, |
28 | filldir_t filldir); | 28 | filldir_t filldir); |
29 | extern int gfs2_dir_mvino(struct gfs2_inode *dip, const struct qstr *filename, | 29 | extern int gfs2_dir_mvino(struct gfs2_inode *dip, const struct qstr *filename, |
diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index acb6f69b02ed..765da06c8f55 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c | |||
@@ -312,11 +312,52 @@ static int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name, | |||
312 | } | 312 | } |
313 | 313 | ||
314 | /** | 314 | /** |
315 | * gfs2_unlink - Unlink a file | 315 | * gfs2_unlink_inode - Removes an inode from its parent dir and unlinks it |
316 | * @dir: The inode of the directory containing the file to unlink | 316 | * @dip: The parent directory |
317 | * @name: The name of the entry in the parent directory | ||
318 | * @bh: The inode buffer for the inode to be removed | ||
319 | * @inode: The inode to be removed | ||
320 | * | ||
321 | * Called with all the locks and in a transaction. This will only be | ||
322 | * called for a directory after it has been checked to ensure it is empty. | ||
323 | * | ||
324 | * Returns: 0 on success, or an error | ||
325 | */ | ||
326 | |||
327 | static int gfs2_unlink_inode(struct gfs2_inode *dip, | ||
328 | const struct dentry *dentry, | ||
329 | struct buffer_head *bh) | ||
330 | { | ||
331 | struct inode *inode = dentry->d_inode; | ||
332 | struct gfs2_inode *ip = GFS2_I(inode); | ||
333 | int error; | ||
334 | |||
335 | error = gfs2_dir_del(dip, dentry); | ||
336 | if (error) | ||
337 | return error; | ||
338 | |||
339 | ip->i_entries = 0; | ||
340 | inode->i_ctime = CURRENT_TIME; | ||
341 | if (S_ISDIR(inode->i_mode)) | ||
342 | clear_nlink(inode); | ||
343 | else | ||
344 | drop_nlink(inode); | ||
345 | gfs2_trans_add_bh(ip->i_gl, bh, 1); | ||
346 | gfs2_dinode_out(ip, bh->b_data); | ||
347 | mark_inode_dirty(inode); | ||
348 | if (inode->i_nlink == 0) | ||
349 | gfs2_unlink_di(inode); | ||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | |||
354 | /** | ||
355 | * gfs2_unlink - Unlink an inode (this does rmdir as well) | ||
356 | * @dir: The inode of the directory containing the inode to unlink | ||
317 | * @dentry: The file itself | 357 | * @dentry: The file itself |
318 | * | 358 | * |
319 | * Unlink a file. Call gfs2_unlinki() | 359 | * This routine uses the type of the inode as a flag to figure out |
360 | * whether this is an unlink or an rmdir. | ||
320 | * | 361 | * |
321 | * Returns: errno | 362 | * Returns: errno |
322 | */ | 363 | */ |
@@ -325,7 +366,9 @@ static int gfs2_unlink(struct inode *dir, struct dentry *dentry) | |||
325 | { | 366 | { |
326 | struct gfs2_inode *dip = GFS2_I(dir); | 367 | struct gfs2_inode *dip = GFS2_I(dir); |
327 | struct gfs2_sbd *sdp = GFS2_SB(dir); | 368 | struct gfs2_sbd *sdp = GFS2_SB(dir); |
328 | struct gfs2_inode *ip = GFS2_I(dentry->d_inode); | 369 | struct inode *inode = dentry->d_inode; |
370 | struct gfs2_inode *ip = GFS2_I(inode); | ||
371 | struct buffer_head *bh; | ||
329 | struct gfs2_holder ghs[3]; | 372 | struct gfs2_holder ghs[3]; |
330 | struct gfs2_rgrpd *rgd; | 373 | struct gfs2_rgrpd *rgd; |
331 | struct gfs2_holder ri_gh; | 374 | struct gfs2_holder ri_gh; |
@@ -351,9 +394,15 @@ static int gfs2_unlink(struct inode *dir, struct dentry *dentry) | |||
351 | goto out_child; | 394 | goto out_child; |
352 | 395 | ||
353 | error = -ENOENT; | 396 | error = -ENOENT; |
354 | if (ip->i_inode.i_nlink == 0) | 397 | if (inode->i_nlink == 0) |
355 | goto out_rgrp; | 398 | goto out_rgrp; |
356 | 399 | ||
400 | if (S_ISDIR(inode->i_mode)) { | ||
401 | error = -ENOTEMPTY; | ||
402 | if (ip->i_entries > 2 || inode->i_nlink > 2) | ||
403 | goto out_rgrp; | ||
404 | } | ||
405 | |||
357 | error = gfs2_glock_nq(ghs + 2); /* rgrp */ | 406 | error = gfs2_glock_nq(ghs + 2); /* rgrp */ |
358 | if (error) | 407 | if (error) |
359 | goto out_rgrp; | 408 | goto out_rgrp; |
@@ -362,15 +411,16 @@ static int gfs2_unlink(struct inode *dir, struct dentry *dentry) | |||
362 | if (error) | 411 | if (error) |
363 | goto out_gunlock; | 412 | goto out_gunlock; |
364 | 413 | ||
365 | error = gfs2_trans_begin(sdp, 2*RES_DINODE + RES_LEAF + RES_RG_BIT, 0); | 414 | error = gfs2_trans_begin(sdp, 2*RES_DINODE + 3*RES_LEAF + RES_RG_BIT, 0); |
366 | if (error) | 415 | if (error) |
367 | goto out_gunlock; | 416 | goto out_gunlock; |
368 | 417 | ||
369 | error = gfs2_dir_del(dip, &dentry->d_name); | 418 | error = gfs2_meta_inode_buffer(ip, &bh); |
370 | if (error) | 419 | if (error) |
371 | goto out_end_trans; | 420 | goto out_end_trans; |
372 | 421 | ||
373 | error = gfs2_change_nlink(ip, -1); | 422 | error = gfs2_unlink_inode(dip, dentry, bh); |
423 | brelse(bh); | ||
374 | 424 | ||
375 | out_end_trans: | 425 | out_end_trans: |
376 | gfs2_trans_end(sdp); | 426 | gfs2_trans_end(sdp); |
@@ -522,138 +572,6 @@ static int gfs2_mkdir(struct inode *dir, struct dentry *dentry, int mode) | |||
522 | } | 572 | } |
523 | 573 | ||
524 | /** | 574 | /** |
525 | * gfs2_rmdiri - Remove a directory | ||
526 | * @dip: The parent directory of the directory to be removed | ||
527 | * @name: The name of the directory to be removed | ||
528 | * @ip: The GFS2 inode of the directory to be removed | ||
529 | * | ||
530 | * Assumes Glocks on dip and ip are held | ||
531 | * | ||
532 | * Returns: errno | ||
533 | */ | ||
534 | |||
535 | static int gfs2_rmdiri(struct gfs2_inode *dip, const struct qstr *name, | ||
536 | struct gfs2_inode *ip) | ||
537 | { | ||
538 | int error; | ||
539 | |||
540 | if (ip->i_entries != 2) { | ||
541 | if (gfs2_consist_inode(ip)) | ||
542 | gfs2_dinode_print(ip); | ||
543 | return -EIO; | ||
544 | } | ||
545 | |||
546 | error = gfs2_dir_del(dip, name); | ||
547 | if (error) | ||
548 | return error; | ||
549 | |||
550 | error = gfs2_change_nlink(dip, -1); | ||
551 | if (error) | ||
552 | return error; | ||
553 | |||
554 | error = gfs2_dir_del(ip, &gfs2_qdot); | ||
555 | if (error) | ||
556 | return error; | ||
557 | |||
558 | error = gfs2_dir_del(ip, &gfs2_qdotdot); | ||
559 | if (error) | ||
560 | return error; | ||
561 | |||
562 | /* It looks odd, but it really should be done twice */ | ||
563 | error = gfs2_change_nlink(ip, -1); | ||
564 | if (error) | ||
565 | return error; | ||
566 | |||
567 | error = gfs2_change_nlink(ip, -1); | ||
568 | if (error) | ||
569 | return error; | ||
570 | |||
571 | return error; | ||
572 | } | ||
573 | |||
574 | /** | ||
575 | * gfs2_rmdir - Remove a directory | ||
576 | * @dir: The parent directory of the directory to be removed | ||
577 | * @dentry: The dentry of the directory to remove | ||
578 | * | ||
579 | * Remove a directory. Call gfs2_rmdiri() | ||
580 | * | ||
581 | * Returns: errno | ||
582 | */ | ||
583 | |||
584 | static int gfs2_rmdir(struct inode *dir, struct dentry *dentry) | ||
585 | { | ||
586 | struct gfs2_inode *dip = GFS2_I(dir); | ||
587 | struct gfs2_sbd *sdp = GFS2_SB(dir); | ||
588 | struct gfs2_inode *ip = GFS2_I(dentry->d_inode); | ||
589 | struct gfs2_holder ghs[3]; | ||
590 | struct gfs2_rgrpd *rgd; | ||
591 | struct gfs2_holder ri_gh; | ||
592 | int error; | ||
593 | |||
594 | error = gfs2_rindex_hold(sdp, &ri_gh); | ||
595 | if (error) | ||
596 | return error; | ||
597 | gfs2_holder_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs); | ||
598 | gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + 1); | ||
599 | |||
600 | rgd = gfs2_blk2rgrpd(sdp, ip->i_no_addr); | ||
601 | gfs2_holder_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, ghs + 2); | ||
602 | |||
603 | error = gfs2_glock_nq(ghs); /* parent */ | ||
604 | if (error) | ||
605 | goto out_parent; | ||
606 | |||
607 | error = gfs2_glock_nq(ghs + 1); /* child */ | ||
608 | if (error) | ||
609 | goto out_child; | ||
610 | |||
611 | error = -ENOENT; | ||
612 | if (ip->i_inode.i_nlink == 0) | ||
613 | goto out_rgrp; | ||
614 | |||
615 | error = gfs2_glock_nq(ghs + 2); /* rgrp */ | ||
616 | if (error) | ||
617 | goto out_rgrp; | ||
618 | |||
619 | error = gfs2_unlink_ok(dip, &dentry->d_name, ip); | ||
620 | if (error) | ||
621 | goto out_gunlock; | ||
622 | |||
623 | if (ip->i_entries < 2) { | ||
624 | if (gfs2_consist_inode(ip)) | ||
625 | gfs2_dinode_print(ip); | ||
626 | error = -EIO; | ||
627 | goto out_gunlock; | ||
628 | } | ||
629 | if (ip->i_entries > 2) { | ||
630 | error = -ENOTEMPTY; | ||
631 | goto out_gunlock; | ||
632 | } | ||
633 | |||
634 | error = gfs2_trans_begin(sdp, 2 * RES_DINODE + 3 * RES_LEAF + RES_RG_BIT, 0); | ||
635 | if (error) | ||
636 | goto out_gunlock; | ||
637 | |||
638 | error = gfs2_rmdiri(dip, &dentry->d_name, ip); | ||
639 | |||
640 | gfs2_trans_end(sdp); | ||
641 | |||
642 | out_gunlock: | ||
643 | gfs2_glock_dq(ghs + 2); | ||
644 | out_rgrp: | ||
645 | gfs2_holder_uninit(ghs + 2); | ||
646 | gfs2_glock_dq(ghs + 1); | ||
647 | out_child: | ||
648 | gfs2_holder_uninit(ghs + 1); | ||
649 | gfs2_glock_dq(ghs); | ||
650 | out_parent: | ||
651 | gfs2_holder_uninit(ghs); | ||
652 | gfs2_glock_dq_uninit(&ri_gh); | ||
653 | return error; | ||
654 | } | ||
655 | |||
656 | /** | ||
657 | * gfs2_mknod - Make a special file | 575 | * gfs2_mknod - Make a special file |
658 | * @dir: The directory in which the special file will reside | 576 | * @dir: The directory in which the special file will reside |
659 | * @dentry: The dentry of the special file | 577 | * @dentry: The dentry of the special file |
@@ -930,25 +848,18 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, | |||
930 | /* Remove the target file, if it exists */ | 848 | /* Remove the target file, if it exists */ |
931 | 849 | ||
932 | if (nip) { | 850 | if (nip) { |
933 | if (S_ISDIR(nip->i_inode.i_mode)) | 851 | struct buffer_head *bh; |
934 | error = gfs2_rmdiri(ndip, &ndentry->d_name, nip); | 852 | error = gfs2_meta_inode_buffer(nip, &bh); |
935 | else { | ||
936 | error = gfs2_dir_del(ndip, &ndentry->d_name); | ||
937 | if (error) | ||
938 | goto out_end_trans; | ||
939 | error = gfs2_change_nlink(nip, -1); | ||
940 | } | ||
941 | if (error) | 853 | if (error) |
942 | goto out_end_trans; | 854 | goto out_end_trans; |
855 | error = gfs2_unlink_inode(ndip, ndentry, bh); | ||
856 | brelse(bh); | ||
943 | } | 857 | } |
944 | 858 | ||
945 | if (dir_rename) { | 859 | if (dir_rename) { |
946 | error = gfs2_change_nlink(ndip, +1); | 860 | error = gfs2_change_nlink(ndip, +1); |
947 | if (error) | 861 | if (error) |
948 | goto out_end_trans; | 862 | goto out_end_trans; |
949 | error = gfs2_change_nlink(odip, -1); | ||
950 | if (error) | ||
951 | goto out_end_trans; | ||
952 | 863 | ||
953 | error = gfs2_dir_mvino(ip, &gfs2_qdotdot, ndip, DT_DIR); | 864 | error = gfs2_dir_mvino(ip, &gfs2_qdotdot, ndip, DT_DIR); |
954 | if (error) | 865 | if (error) |
@@ -964,7 +875,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, | |||
964 | brelse(dibh); | 875 | brelse(dibh); |
965 | } | 876 | } |
966 | 877 | ||
967 | error = gfs2_dir_del(odip, &odentry->d_name); | 878 | error = gfs2_dir_del(odip, odentry); |
968 | if (error) | 879 | if (error) |
969 | goto out_end_trans; | 880 | goto out_end_trans; |
970 | 881 | ||
@@ -1347,7 +1258,7 @@ const struct inode_operations gfs2_dir_iops = { | |||
1347 | .unlink = gfs2_unlink, | 1258 | .unlink = gfs2_unlink, |
1348 | .symlink = gfs2_symlink, | 1259 | .symlink = gfs2_symlink, |
1349 | .mkdir = gfs2_mkdir, | 1260 | .mkdir = gfs2_mkdir, |
1350 | .rmdir = gfs2_rmdir, | 1261 | .rmdir = gfs2_unlink, |
1351 | .mknod = gfs2_mknod, | 1262 | .mknod = gfs2_mknod, |
1352 | .rename = gfs2_rename, | 1263 | .rename = gfs2_rename, |
1353 | .permission = gfs2_permission, | 1264 | .permission = gfs2_permission, |