|
30 | 30 | #include "../fs/internal.h" |
31 | 31 | #include "blk.h" |
32 | 32 |
|
| 33 | +/* Should we allow writing to mounted block devices? */ |
| 34 | +static bool bdev_allow_write_mounted = IS_ENABLED(CONFIG_BLK_DEV_WRITE_MOUNTED); |
| 35 | + |
33 | 36 | struct bdev_inode { |
34 | 37 | struct block_device bdev; |
35 | 38 | struct inode vfs_inode; |
@@ -730,7 +733,58 @@ void blkdev_put_no_open(struct block_device *bdev) |
730 | 733 | { |
731 | 734 | put_device(&bdev->bd_device); |
732 | 735 | } |
733 | | - |
| 736 | + |
| 737 | +static bool bdev_writes_blocked(struct block_device *bdev) |
| 738 | +{ |
| 739 | + return bdev->bd_writers == -1; |
| 740 | +} |
| 741 | + |
| 742 | +static void bdev_block_writes(struct block_device *bdev) |
| 743 | +{ |
| 744 | + bdev->bd_writers = -1; |
| 745 | +} |
| 746 | + |
| 747 | +static void bdev_unblock_writes(struct block_device *bdev) |
| 748 | +{ |
| 749 | + bdev->bd_writers = 0; |
| 750 | +} |
| 751 | + |
| 752 | +static bool bdev_may_open(struct block_device *bdev, blk_mode_t mode) |
| 753 | +{ |
| 754 | + if (bdev_allow_write_mounted) |
| 755 | + return true; |
| 756 | + /* Writes blocked? */ |
| 757 | + if (mode & BLK_OPEN_WRITE && bdev_writes_blocked(bdev)) |
| 758 | + return false; |
| 759 | + if (mode & BLK_OPEN_RESTRICT_WRITES && bdev->bd_writers > 0) |
| 760 | + return false; |
| 761 | + return true; |
| 762 | +} |
| 763 | + |
| 764 | +static void bdev_claim_write_access(struct block_device *bdev, blk_mode_t mode) |
| 765 | +{ |
| 766 | + if (bdev_allow_write_mounted) |
| 767 | + return; |
| 768 | + |
| 769 | + /* Claim exclusive or shared write access. */ |
| 770 | + if (mode & BLK_OPEN_RESTRICT_WRITES) |
| 771 | + bdev_block_writes(bdev); |
| 772 | + else if (mode & BLK_OPEN_WRITE) |
| 773 | + bdev->bd_writers++; |
| 774 | +} |
| 775 | + |
| 776 | +static void bdev_yield_write_access(struct block_device *bdev, blk_mode_t mode) |
| 777 | +{ |
| 778 | + if (bdev_allow_write_mounted) |
| 779 | + return; |
| 780 | + |
| 781 | + /* Yield exclusive or shared write access. */ |
| 782 | + if (mode & BLK_OPEN_RESTRICT_WRITES) |
| 783 | + bdev_unblock_writes(bdev); |
| 784 | + else if (mode & BLK_OPEN_WRITE) |
| 785 | + bdev->bd_writers--; |
| 786 | +} |
| 787 | + |
734 | 788 | /** |
735 | 789 | * bdev_open_by_dev - open a block device by device number |
736 | 790 | * @dev: device number of block device to open |
@@ -773,6 +827,10 @@ struct bdev_handle *bdev_open_by_dev(dev_t dev, blk_mode_t mode, void *holder, |
773 | 827 | if (ret) |
774 | 828 | goto free_handle; |
775 | 829 |
|
| 830 | + /* Blocking writes requires exclusive opener */ |
| 831 | + if (mode & BLK_OPEN_RESTRICT_WRITES && !holder) |
| 832 | + return ERR_PTR(-EINVAL); |
| 833 | + |
776 | 834 | bdev = blkdev_get_no_open(dev); |
777 | 835 | if (!bdev) { |
778 | 836 | ret = -ENXIO; |
@@ -800,12 +858,16 @@ struct bdev_handle *bdev_open_by_dev(dev_t dev, blk_mode_t mode, void *holder, |
800 | 858 | goto abort_claiming; |
801 | 859 | if (!try_module_get(disk->fops->owner)) |
802 | 860 | goto abort_claiming; |
| 861 | + ret = -EBUSY; |
| 862 | + if (!bdev_may_open(bdev, mode)) |
| 863 | + goto abort_claiming; |
803 | 864 | if (bdev_is_partition(bdev)) |
804 | 865 | ret = blkdev_get_part(bdev, mode); |
805 | 866 | else |
806 | 867 | ret = blkdev_get_whole(bdev, mode); |
807 | 868 | if (ret) |
808 | 869 | goto put_module; |
| 870 | + bdev_claim_write_access(bdev, mode); |
809 | 871 | if (holder) { |
810 | 872 | bd_finish_claiming(bdev, holder, hops); |
811 | 873 |
|
@@ -901,6 +963,8 @@ void bdev_release(struct bdev_handle *handle) |
901 | 963 | sync_blockdev(bdev); |
902 | 964 |
|
903 | 965 | mutex_lock(&disk->open_mutex); |
| 966 | + bdev_yield_write_access(bdev, handle->mode); |
| 967 | + |
904 | 968 | if (handle->holder) |
905 | 969 | bd_end_claim(bdev, handle->holder); |
906 | 970 |
|
@@ -1069,3 +1133,12 @@ void bdev_statx_dioalign(struct inode *inode, struct kstat *stat) |
1069 | 1133 |
|
1070 | 1134 | blkdev_put_no_open(bdev); |
1071 | 1135 | } |
| 1136 | + |
| 1137 | +static int __init setup_bdev_allow_write_mounted(char *str) |
| 1138 | +{ |
| 1139 | + if (kstrtobool(str, &bdev_allow_write_mounted)) |
| 1140 | + pr_warn("Invalid option string for bdev_allow_write_mounted:" |
| 1141 | + " '%s'\n", str); |
| 1142 | + return 1; |
| 1143 | +} |
| 1144 | +__setup("bdev_allow_write_mounted=", setup_bdev_allow_write_mounted); |
0 commit comments