Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F102977517
D33721.id100845.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
D33721.id100845.diff
View Options
diff --git a/sys/fs/msdosfs/msdosfs_denode.c b/sys/fs/msdosfs/msdosfs_denode.c
--- a/sys/fs/msdosfs/msdosfs_denode.c
+++ b/sys/fs/msdosfs/msdosfs_denode.c
@@ -142,9 +142,24 @@
return (error);
if (nvp != NULL) {
*depp = VTODE(nvp);
- KASSERT((*depp)->de_dirclust == dirclust, ("wrong dirclust"));
- KASSERT((*depp)->de_diroffset == diroffset, ("wrong diroffset"));
+ if ((*depp)->de_dirclust != dirclust) {
+ printf("wrong dir cluster %s %lu %lu\n",
+ pmp->pm_mountp->mnt_stat.f_mntonname,
+ (*depp)->de_dirclust, dirclust);
+ goto badoff;
+ }
+ if ((*depp)->de_diroffset != diroffset) {
+ printf("wrong dir offset %s %lu %lu\n",
+ pmp->pm_mountp->mnt_stat.f_mntonname,
+ (*depp)->de_diroffset, diroffset);
+ goto badoff;
+ }
return (0);
+badoff:
+ vgone(nvp);
+ vput(nvp);
+ msdosfs_integrity_error(pmp);
+ return (EBADF);
}
ldep = malloc(sizeof(struct denode), M_MSDOSFSNODE, M_WAITOK | M_ZERO);
diff --git a/sys/fs/msdosfs/msdosfs_fat.c b/sys/fs/msdosfs/msdosfs_fat.c
--- a/sys/fs/msdosfs/msdosfs_fat.c
+++ b/sys/fs/msdosfs/msdosfs_fat.c
@@ -80,8 +80,7 @@
u_long fatbn);
static __inline void
usemap_alloc(struct msdosfsmount *pmp, u_long cn);
-static __inline void
- usemap_free(struct msdosfsmount *pmp, u_long cn);
+static int usemap_free(struct msdosfsmount *pmp, u_long cn);
static int clusteralloc1(struct msdosfsmount *pmp, u_long start,
u_long count, u_long fillwith, u_long *retcluster,
u_long *got);
@@ -398,7 +397,7 @@
pmp->pm_flags |= MSDOSFS_FSIMOD;
}
-static __inline void
+static int
usemap_free(struct msdosfsmount *pmp, u_long cn)
{
@@ -408,13 +407,18 @@
pmp->pm_maxcluster));
KASSERT((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0,
("usemap_free on ro msdosfs mount"));
+ if ((pmp->pm_inusemap[cn / N_INUSEBITS] &
+ (1U << (cn % N_INUSEBITS))) == 0) {
+ printf("%s: Freeing unused sector %ld %ld %x\n",
+ pmp->pm_mountp->mnt_stat.f_mntonname, cn, cn % N_INUSEBITS,
+ (unsigned)pmp->pm_inusemap[cn / N_INUSEBITS]);
+ msdosfs_integrity_error(pmp);
+ return (EINTEGRITY);
+ }
pmp->pm_freeclustercount++;
pmp->pm_flags |= MSDOSFS_FSIMOD;
- KASSERT((pmp->pm_inusemap[cn / N_INUSEBITS] &
- (1U << (cn % N_INUSEBITS))) != 0,
- ("Freeing unused sector %ld %ld %x", cn, cn % N_INUSEBITS,
- (unsigned)pmp->pm_inusemap[cn / N_INUSEBITS]));
pmp->pm_inusemap[cn / N_INUSEBITS] &= ~(1U << (cn % N_INUSEBITS));
+ return (0);
}
int
@@ -432,11 +436,11 @@
* bit in the "in use" cluster bit map.
*/
MSDOSFS_LOCK_MP(pmp);
- usemap_free(pmp, cluster);
+ error = usemap_free(pmp, cluster);
MSDOSFS_UNLOCK_MP(pmp);
- if (oldcnp)
+ if (error == 0 && oldcnp != NULL)
*oldcnp = oldcn;
- return (0);
+ return (error);
}
/*
@@ -712,7 +716,7 @@
error = fatchain(pmp, start, count, fillwith);
if (error != 0) {
for (cl = start, n = count; n-- > 0;)
- usemap_free(pmp, cl++);
+ (void)usemap_free(pmp, cl++);
return (error);
}
#ifdef MSDOSFS_DEBUG
@@ -846,7 +850,12 @@
}
lbn = bn;
}
- usemap_free(pmp, cluster);
+ error = usemap_free(pmp, cluster);
+ if (error != 0) {
+ bwrite(bp);
+ MSDOSFS_UNLOCK_MP(pmp);
+ return (error);
+ }
switch (pmp->pm_fatmask) {
case FAT12_MASK:
readcn = getushort(bp->b_data + bo);
@@ -940,8 +949,13 @@
#endif
brelse(bp);
return (EINVAL);
- } else if (readcn == CLUST_FREE)
- usemap_free(pmp, cn);
+ } else if (readcn == CLUST_FREE) {
+ error = usemap_free(pmp, cn);
+ if (error != 0) {
+ brelse(bp);
+ return (error);
+ }
+ }
}
if (bp != NULL)
brelse(bp);
diff --git a/sys/fs/msdosfs/msdosfs_lookup.c b/sys/fs/msdosfs/msdosfs_lookup.c
--- a/sys/fs/msdosfs/msdosfs_lookup.c
+++ b/sys/fs/msdosfs/msdosfs_lookup.c
@@ -63,6 +63,29 @@
#include <fs/msdosfs/fat.h>
#include <fs/msdosfs/msdosfsmount.h>
+static int
+msdosfs_lookup_checker(struct msdosfsmount *pmp, struct vnode *dvp,
+ struct denode *tdp, struct vnode **vpp)
+{
+ struct vnode *vp;
+
+ vp = DETOV(tdp);
+
+ /*
+ * Lookup assumes that directory cannot be hardlinked.
+ * Corrupted msdosfs filesystem could break this assumption.
+ */
+ if (vp == dvp) {
+ vput(vp); /* XXXKIB or vrele ? */
+ msdosfs_integrity_error(pmp);
+ *vpp = NULL;
+ return (EBADF);
+ }
+
+ *vpp = vp;
+ return (0);
+}
+
int
msdosfs_lookup(struct vop_cachedlookup_args *ap)
{
@@ -501,8 +524,7 @@
error = deget(pmp, cluster, blkoff, LK_EXCLUSIVE, &tdp);
if (error)
return (error);
- *vpp = DETOV(tdp);
- return (0);
+ return (msdosfs_lookup_checker(pmp, vdp, tdp, vpp));
}
/*
@@ -529,7 +551,9 @@
if ((error = deget(pmp, cluster, blkoff, LK_EXCLUSIVE,
&tdp)) != 0)
return (error);
- *vpp = DETOV(tdp);
+ if ((error = msdosfs_lookup_checker(pmp, vdp, tdp, vpp))
+ != 0)
+ return (error);
cnp->cn_flags |= SAVENAME;
return (0);
}
@@ -572,14 +596,23 @@
vput(*vpp);
goto restart;
}
+ error = msdosfs_lookup_checker(pmp, vdp, VTODE(*vpp), vpp);
+ if (error != 0)
+ return (error);
} else if (dp->de_StartCluster == scn && isadir) {
+ if (cnp->cn_namelen != 1 || cnp->cn_nameptr[0] != '.') {
+ /* fs is corrupted, non-dot lookup returned dvp */
+ msdosfs_integrity_error(pmp);
+ return (EBADF);
+ }
VREF(vdp); /* we want ourself, ie "." */
*vpp = vdp;
} else {
if ((error = deget(pmp, cluster, blkoff, LK_EXCLUSIVE,
&tdp)) != 0)
return (error);
- *vpp = DETOV(tdp);
+ if ((error = msdosfs_lookup_checker(pmp, vdp, tdp, vpp)) != 0)
+ return (error);
}
/*
diff --git a/sys/fs/msdosfs/msdosfs_vfsops.c b/sys/fs/msdosfs/msdosfs_vfsops.c
--- a/sys/fs/msdosfs/msdosfs_vfsops.c
+++ b/sys/fs/msdosfs/msdosfs_vfsops.c
@@ -65,6 +65,7 @@
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/stat.h>
+#include <sys/taskqueue.h>
#include <sys/vnode.h>
#include <geom/geom.h>
@@ -112,6 +113,7 @@
static int update_mp(struct mount *mp, struct thread *td);
static int mountmsdosfs(struct vnode *devvp, struct mount *mp);
+static void msdosfs_remount_ro(void *arg, int pending);
static vfs_fhtovp_t msdosfs_fhtovp;
static vfs_mount_t msdosfs_mount;
static vfs_root_t msdosfs_root;
@@ -337,6 +339,13 @@
mp->mnt_flag &= ~MNT_RDONLY;
MNT_IUNLOCK(mp);
}
+
+ /*
+ * Avoid namei() below. The "from" option is not set.
+ * Update of the devvp is pointless for this case.
+ */
+ if ((pmp->pm_flags & MSDOSFS_ERR_RO) != 0)
+ return (0);
}
/*
* Not an update, or updating the name: look up the name
@@ -471,6 +480,8 @@
lockinit(&pmp->pm_fatlock, 0, msdosfs_lock_msg, 0, 0);
lockinit(&pmp->pm_checkpath_lock, 0, "msdoscp", 0, 0);
+ TASK_INIT(&pmp->pm_rw2ro_task, 0, msdosfs_remount_ro, pmp);
+
/*
* Initialize ownerships and permissions, since nothing else will
* initialize them iff we are mounting root.
@@ -558,6 +569,14 @@
}
pmp->pm_HugeSectors *= pmp->pm_BlkPerSec;
+ if ((off_t)pmp->pm_HugeSectors * pmp->pm_BytesPerSec <
+ pmp->pm_HugeSectors /* overflow */ ||
+ (off_t)pmp->pm_HugeSectors * pmp->pm_BytesPerSec >
+ cp->provider->mediasize /* past end of vol */) {
+ error = EINVAL;
+ goto error_exit;
+ }
+
pmp->pm_HiddenSects *= pmp->pm_BlkPerSec; /* XXX not used? */
pmp->pm_FATsecs *= pmp->pm_BlkPerSec;
SecPerClust *= pmp->pm_BlkPerSec;
@@ -577,6 +596,10 @@
pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize;
}
+ if (pmp->pm_HugeSectors <= pmp->pm_firstcluster) {
+ error = EINVAL;
+ goto error_exit;
+ }
pmp->pm_maxcluster = (pmp->pm_HugeSectors - pmp->pm_firstcluster) /
SecPerClust + 1;
pmp->pm_fatsize = pmp->pm_FATsecs * DEV_BSIZE; /* XXX not used? */
@@ -835,6 +858,47 @@
return (error);
}
+static void
+msdosfs_remount_ro(void *arg, int pending)
+{
+ struct msdosfsmount *pmp;
+ int error;
+
+ pmp = arg;
+
+ MSDOSFS_LOCK_MP(pmp);
+ if ((pmp->pm_flags & MSDOSFS_ERR_RO) != 0) {
+ while ((pmp->pm_flags & MSDOSFS_ERR_RO) != 0)
+ msleep(&pmp->pm_flags, &pmp->pm_fatlock, PVFS,
+ "msdoserrro", hz);
+ } else if ((pmp->pm_mountp->mnt_flag & MNT_RDONLY) == 0) {
+ pmp->pm_flags |= MSDOSFS_ERR_RO;
+ MSDOSFS_UNLOCK_MP(pmp);
+ printf("remounting %s read-only due to corruption\n",
+ pmp->pm_mountp->mnt_stat.f_mntfromname);
+ error = vfs_remount_ro(pmp->pm_mountp);
+ if (error != 0)
+ printf("remounting %s read-only failed: error %d\n",
+ pmp->pm_mountp->mnt_stat.f_mntfromname, error);
+ else
+ printf("remounted %s read-only\n",
+ pmp->pm_mountp->mnt_stat.f_mntfromname);
+ MSDOSFS_LOCK_MP(pmp);
+ pmp->pm_flags &= ~MSDOSFS_ERR_RO;
+ wakeup(&pmp->pm_flags);
+ }
+ MSDOSFS_UNLOCK_MP(pmp);
+
+ vfs_unbusy(pmp->pm_mountp);
+}
+
+void
+msdosfs_integrity_error(struct msdosfsmount *pmp)
+{
+ if (vfs_busy(pmp->pm_mountp, MBF_NOWAIT) == 0)
+ taskqueue_enqueue(taskqueue_thread, &pmp->pm_rw2ro_task);
+}
+
static int
msdosfs_root(struct mount *mp, int flags, struct vnode **vpp)
{
diff --git a/sys/fs/msdosfs/msdosfsmount.h b/sys/fs/msdosfs/msdosfsmount.h
--- a/sys/fs/msdosfs/msdosfsmount.h
+++ b/sys/fs/msdosfs/msdosfsmount.h
@@ -59,6 +59,7 @@
#ifndef MAKEFS
#include <sys/lock.h>
#include <sys/lockmgr.h>
+#include <sys/_task.h>
#endif
#include <sys/tree.h>
@@ -116,6 +117,7 @@
struct lock pm_fatlock; /* lockmgr protecting allocations */
struct lock pm_checkpath_lock; /* protects doscheckpath result */
#endif
+ struct task pm_rw2ro_task;
};
/*
@@ -263,5 +265,10 @@
#define MSDOSFSMNT_WAITONFAT 0x40000000 /* mounted synchronous */
#define MSDOSFS_FATMIRROR 0x20000000 /* FAT is mirrored */
#define MSDOSFS_FSIMOD 0x01000000
+#define MSDOSFS_ERR_RO 0x00800000 /* remouning ro due to error */
+
+#ifdef _KERNEL
+void msdosfs_integrity_error(struct msdosfsmount *pmp);
+#endif
#endif /* !_MSDOSFS_MSDOSFSMOUNT_H_ */
diff --git a/sys/geom/label/g_label_msdosfs.c b/sys/geom/label/g_label_msdosfs.c
--- a/sys/geom/label/g_label_msdosfs.c
+++ b/sys/geom/label/g_label_msdosfs.c
@@ -156,6 +156,12 @@
G_LABEL_DEBUG(2,
"MSDOSFS: FAT_FirstDataSector=0x%x, FAT_BytesPerSector=%d",
fat_FirstDataSector, fat_BytesPerSector);
+ if (fat_BytesPerSector == 0 ||
+ fat_BytesPerSector % pp->sectorsize != 0) {
+ G_LABEL_DEBUG(1, "MSDOSFS: %s: corrupted BPB",
+ pp->name);
+ goto error;
+ }
for (offset = fat_BytesPerSector * fat_FirstDataSector;;
offset += fat_BytesPerSector) {
diff --git a/sys/kern/vfs_mount.c b/sys/kern/vfs_mount.c
--- a/sys/kern/vfs_mount.c
+++ b/sys/kern/vfs_mount.c
@@ -2868,6 +2868,95 @@
free(buf, M_MOUNT);
}
+/*
+ * Force remount specified mount point to read-only. The argument
+ * must be busied to avoid parallel unmount attempts.
+ *
+ * Inteded use is to prevent further writes if some metadata
+ * inconsistency is detected. Note that the function still flushes
+ * all cached metadata and data for the mount point, which might be
+ * not always suitable.
+ */
+int
+vfs_remount_ro(struct mount *mp)
+{
+ struct vfsoptlist *opts;
+ struct vfsopt *opt;
+ struct vnode *vp_covered, *rootvp;
+ int error;
+
+ KASSERT(mp->mnt_lockref > 0,
+ ("vfs_remount_ro: mp %p is not busied", mp));
+ KASSERT((mp->mnt_kern_flag & MNTK_UNMOUNT) == 0,
+ ("vfs_remount_ro: mp %p is being unmounted (and busy?)", mp));
+
+ rootvp = NULL;
+ vp_covered = mp->mnt_vnodecovered;
+ error = vget(vp_covered, LK_EXCLUSIVE | LK_NOWAIT);
+ if (error != 0)
+ return (error);
+ VI_LOCK(vp_covered);
+ if ((vp_covered->v_iflag & VI_MOUNT) != 0) {
+ VI_UNLOCK(vp_covered);
+ vput(vp_covered);
+ return (EBUSY);
+ }
+ vp_covered->v_iflag |= VI_MOUNT;
+ VI_UNLOCK(vp_covered);
+ vfs_op_enter(mp);
+ vn_seqc_write_begin(vp_covered);
+
+ MNT_ILOCK(mp);
+ if ((mp->mnt_flag & MNT_RDONLY) != 0) {
+ MNT_IUNLOCK(mp);
+ error = EBUSY;
+ goto out;
+ }
+ mp->mnt_flag |= MNT_UPDATE | MNT_FORCE | MNT_RDONLY;
+ rootvp = vfs_cache_root_clear(mp);
+ MNT_IUNLOCK(mp);
+
+ opts = malloc(sizeof(struct vfsoptlist), M_MOUNT, M_WAITOK | M_ZERO);
+ TAILQ_INIT(opts);
+ opt = malloc(sizeof(struct vfsopt), M_MOUNT, M_WAITOK | M_ZERO);
+ opt->name = strdup("ro", M_MOUNT);
+ opt->value = NULL;
+ TAILQ_INSERT_TAIL(opts, opt, link);
+ vfs_mergeopts(opts, mp->mnt_opt);
+ mp->mnt_optnew = opts;
+
+ error = VFS_MOUNT(mp);
+
+ if (error == 0) {
+ MNT_ILOCK(mp);
+ mp->mnt_flag &= ~(MNT_UPDATE | MNT_FORCE);
+ MNT_IUNLOCK(mp);
+ vfs_deallocate_syncvnode(mp);
+ if (mp->mnt_opt != NULL)
+ vfs_freeopts(mp->mnt_opt);
+ mp->mnt_opt = mp->mnt_optnew;
+ } else {
+ MNT_ILOCK(mp);
+ mp->mnt_flag &= ~(MNT_UPDATE | MNT_FORCE | MNT_RDONLY);
+ MNT_IUNLOCK(mp);
+ vfs_freeopts(mp->mnt_optnew);
+ }
+ mp->mnt_optnew = NULL;
+
+out:
+ vfs_op_exit(mp);
+ VI_LOCK(vp_covered);
+ vp_covered->v_iflag &= ~VI_MOUNT;
+ VI_UNLOCK(vp_covered);
+ vput(vp_covered);
+ vn_seqc_write_end(vp_covered);
+ if (rootvp != NULL) {
+ vn_seqc_write_end(rootvp);
+ vrele(rootvp);
+ }
+ return (error);
+}
+
/*
* Suspend write operations on all local writeable filesystems. Does
* full sync of them in the process.
diff --git a/sys/sys/mount.h b/sys/sys/mount.h
--- a/sys/sys/mount.h
+++ b/sys/sys/mount.h
@@ -1036,6 +1036,8 @@
void vfs_unregister_for_notification(struct mount *,
struct mount_upper_node *);
void vfs_unregister_upper(struct mount *, struct mount_upper_node *);
+int vfs_remount_ro(struct mount *mp);
+
extern TAILQ_HEAD(mntlist, mount) mountlist; /* mounted filesystem list */
extern struct mtx_padalign mountlist_mtx;
extern struct nfs_public nfs_pub;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Nov 20, 10:06 AM (14 h, 33 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14735586
Default Alt Text
D33721.id100845.diff (13 KB)
Attached To
Mode
D33721: Improve msdosfs robustness
Attached
Detach File
Event Timeline
Log In to Comment