Add chr/blk devices support.

The dev field is placed into the inode structure.
The major/minor numbers conversion to/from linux compatile
format happen during on-disk inodes writing/reading.

Reviewed by:    pfg
MFC after:      2 weeks
Differential Revision:  https://reviews.freebsd.org/D29930
This commit is contained in:
Fedor Uporov 2021-02-18 11:26:50 +03:00
parent 1484574843
commit 1ed5f62d61
5 changed files with 82 additions and 16 deletions

View File

@ -605,8 +605,7 @@ ext2_inactive(struct vop_inactive_args *ap)
if (ip->i_nlink <= 0) {
ext2_extattr_free(ip);
error = ext2_truncate(vp, (off_t)0, 0, NOCRED, td);
if (!(ip->i_flag & IN_E4EXTENTS))
ip->i_rdev = 0;
ip->i_rdev = 0;
mode = ip->i_mode;
ip->i_mode = 0;
ip->i_flag |= IN_CHANGE | IN_UPDATE;

View File

@ -96,6 +96,42 @@ ext2_print_inode(struct inode *in)
#define XTIME_TO_NSEC(x) ((le32toh(x) & EXT3_NSEC_MASK) >> 2)
static inline bool
ext2_old_valid_dev(dev_t dev)
{
return (major(dev) < 256 && minor(dev) < 256);
}
static inline uint16_t
ext2_old_encode_dev(dev_t dev)
{
return ((major(dev) << 8) | minor(dev));
}
static inline dev_t
ext2_old_decode_dev(uint16_t val)
{
return (makedev((val >> 8) & 255, val & 255));
}
static inline uint32_t
ext2_new_encode_dev(dev_t dev)
{
unsigned maj = major(dev);
unsigned min = minor(dev);
return ((min & 0xff) | (maj << 8) | ((min & ~0xff) << 12));
}
static inline dev_t
ext2_new_decode_dev(uint32_t dev)
{
unsigned maj = (dev & 0xfff00) >> 8;
unsigned min = (dev & 0xff) | ((dev >> 12) & 0xfff00);
return (makedev(maj, min));
}
/*
* raw ext2 inode LE to host inode conversion
*/
@ -172,7 +208,12 @@ ext2_ei2i(struct ext2fs_dinode *ei, struct inode *ip)
ip->i_uid |= (uint32_t)le16toh(ei->e2di_uid_high) << 16;
ip->i_gid |= (uint32_t)le16toh(ei->e2di_gid_high) << 16;
if ((ip->i_flag & IN_E4EXTENTS)) {
if (S_ISCHR(ip->i_mode) || S_ISBLK(ip->i_mode)) {
if (ei->e2di_blocks[0])
ip->i_rdev = ext2_old_decode_dev(le32toh(ei->e2di_blocks[0]));
else
ip->i_rdev = ext2_new_decode_dev(le32toh(ei->e2di_blocks[1]));
} else if ((ip->i_flag & IN_E4EXTENTS)) {
memcpy(ip->i_data, ei->e2di_blocks, sizeof(ei->e2di_blocks));
} else {
for (i = 0; i < EXT2_NDADDR; i++)
@ -247,7 +288,16 @@ ext2_i2ei(struct inode *ip, struct ext2fs_dinode *ei)
ei->e2di_gid = htole16(ip->i_gid & 0xffff);
ei->e2di_gid_high = htole16(ip->i_gid >> 16 & 0xffff);
if ((ip->i_flag & IN_E4EXTENTS)) {
if (S_ISCHR(ip->i_mode) || S_ISBLK(ip->i_mode)) {
if (ext2_old_valid_dev(ip->i_rdev)) {
ei->e2di_blocks[0] = htole32(ext2_old_encode_dev(ip->i_rdev));
ei->e2di_blocks[1] = 0;
} else {
ei->e2di_blocks[0] = 0;
ei->e2di_blocks[1] = htole32(ext2_new_encode_dev(ip->i_rdev));
ei->e2di_blocks[2] = 0;
}
} else if ((ip->i_flag & IN_E4EXTENTS)) {
memcpy(ei->e2di_blocks, ip->i_data, sizeof(ei->e2di_blocks));
} else {
for (i = 0; i < EXT2_NDADDR; i++)

View File

@ -322,9 +322,6 @@ ext2_access(struct vop_access_args *ap)
accmode_t accmode = ap->a_accmode;
int error;
if (vp->v_type == VBLK || vp->v_type == VCHR)
return (EOPNOTSUPP);
/*
* Disallow write attempts on read-only file systems;
* unless the file is a socket, fifo, or a block or
@ -622,6 +619,18 @@ ext2_fsync(struct vop_fsync_args *ap)
return (ext2_update(ap->a_vp, ap->a_waitfor == MNT_WAIT));
}
static int
ext2_check_mknod_limits(dev_t dev)
{
unsigned maj = major(dev);
unsigned min = minor(dev);
if (maj > EXT2_MAJOR_MAX || min > EXT2_MINOR_MAX)
return (EINVAL);
return (0);
}
/*
* Mknod vnode call
*/
@ -635,20 +644,21 @@ ext2_mknod(struct vop_mknod_args *ap)
ino_t ino;
int error;
if (vap->va_rdev != VNOVAL) {
error = ext2_check_mknod_limits(vap->va_rdev);
if (error)
return (error);
}
error = ext2_makeinode(MAKEIMODE(vap->va_type, vap->va_mode),
ap->a_dvp, vpp, ap->a_cnp);
if (error)
return (error);
ip = VTOI(*vpp);
ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
if (vap->va_rdev != VNOVAL) {
/*
* Want to be able to use this to make badblock
* inodes, so don't truncate the dev number.
*/
if (!(ip->i_flag & IN_E4EXTENTS))
ip->i_rdev = vap->va_rdev;
}
if (vap->va_rdev != VNOVAL)
ip->i_rdev = vap->va_rdev;
/*
* Remove inode, then reload it through VFS_VGET so it is
* checked to see if it is an alias of an existing entry in

View File

@ -429,4 +429,11 @@ struct ext2_gd {
#define EXT2_FIRST_INO(s) (le32toh((EXT2_SB(s)->e2fs->e2fs_rev) == \
E2FS_REV0) ? EXT2_FIRSTINO : le32toh(EXT2_SB(s)->e2fs->e2fs_first_ino))
/*
* Linux major/minor values limits
*/
#define EXT2_MINORBITS (20)
#define EXT2_MAJOR_MAX (0xffffffff >> EXT2_MINORBITS)
#define EXT2_MINOR_MAX ((1 << EXT2_MINORBITS) - 1)
#endif /* !_FS_EXT2FS_EXT2FS_H_ */

View File

@ -110,6 +110,7 @@ struct inode {
uint32_t i_gen; /* Generation number. */
uint64_t i_facl; /* EA block number. */
uint32_t i_flags; /* Status flags (chflags). */
dev_t i_rdev; /* Major/minor inode values. */
union {
struct {
uint32_t i_db[EXT2_NDADDR]; /* Direct disk blocks. */
@ -131,7 +132,6 @@ struct inode {
* di_db area.
*/
#define i_shortlink i_db
#define i_rdev i_db[0]
/* File permissions. */
#define IEXEC 0000100 /* Executable. */