diff options
Diffstat (limited to 'fs/jfs')
-rw-r--r-- | fs/jfs/inode.c | 10 | ||||
-rw-r--r-- | fs/jfs/jfs_discard.c | 14 | ||||
-rw-r--r-- | fs/jfs/jfs_dmap.c | 60 | ||||
-rw-r--r-- | fs/jfs/jfs_dtree.c | 38 | ||||
-rw-r--r-- | fs/jfs/jfs_extent.c | 10 | ||||
-rw-r--r-- | fs/jfs/jfs_filsys.h | 1 | ||||
-rw-r--r-- | fs/jfs/jfs_imap.c | 24 | ||||
-rw-r--r-- | fs/jfs/jfs_logmgr.c | 2 | ||||
-rw-r--r-- | fs/jfs/jfs_metapage.c | 422 | ||||
-rw-r--r-- | fs/jfs/jfs_metapage.h | 16 | ||||
-rw-r--r-- | fs/jfs/namei.c | 11 | ||||
-rw-r--r-- | fs/jfs/super.c | 471 | ||||
-rw-r--r-- | fs/jfs/xattr.c | 38 |
13 files changed, 665 insertions, 452 deletions
diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c index 1a6b5921d17a..60fc92dee24d 100644 --- a/fs/jfs/inode.c +++ b/fs/jfs/inode.c @@ -292,11 +292,11 @@ static void jfs_write_failed(struct address_space *mapping, loff_t to) static int jfs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, - struct page **pagep, void **fsdata) + struct folio **foliop, void **fsdata) { int ret; - ret = block_write_begin(mapping, pos, len, pagep, jfs_get_block); + ret = block_write_begin(mapping, pos, len, foliop, jfs_get_block); if (unlikely(ret)) jfs_write_failed(mapping, pos + len); @@ -304,12 +304,12 @@ static int jfs_write_begin(struct file *file, struct address_space *mapping, } static int jfs_write_end(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned copied, struct page *page, + loff_t pos, unsigned len, unsigned copied, struct folio *folio, void *fsdata) { int ret; - ret = generic_write_end(file, mapping, pos, len, copied, page, fsdata); + ret = generic_write_end(file, mapping, pos, len, copied, folio, fsdata); if (ret < len) jfs_write_failed(mapping, pos + len); return ret; @@ -369,7 +369,7 @@ void jfs_truncate_nolock(struct inode *ip, loff_t length) ASSERT(length >= 0); - if (test_cflag(COMMIT_Nolink, ip)) { + if (test_cflag(COMMIT_Nolink, ip) || isReadOnly(ip)) { xtTruncate(0, ip, length, COMMIT_WMAP); return; } diff --git a/fs/jfs/jfs_discard.c b/fs/jfs/jfs_discard.c index 575cb2ba74fc..4b660296caf3 100644 --- a/fs/jfs/jfs_discard.c +++ b/fs/jfs/jfs_discard.c @@ -65,7 +65,7 @@ void jfs_issue_discard(struct inode *ip, u64 blkno, u64 nblocks) int jfs_ioc_trim(struct inode *ip, struct fstrim_range *range) { struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap; - struct bmap *bmp = JFS_SBI(ip->i_sb)->bmap; + struct bmap *bmp; struct super_block *sb = ipbmap->i_sb; int agno, agno_end; u64 start, end, minlen; @@ -83,10 +83,16 @@ int jfs_ioc_trim(struct inode *ip, struct fstrim_range *range) if (minlen == 0) minlen = 1; - if (minlen > bmp->db_agsize || + down_read(&sb->s_umount); + bmp = JFS_SBI(ip->i_sb)->bmap; + + if (bmp == NULL || + minlen > bmp->db_agsize || start >= bmp->db_mapsize || - range->len < sb->s_blocksize) + range->len < sb->s_blocksize) { + up_read(&sb->s_umount); return -EINVAL; + } if (end >= bmp->db_mapsize) end = bmp->db_mapsize - 1; @@ -100,6 +106,8 @@ int jfs_ioc_trim(struct inode *ip, struct fstrim_range *range) trimmed += dbDiscardAG(ip, agno, minlen); agno++; } + + up_read(&sb->s_umount); range->len = trimmed << sb->s_blocksize_bits; return 0; diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c index cb3cda1390ad..35e063c9f3a4 100644 --- a/fs/jfs/jfs_dmap.c +++ b/fs/jfs/jfs_dmap.c @@ -178,41 +178,30 @@ int dbMount(struct inode *ipbmap) dbmp_le = (struct dbmap_disk *) mp->data; bmp->db_mapsize = le64_to_cpu(dbmp_le->dn_mapsize); bmp->db_nfree = le64_to_cpu(dbmp_le->dn_nfree); - bmp->db_l2nbperpage = le32_to_cpu(dbmp_le->dn_l2nbperpage); - if (bmp->db_l2nbperpage > L2PSIZE - L2MINBLOCKSIZE || - bmp->db_l2nbperpage < 0) { - err = -EINVAL; - goto err_release_metapage; - } - bmp->db_numag = le32_to_cpu(dbmp_le->dn_numag); - if (!bmp->db_numag) { - err = -EINVAL; - goto err_release_metapage; - } - bmp->db_maxlevel = le32_to_cpu(dbmp_le->dn_maxlevel); bmp->db_maxag = le32_to_cpu(dbmp_le->dn_maxag); bmp->db_agpref = le32_to_cpu(dbmp_le->dn_agpref); - if (bmp->db_maxag >= MAXAG || bmp->db_maxag < 0 || - bmp->db_agpref >= MAXAG || bmp->db_agpref < 0) { - err = -EINVAL; - goto err_release_metapage; - } - bmp->db_aglevel = le32_to_cpu(dbmp_le->dn_aglevel); bmp->db_agheight = le32_to_cpu(dbmp_le->dn_agheight); bmp->db_agwidth = le32_to_cpu(dbmp_le->dn_agwidth); bmp->db_agstart = le32_to_cpu(dbmp_le->dn_agstart); bmp->db_agl2size = le32_to_cpu(dbmp_le->dn_agl2size); - if (bmp->db_agl2size > L2MAXL2SIZE - L2MAXAG || - bmp->db_agl2size < 0) { - err = -EINVAL; - goto err_release_metapage; - } - if (((bmp->db_mapsize - 1) >> bmp->db_agl2size) > MAXAG) { + if ((bmp->db_l2nbperpage > L2PSIZE - L2MINBLOCKSIZE) || + (bmp->db_l2nbperpage < 0) || + !bmp->db_numag || (bmp->db_numag > MAXAG) || + (bmp->db_maxag >= MAXAG) || (bmp->db_maxag < 0) || + (bmp->db_agpref >= MAXAG) || (bmp->db_agpref < 0) || + (bmp->db_agheight < 0) || (bmp->db_agheight > (L2LPERCTL >> 1)) || + (bmp->db_agwidth < 1) || (bmp->db_agwidth > (LPERCTL / MAXAG)) || + (bmp->db_agwidth > (1 << (L2LPERCTL - (bmp->db_agheight << 1)))) || + (bmp->db_agstart < 0) || + (bmp->db_agstart > (CTLTREESIZE - 1 - bmp->db_agwidth * (MAXAG - 1))) || + (bmp->db_agl2size > L2MAXL2SIZE - L2MAXAG) || + (bmp->db_agl2size < 0) || + ((bmp->db_mapsize - 1) >> bmp->db_agl2size) > MAXAG) { err = -EINVAL; goto err_release_metapage; } @@ -652,7 +641,7 @@ int dbNextAG(struct inode *ipbmap) * average free space. */ for (i = 0 ; i < bmp->db_numag; i++, agpref++) { - if (agpref == bmp->db_numag) + if (agpref >= bmp->db_numag) agpref = 0; if (atomic_read(&bmp->db_active[agpref])) @@ -1626,6 +1615,8 @@ s64 dbDiscardAG(struct inode *ip, int agno, s64 minlen) } else if (rc == -ENOSPC) { /* search for next smaller log2 block */ l2nb = BLKSTOL2(nblocks) - 1; + if (unlikely(l2nb < 0)) + break; nblocks = 1LL << l2nb; } else { /* Trim any already allocated blocks */ @@ -1818,6 +1809,9 @@ dbAllocCtl(struct bmap * bmp, s64 nblocks, int l2nb, s64 blkno, s64 * results) return -EIO; dp = (struct dmap *) mp->data; + if (dp->tree.budmin < 0) + return -EIO; + /* try to allocate the blocks. */ rc = dbAllocDmapLev(bmp, dp, (int) nblocks, l2nb, results); @@ -2886,6 +2880,9 @@ static void dbAdjTree(dmtree_t *tp, int leafno, int newval, bool is_ctl) /* bubble the new value up the tree as required. */ for (k = 0; k < le32_to_cpu(tp->dmt_height); k++) { + if (lp == 0) + break; + /* get the index of the first leaf of the 4 leaf * group containing the specified leaf (leafno). */ @@ -2942,9 +2939,10 @@ static void dbAdjTree(dmtree_t *tp, int leafno, int newval, bool is_ctl) static int dbFindLeaf(dmtree_t *tp, int l2nb, int *leafidx, bool is_ctl) { int ti, n = 0, k, x = 0; - int max_size; + int max_size, max_idx; max_size = is_ctl ? CTLTREESIZE : TREESIZE; + max_idx = is_ctl ? LPERCTL : LPERDMAP; /* first check the root of the tree to see if there is * sufficient free space. @@ -2976,6 +2974,8 @@ static int dbFindLeaf(dmtree_t *tp, int l2nb, int *leafidx, bool is_ctl) */ assert(n < 4); } + if (le32_to_cpu(tp->dmt_leafidx) >= max_idx) + return -ENOSPC; /* set the return to the leftmost leaf describing sufficient * free space. @@ -3020,7 +3020,7 @@ static int dbFindBits(u32 word, int l2nb) /* scan the word for nb free bits at nb alignments. */ - for (bitno = 0; mask != 0; bitno += nb, mask >>= nb) { + for (bitno = 0; mask != 0; bitno += nb, mask = (mask >> nb)) { if ((mask & word) == mask) break; } @@ -3392,7 +3392,7 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks) oldl2agsize = bmp->db_agl2size; bmp->db_agl2size = l2agsize; - bmp->db_agsize = 1 << l2agsize; + bmp->db_agsize = (s64)1 << l2agsize; /* compute new number of AG */ agno = bmp->db_numag; @@ -3655,8 +3655,8 @@ void dbFinalizeBmap(struct inode *ipbmap) * system size is not a multiple of the group size). */ inactfree = (inactags && ag_rem) ? - ((inactags - 1) << bmp->db_agl2size) + ag_rem - : inactags << bmp->db_agl2size; + (((s64)inactags - 1) << bmp->db_agl2size) + ag_rem + : ((s64)inactags << bmp->db_agl2size); /* determine how many free blocks are in the active * allocation groups plus the average number of free blocks diff --git a/fs/jfs/jfs_dtree.c b/fs/jfs/jfs_dtree.c index 031d8f570f58..ab11849cf9cc 100644 --- a/fs/jfs/jfs_dtree.c +++ b/fs/jfs/jfs_dtree.c @@ -117,7 +117,8 @@ do { \ if (!(RC)) { \ if (((P)->header.nextindex > \ (((BN) == 0) ? DTROOTMAXSLOT : (P)->header.maxslot)) || \ - ((BN) && ((P)->header.maxslot > DTPAGEMAXSLOT))) { \ + ((BN) && (((P)->header.maxslot > DTPAGEMAXSLOT) || \ + ((P)->header.stblindex >= DTPAGEMAXSLOT)))) { \ BT_PUTPAGE(MP); \ jfs_error((IP)->i_sb, \ "DT_GETPAGE: dtree page corrupt\n"); \ @@ -834,6 +835,8 @@ int dtInsert(tid_t tid, struct inode *ip, * the full page. */ DT_GETSEARCH(ip, btstack->top, bn, mp, p, index); + if (p->header.freelist == 0) + return -EINVAL; /* * insert entry for new key @@ -2610,7 +2613,7 @@ void dtInitRoot(tid_t tid, struct inode *ip, u32 idotdot) * fsck.jfs should really fix this, but it currently does not. * Called from jfs_readdir when bad index is detected. */ -static void add_missing_indices(struct inode *inode, s64 bn) +static int add_missing_indices(struct inode *inode, s64 bn) { struct ldtentry *d; struct dt_lock *dtlck; @@ -2619,7 +2622,7 @@ static void add_missing_indices(struct inode *inode, s64 bn) struct lv *lv; struct metapage *mp; dtpage_t *p; - int rc; + int rc = 0; s8 *stbl; tid_t tid; struct tlock *tlck; @@ -2644,6 +2647,16 @@ static void add_missing_indices(struct inode *inode, s64 bn) stbl = DT_GETSTBL(p); for (i = 0; i < p->header.nextindex; i++) { + if (stbl[i] < 0) { + jfs_err("jfs: add_missing_indices: Invalid stbl[%d] = %d for inode %ld, block = %lld", + i, stbl[i], (long)inode->i_ino, (long long)bn); + rc = -EIO; + + DT_PUTPAGE(mp); + txAbort(tid, 0); + goto end; + } + d = (struct ldtentry *) &p->slot[stbl[i]]; index = le32_to_cpu(d->index); if ((index < 2) || (index >= JFS_IP(inode)->next_index)) { @@ -2661,6 +2674,7 @@ static void add_missing_indices(struct inode *inode, s64 bn) (void) txCommit(tid, 1, &inode, 0); end: txEnd(tid); + return rc; } /* @@ -2889,6 +2903,14 @@ int jfs_readdir(struct file *file, struct dir_context *ctx) stbl = DT_GETSTBL(p); for (i = index; i < p->header.nextindex; i++) { + if (stbl[i] < 0 || stbl[i] > 127) { + jfs_err("JFS: Invalid stbl[%d] = %d for inode %ld, block = %lld", + i, stbl[i], (long)ip->i_ino, (long long)bn); + free_page(dirent_buf); + DT_PUTPAGE(mp); + return -EIO; + } + d = (struct ldtentry *) & p->slot[stbl[i]]; if (((long) jfs_dirent + d->namlen + 1) > @@ -3006,7 +3028,8 @@ skip_one: } if (fix_page) { - add_missing_indices(ip, bn); + if ((rc = add_missing_indices(ip, bn))) + goto out; page_fixed = 1; } @@ -3084,6 +3107,13 @@ static int dtReadFirst(struct inode *ip, struct btstack * btstack) /* get the leftmost entry */ stbl = DT_GETSTBL(p); + + if (stbl[0] < 0 || stbl[0] > 127) { + DT_PUTPAGE(mp); + jfs_error(ip->i_sb, "stbl[0] out of bound\n"); + return -EIO; + } + xd = (pxd_t *) & p->slot[stbl[0]]; /* get the child page block address */ diff --git a/fs/jfs/jfs_extent.c b/fs/jfs/jfs_extent.c index 63d21822d309..46529bcc8297 100644 --- a/fs/jfs/jfs_extent.c +++ b/fs/jfs/jfs_extent.c @@ -74,6 +74,11 @@ extAlloc(struct inode *ip, s64 xlen, s64 pno, xad_t * xp, bool abnr) int rc; int xflag; + if (isReadOnly(ip)) { + jfs_error(ip->i_sb, "read-only filesystem\n"); + return -EIO; + } + /* This blocks if we are low on resources */ txBeginAnon(ip->i_sb); @@ -253,6 +258,11 @@ int extRecord(struct inode *ip, xad_t * xp) { int rc; + if (isReadOnly(ip)) { + jfs_error(ip->i_sb, "read-only filesystem\n"); + return -EIO; + } + txBeginAnon(ip->i_sb); mutex_lock(&JFS_IP(ip)->commit_mutex); diff --git a/fs/jfs/jfs_filsys.h b/fs/jfs/jfs_filsys.h index 33ef13a0b110..8794281f8ffd 100644 --- a/fs/jfs/jfs_filsys.h +++ b/fs/jfs/jfs_filsys.h @@ -24,6 +24,7 @@ #define JFS_ERR_REMOUNT_RO 0x00000002 /* remount read-only */ #define JFS_ERR_CONTINUE 0x00000004 /* continue */ #define JFS_ERR_PANIC 0x00000008 /* panic */ +#define JFS_ERR_MASK (JFS_ERR_REMOUNT_RO|JFS_ERR_CONTINUE|JFS_ERR_PANIC) /* Quota support */ #define JFS_USRQUOTA 0x00000010 diff --git a/fs/jfs/jfs_imap.c b/fs/jfs/jfs_imap.c index 2ec35889ad24..ecb8e05b8b84 100644 --- a/fs/jfs/jfs_imap.c +++ b/fs/jfs/jfs_imap.c @@ -102,7 +102,7 @@ int diMount(struct inode *ipimap) * allocate/initialize the in-memory inode map control structure */ /* allocate the in-memory inode map control structure. */ - imap = kmalloc(sizeof(struct inomap), GFP_KERNEL); + imap = kzalloc(sizeof(struct inomap), GFP_KERNEL); if (imap == NULL) return -ENOMEM; @@ -290,7 +290,7 @@ int diSync(struct inode *ipimap) int diRead(struct inode *ip) { struct jfs_sb_info *sbi = JFS_SBI(ip->i_sb); - int iagno, ino, extno, rc; + int iagno, ino, extno, rc, agno; struct inode *ipimap; struct dinode *dp; struct iag *iagp; @@ -339,8 +339,11 @@ int diRead(struct inode *ip) /* get the ag for the iag */ agstart = le64_to_cpu(iagp->agstart); + agno = BLKTOAG(agstart, JFS_SBI(ip->i_sb)); release_metapage(mp); + if (agno >= MAXAG || agno < 0) + return -EIO; rel_inode = (ino & (INOSPERPAGE - 1)); pageno = blkno >> sbi->l2nbperpage; @@ -453,7 +456,7 @@ struct inode *diReadSpecial(struct super_block *sb, ino_t inum, int secondary) dp += inum % 8; /* 8 inodes per 4K page */ /* copy on-disk inode to in-memory inode */ - if ((copy_from_dinode(dp, ip)) != 0) { + if ((copy_from_dinode(dp, ip) != 0) || (ip->i_nlink == 0)) { /* handle bad return by returning NULL for ip */ set_nlink(ip, 1); /* Don't want iput() deleting it */ iput(ip); @@ -1357,7 +1360,7 @@ int diAlloc(struct inode *pip, bool dir, struct inode *ip) /* get the ag number of this iag */ agno = BLKTOAG(JFS_IP(pip)->agstart, JFS_SBI(pip->i_sb)); dn_numag = JFS_SBI(pip->i_sb)->bmap->db_numag; - if (agno < 0 || agno > dn_numag) + if (agno < 0 || agno > dn_numag || agno >= MAXAG) return -EIO; if (atomic_read(&JFS_SBI(pip->i_sb)->bmap->db_active[agno])) { @@ -3026,14 +3029,23 @@ static void duplicateIXtree(struct super_block *sb, s64 blkno, * * RETURN VALUES: * 0 - success - * -ENOMEM - insufficient memory + * -EINVAL - unexpected inode type */ static int copy_from_dinode(struct dinode * dip, struct inode *ip) { struct jfs_inode_info *jfs_ip = JFS_IP(ip); struct jfs_sb_info *sbi = JFS_SBI(ip->i_sb); + int fileset = le32_to_cpu(dip->di_fileset); + + switch (fileset) { + case AGGR_RESERVED_I: case AGGREGATE_I: case BMAP_I: + case LOG_I: case BADBLOCK_I: case FILESYSTEM_I: + break; + default: + return -EINVAL; + } - jfs_ip->fileset = le32_to_cpu(dip->di_fileset); + jfs_ip->fileset = fileset; jfs_ip->mode2 = le32_to_cpu(dip->di_mode); jfs_set_inode_flags(ip); diff --git a/fs/jfs/jfs_logmgr.c b/fs/jfs/jfs_logmgr.c index 9609349e92e5..270808b6219b 100644 --- a/fs/jfs/jfs_logmgr.c +++ b/fs/jfs/jfs_logmgr.c @@ -1600,7 +1600,7 @@ void jfs_flush_journal(struct jfs_log *log, int wait) mp, sizeof(struct metapage), 0); print_hex_dump(KERN_ERR, "page: ", DUMP_PREFIX_ADDRESS, 16, - sizeof(long), mp->page, + sizeof(long), mp->folio, sizeof(struct page), 0); } else print_hex_dump(KERN_ERR, "tblock:", diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index 961569c11159..9029cd216912 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -4,6 +4,7 @@ * Portions Copyright (C) Christoph Hellwig, 2001-2002 */ +#include <linux/blkdev.h> #include <linux/fs.h> #include <linux/mm.h> #include <linux/module.h> @@ -14,6 +15,7 @@ #include <linux/mempool.h> #include <linux/seq_file.h> #include <linux/writeback.h> +#include <linux/migrate.h> #include "jfs_incore.h" #include "jfs_superblock.h" #include "jfs_filsys.h" @@ -46,9 +48,9 @@ static inline void __lock_metapage(struct metapage *mp) do { set_current_state(TASK_UNINTERRUPTIBLE); if (metapage_locked(mp)) { - unlock_page(mp->page); + folio_unlock(mp->folio); io_schedule(); - lock_page(mp->page); + folio_lock(mp->folio); } } while (trylock_metapage(mp)); __set_current_state(TASK_RUNNING); @@ -56,7 +58,7 @@ static inline void __lock_metapage(struct metapage *mp) } /* - * Must have mp->page locked + * Must have mp->folio locked */ static inline void lock_metapage(struct metapage *mp) { @@ -75,36 +77,36 @@ static mempool_t *metapage_mempool; struct meta_anchor { int mp_count; atomic_t io_count; + blk_status_t status; struct metapage *mp[MPS_PER_PAGE]; }; -#define mp_anchor(page) ((struct meta_anchor *)page_private(page)) -static inline struct metapage *page_to_mp(struct page *page, int offset) +static inline struct metapage *folio_to_mp(struct folio *folio, int offset) { - if (!PagePrivate(page)) + struct meta_anchor *anchor = folio->private; + + if (!anchor) return NULL; - return mp_anchor(page)->mp[offset >> L2PSIZE]; + return anchor->mp[offset >> L2PSIZE]; } -static inline int insert_metapage(struct page *page, struct metapage *mp) +static inline int insert_metapage(struct folio *folio, struct metapage *mp) { struct meta_anchor *a; int index; int l2mp_blocks; /* log2 blocks per metapage */ - if (PagePrivate(page)) - a = mp_anchor(page); - else { + a = folio->private; + if (!a) { a = kzalloc(sizeof(struct meta_anchor), GFP_NOFS); if (!a) return -ENOMEM; - set_page_private(page, (unsigned long)a); - SetPagePrivate(page); - kmap(page); + folio_attach_private(folio, a); + kmap(&folio->page); } if (mp) { - l2mp_blocks = L2PSIZE - page->mapping->host->i_blkbits; + l2mp_blocks = L2PSIZE - folio->mapping->host->i_blkbits; index = (mp->index >> l2mp_blocks) & (MPS_PER_PAGE - 1); a->mp_count++; a->mp[index] = mp; @@ -113,10 +115,10 @@ static inline int insert_metapage(struct page *page, struct metapage *mp) return 0; } -static inline void remove_metapage(struct page *page, struct metapage *mp) +static inline void remove_metapage(struct folio *folio, struct metapage *mp) { - struct meta_anchor *a = mp_anchor(page); - int l2mp_blocks = L2PSIZE - page->mapping->host->i_blkbits; + struct meta_anchor *a = folio->private; + int l2mp_blocks = L2PSIZE - folio->mapping->host->i_blkbits; int index; index = (mp->index >> l2mp_blocks) & (MPS_PER_PAGE - 1); @@ -126,48 +128,134 @@ static inline void remove_metapage(struct page *page, struct metapage *mp) a->mp[index] = NULL; if (--a->mp_count == 0) { kfree(a); - set_page_private(page, 0); - ClearPagePrivate(page); - kunmap(page); + folio_detach_private(folio); + kunmap(&folio->page); } } -static inline void inc_io(struct page *page) +static inline void inc_io(struct folio *folio) { - atomic_inc(&mp_anchor(page)->io_count); + struct meta_anchor *anchor = folio->private; + + atomic_inc(&anchor->io_count); } -static inline void dec_io(struct page *page, void (*handler) (struct page *)) +static inline void dec_io(struct folio *folio, blk_status_t status, + void (*handler)(struct folio *, blk_status_t)) { - if (atomic_dec_and_test(&mp_anchor(page)->io_count)) - handler(page); + struct meta_anchor *anchor = folio->private; + + if (anchor->status == BLK_STS_OK) + anchor->status = status; + + if (atomic_dec_and_test(&anchor->io_count)) + handler(folio, anchor->status); } +#ifdef CONFIG_MIGRATION +static int __metapage_migrate_folio(struct address_space *mapping, + struct folio *dst, struct folio *src, + enum migrate_mode mode) +{ + struct meta_anchor *src_anchor = src->private; + struct metapage *mps[MPS_PER_PAGE] = {0}; + struct metapage *mp; + int i, rc; + + for (i = 0; i < MPS_PER_PAGE; i++) { + mp = src_anchor->mp[i]; + if (mp && metapage_locked(mp)) + return -EAGAIN; + } + + rc = filemap_migrate_folio(mapping, dst, src, mode); + if (rc != MIGRATEPAGE_SUCCESS) + return rc; + + for (i = 0; i < MPS_PER_PAGE; i++) { + mp = src_anchor->mp[i]; + if (!mp) + continue; + if (unlikely(insert_metapage(dst, mp))) { + /* If error, roll-back previosly inserted pages */ + for (int j = 0 ; j < i; j++) { + if (mps[j]) + remove_metapage(dst, mps[j]); + } + return -EAGAIN; + } + mps[i] = mp; + } + + /* Update the metapage and remove it from src */ + for (i = 0; i < MPS_PER_PAGE; i++) { + mp = mps[i]; + if (mp) { + int page_offset = mp->data - folio_address(src); + + mp->data = folio_address(dst) + page_offset; + mp->folio = dst; + remove_metapage(src, mp); + } + } + + return MIGRATEPAGE_SUCCESS; +} +#endif /* CONFIG_MIGRATION */ + #else -static inline struct metapage *page_to_mp(struct page *page, int offset) + +static inline struct metapage *folio_to_mp(struct folio *folio, int offset) { - return PagePrivate(page) ? (struct metapage *)page_private(page) : NULL; + return folio->private; } -static inline int insert_metapage(struct page *page, struct metapage *mp) +static inline int insert_metapage(struct folio *folio, struct metapage *mp) { if (mp) { - set_page_private(page, (unsigned long)mp); - SetPagePrivate(page); - kmap(page); + folio_attach_private(folio, mp); + kmap(&folio->page); } return 0; } -static inline void remove_metapage(struct page *page, struct metapage *mp) +static inline void remove_metapage(struct folio *folio, struct metapage *mp) { - set_page_private(page, 0); - ClearPagePrivate(page); - kunmap(page); + folio_detach_private(folio); + kunmap(&folio->page); } -#define inc_io(page) do {} while(0) -#define dec_io(page, handler) handler(page) +#define inc_io(folio) do {} while(0) +#define dec_io(folio, status, handler) handler(folio, status) + +#ifdef CONFIG_MIGRATION +static int __metapage_migrate_folio(struct address_space *mapping, + struct folio *dst, struct folio *src, + enum migrate_mode mode) +{ + struct metapage *mp; + int page_offset; + int rc; + + mp = folio_to_mp(src, 0); + if (metapage_locked(mp)) + return -EAGAIN; + + rc = filemap_migrate_folio(mapping, dst, src, mode); + if (rc != MIGRATEPAGE_SUCCESS) + return rc; + + if (unlikely(insert_metapage(dst, mp))) + return -EAGAIN; + + page_offset = mp->data - folio_address(src); + mp->data = folio_address(dst) + page_offset; + mp->folio = dst; + remove_metapage(src, mp); + + return MIGRATEPAGE_SUCCESS; +} +#endif /* CONFIG_MIGRATION */ #endif @@ -218,12 +306,12 @@ void metapage_exit(void) kmem_cache_destroy(metapage_cache); } -static inline void drop_metapage(struct page *page, struct metapage *mp) +static inline void drop_metapage(struct folio *folio, struct metapage *mp) { if (mp->count || mp->nohomeok || test_bit(META_dirty, &mp->flag) || test_bit(META_io, &mp->flag)) return; - remove_metapage(page, mp); + remove_metapage(folio, mp); INCREMENT(mpStat.pagefree); free_metapage(mp); } @@ -257,23 +345,20 @@ static sector_t metapage_get_blocks(struct inode *inode, sector_t lblock, return lblock; } -static void last_read_complete(struct page *page) +static void last_read_complete(struct folio *folio, blk_status_t status) { - if (!PageError(page)) - SetPageUptodate(page); - unlock_page(page); + if (status) + printk(KERN_ERR "Read error %d at %#llx\n", status, + folio_pos(folio)); + + folio_end_read(folio, status == 0); } static void metapage_read_end_io(struct bio *bio) { - struct page *page = bio->bi_private; - - if (bio->bi_status) { - printk(KERN_ERR "metapage_read_end_io: I/O error\n"); - SetPageError(page); - } + struct folio *folio = bio->bi_private; - dec_io(page, last_read_complete); + dec_io(folio, bio->bi_status, last_read_complete); bio_put(bio); } @@ -299,13 +384,19 @@ static void remove_from_logsync(struct metapage *mp) LOGSYNC_UNLOCK(log, flags); } -static void last_write_complete(struct page *page) +static void last_write_complete(struct folio *folio, blk_status_t status) { struct metapage *mp; unsigned int offset; + if (status) { + int err = blk_status_to_errno(status); + printk(KERN_ERR "metapage_write_end_io: I/O error\n"); + mapping_set_error(folio->mapping, err); + } + for (offset = 0; offset < PAGE_SIZE; offset += PSIZE) { - mp = page_to_mp(page, offset); + mp = folio_to_mp(folio, offset); if (mp && test_bit(META_io, &mp->flag)) { if (mp->lsn) remove_from_logsync(mp); @@ -316,28 +407,25 @@ static void last_write_complete(struct page *page) * safe unless I have the page locked */ } - end_page_writeback(page); + folio_end_writeback(folio); } static void metapage_write_end_io(struct bio *bio) { - struct page *page = bio->bi_private; + struct folio *folio = bio->bi_private; - BUG_ON(!PagePrivate(page)); + BUG_ON(!folio->private); - if (bio->bi_status) { - printk(KERN_ERR "metapage_write_end_io: I/O error\n"); - SetPageError(page); - } - dec_io(page, last_write_complete); + dec_io(folio, bio->bi_status, last_write_complete); bio_put(bio); } -static int metapage_writepage(struct page *page, struct writeback_control *wbc) +static int metapage_write_folio(struct folio *folio, + struct writeback_control *wbc, void *unused) { struct bio *bio = NULL; int block_offset; /* block offset of mp within page */ - struct inode *inode = page->mapping->host; + struct inode *inode = folio->mapping->host; int blocks_per_mp = JFS_SBI(inode->i_sb)->nbperpage; int len; int xlen; @@ -353,14 +441,13 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc) int offset; int bad_blocks = 0; - page_start = (sector_t)page->index << - (PAGE_SHIFT - inode->i_blkbits); - BUG_ON(!PageLocked(page)); - BUG_ON(PageWriteback(page)); - set_page_writeback(page); + page_start = folio_pos(folio) >> inode->i_blkbits; + BUG_ON(!folio_test_locked(folio)); + BUG_ON(folio_test_writeback(folio)); + folio_start_writeback(folio); for (offset = 0; offset < PAGE_SIZE; offset += PSIZE) { - mp = page_to_mp(page, offset); + mp = folio_to_mp(folio, offset); if (!mp || !test_bit(META_dirty, &mp->flag)) continue; @@ -389,22 +476,20 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc) continue; } /* Not contiguous */ - if (bio_add_page(bio, page, bio_bytes, bio_offset) < - bio_bytes) - goto add_failed; + bio_add_folio_nofail(bio, folio, bio_bytes, bio_offset); /* * Increment counter before submitting i/o to keep * count from hitting zero before we're through */ - inc_io(page); + inc_io(folio); if (!bio->bi_iter.bi_size) goto dump_bio; submit_bio(bio); nr_underway++; bio = NULL; } else - inc_io(page); - xlen = (PAGE_SIZE - offset) >> inode->i_blkbits; + inc_io(folio); + xlen = (folio_size(folio) - offset) >> inode->i_blkbits; pblock = metapage_get_blocks(inode, lblock, &xlen); if (!pblock) { printk(KERN_ERR "JFS: metapage_get_blocks failed\n"); @@ -420,7 +505,7 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc) bio = bio_alloc(inode->i_sb->s_bdev, 1, REQ_OP_WRITE, GFP_NOFS); bio->bi_iter.bi_sector = pblock << (inode->i_blkbits - 9); bio->bi_end_io = metapage_write_end_io; - bio->bi_private = page; + bio->bi_private = folio; /* Don't call bio_add_page yet, we may add to this vec */ bio_offset = offset; @@ -430,8 +515,7 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc) next_block = lblock + len; } if (bio) { - if (bio_add_page(bio, page, bio_bytes, bio_offset) < bio_bytes) - goto add_failed; + bio_add_folio_nofail(bio, folio, bio_bytes, bio_offset); if (!bio->bi_iter.bi_size) goto dump_bio; @@ -439,50 +523,56 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc) nr_underway++; } if (redirty) - redirty_page_for_writepage(wbc, page); + folio_redirty_for_writepage(wbc, folio); - unlock_page(page); + folio_unlock(folio); if (bad_blocks) goto err_out; if (nr_underway == 0) - end_page_writeback(page); + folio_end_writeback(folio); return 0; -add_failed: - /* We should never reach here, since we're only adding one vec */ - printk(KERN_ERR "JFS: bio_add_page failed unexpectedly\n"); - goto skip; dump_bio: print_hex_dump(KERN_ERR, "JFS: dump of bio: ", DUMP_PREFIX_ADDRESS, 16, 4, bio, sizeof(*bio), 0); -skip: bio_put(bio); - unlock_page(page); - dec_io(page, last_write_complete); + folio_unlock(folio); + dec_io(folio, BLK_STS_OK, last_write_complete); err_out: while (bad_blocks--) - dec_io(page, last_write_complete); + dec_io(folio, BLK_STS_OK, last_write_complete); return -EIO; } +static int metapage_writepages(struct address_space *mapping, + struct writeback_control *wbc) +{ + struct blk_plug plug; + int err; + + blk_start_plug(&plug); + err = write_cache_pages(mapping, wbc, metapage_write_folio, NULL); + blk_finish_plug(&plug); + + return err; +} + static int metapage_read_folio(struct file *fp, struct folio *folio) { - struct page *page = &folio->page; - struct inode *inode = page->mapping->host; + struct inode *inode = folio->mapping->host; struct bio *bio = NULL; int block_offset; - int blocks_per_page = i_blocks_per_page(inode, page); + int blocks_per_page = i_blocks_per_folio(inode, folio); sector_t page_start; /* address of page in fs blocks */ sector_t pblock; int xlen; unsigned int len; int offset; - BUG_ON(!PageLocked(page)); - page_start = (sector_t)page->index << - (PAGE_SHIFT - inode->i_blkbits); + BUG_ON(!folio_test_locked(folio)); + page_start = folio_pos(folio) >> inode->i_blkbits; block_offset = 0; while (block_offset < blocks_per_page) { @@ -490,9 +580,9 @@ static int metapage_read_folio(struct file *fp, struct folio *folio) pblock = metapage_get_blocks(inode, page_start + block_offset, &xlen); if (pblock) { - if (!PagePrivate(page)) - insert_metapage(page, NULL); - inc_io(page); + if (!folio->private) + insert_metapage(folio, NULL); + inc_io(folio); if (bio) submit_bio(bio); @@ -501,11 +591,10 @@ static int metapage_read_folio(struct file *fp, struct folio *folio) bio->bi_iter.bi_sector = pblock << (inode->i_blkbits - 9); bio->bi_end_io = metapage_read_end_io; - bio->bi_private = page; + bio->bi_private = folio; len = xlen << inode->i_blkbits; offset = block_offset << inode->i_blkbits; - if (bio_add_page(bio, page, len, offset) < len) - goto add_failed; + bio_add_folio_nofail(bio, folio, len, offset); block_offset += xlen; } else block_offset++; @@ -513,15 +602,9 @@ static int metapage_read_folio(struct file *fp, struct folio *folio) if (bio) submit_bio(bio); else - unlock_page(page); + folio_unlock(folio); return 0; - -add_failed: - printk(KERN_ERR "JFS: bio_add_page failed unexpectedly\n"); - bio_put(bio); - dec_io(page, last_read_complete); - return -EIO; } static bool metapage_release_folio(struct folio *folio, gfp_t gfp_mask) @@ -531,7 +614,7 @@ static bool metapage_release_folio(struct folio *folio, gfp_t gfp_mask) int offset; for (offset = 0; offset < PAGE_SIZE; offset += PSIZE) { - mp = page_to_mp(&folio->page, offset); + mp = folio_to_mp(folio, offset); if (!mp) continue; @@ -546,13 +629,36 @@ static bool metapage_release_folio(struct folio *folio, gfp_t gfp_mask) } if (mp->lsn) remove_from_logsync(mp); - remove_metapage(&folio->page, mp); + remove_metapage(folio, mp); INCREMENT(mpStat.pagefree); free_metapage(mp); } return ret; } +#ifdef CONFIG_MIGRATION +/* + * metapage_migrate_folio - Migration function for JFS metapages + */ +static int metapage_migrate_folio(struct address_space *mapping, + struct folio *dst, struct folio *src, + enum migrate_mode mode) +{ + int expected_count; + + if (!src->private) + return filemap_migrate_folio(mapping, dst, src, mode); + + /* Check whether page does not have extra refs before we do more work */ + expected_count = folio_expected_ref_count(src) + 1; + if (folio_ref_count(src) != expected_count) + return -EAGAIN; + return __metapage_migrate_folio(mapping, dst, src, mode); +} +#else +#define metapage_migrate_folio NULL +#endif /* CONFIG_MIGRATION */ + static void metapage_invalidate_folio(struct folio *folio, size_t offset, size_t length) { @@ -565,10 +671,11 @@ static void metapage_invalidate_folio(struct folio *folio, size_t offset, const struct address_space_operations jfs_metapage_aops = { .read_folio = metapage_read_folio, - .writepage = metapage_writepage, + .writepages = metapage_writepages, .release_folio = metapage_release_folio, .invalidate_folio = metapage_invalidate_folio, .dirty_folio = filemap_dirty_folio, + .migrate_folio = metapage_migrate_folio, }; struct metapage *__get_metapage(struct inode *inode, unsigned long lblock, @@ -579,7 +686,7 @@ struct metapage *__get_metapage(struct inode *inode, unsigned long lblock, int l2bsize; struct address_space *mapping; struct metapage *mp = NULL; - struct page *page; + struct folio *folio; unsigned long page_index; unsigned long page_offset; @@ -610,22 +717,22 @@ struct metapage *__get_metapage(struct inode *inode, unsigned long lblock, } if (new && (PSIZE == PAGE_SIZE)) { - page = grab_cache_page(mapping, page_index); - if (!page) { - jfs_err("grab_cache_page failed!"); + folio = filemap_grab_folio(mapping, page_index); + if (IS_ERR(folio)) { + jfs_err("filemap_grab_folio failed!"); return NULL; } - SetPageUptodate(page); + folio_mark_uptodate(folio); } else { - page = read_mapping_page(mapping, page_index, NULL); - if (IS_ERR(page)) { + folio = read_mapping_folio(mapping, page_index, NULL); + if (IS_ERR(folio)) { jfs_err("read_mapping_page failed!"); return NULL; } - lock_page(page); + folio_lock(folio); } - mp = page_to_mp(page, page_offset); + mp = folio_to_mp(folio, page_offset); if (mp) { if (mp->logical_size != size) { jfs_error(inode->i_sb, @@ -651,16 +758,16 @@ struct metapage *__get_metapage(struct inode *inode, unsigned long lblock, mp = alloc_metapage(GFP_NOFS); if (!mp) goto unlock; - mp->page = page; + mp->folio = folio; mp->sb = inode->i_sb; mp->flag = 0; mp->xflag = COMMIT_PAGE; mp->count = 1; mp->nohomeok = 0; mp->logical_size = size; - mp->data = page_address(page) + page_offset; + mp->data = folio_address(folio) + page_offset; mp->index = lblock; - if (unlikely(insert_metapage(page, mp))) { + if (unlikely(insert_metapage(folio, mp))) { free_metapage(mp); goto unlock; } @@ -672,28 +779,27 @@ struct metapage *__get_metapage(struct inode *inode, unsigned long lblock, memset(mp->data, 0, PSIZE); } - unlock_page(page); + folio_unlock(folio); jfs_info("__get_metapage: returning = 0x%p data = 0x%p", mp, mp->data); return mp; unlock: - unlock_page(page); + folio_unlock(folio); return NULL; } void grab_metapage(struct metapage * mp) { jfs_info("grab_metapage: mp = 0x%p", mp); - get_page(mp->page); - lock_page(mp->page); + folio_get(mp->folio); + folio_lock(mp->folio); mp->count++; lock_metapage(mp); - unlock_page(mp->page); + folio_unlock(mp->folio); } -static int metapage_write_one(struct page *page) +static int metapage_write_one(struct folio *folio) { - struct folio *folio = page_folio(page); struct address_space *mapping = folio->mapping; struct writeback_control wbc = { .sync_mode = WB_SYNC_ALL, @@ -707,7 +813,7 @@ static int metapage_write_one(struct page *page) if (folio_clear_dirty_for_io(folio)) { folio_get(folio); - ret = metapage_writepage(page, &wbc); + ret = metapage_write_folio(folio, &wbc, NULL); if (ret == 0) folio_wait_writeback(folio); folio_put(folio); @@ -722,71 +828,69 @@ static int metapage_write_one(struct page *page) void force_metapage(struct metapage *mp) { - struct page *page = mp->page; + struct folio *folio = mp->folio; jfs_info("force_metapage: mp = 0x%p", mp); set_bit(META_forcewrite, &mp->flag); clear_bit(META_sync, &mp->flag); - get_page(page); - lock_page(page); - set_page_dirty(page); - if (metapage_write_one(page)) + folio_get(folio); + folio_lock(folio); + folio_mark_dirty(folio); + if (metapage_write_one(folio)) jfs_error(mp->sb, "metapage_write_one() failed\n"); clear_bit(META_forcewrite, &mp->flag); - put_page(page); + folio_put(folio); } void hold_metapage(struct metapage *mp) { - lock_page(mp->page); + folio_lock(mp->folio); } void put_metapage(struct metapage *mp) { if (mp->count || mp->nohomeok) { /* Someone else will release this */ - unlock_page(mp->page); + folio_unlock(mp->folio); return; } - get_page(mp->page); + folio_get(mp->folio); mp->count++; lock_metapage(mp); - unlock_page(mp->page); + folio_unlock(mp->folio); release_metapage(mp); } void release_metapage(struct metapage * mp) { - struct page *page = mp->page; + struct folio *folio = mp->folio; jfs_info("release_metapage: mp = 0x%p, flag = 0x%lx", mp, mp->flag); - BUG_ON(!page); - - lock_page(page); + folio_lock(folio); unlock_metapage(mp); assert(mp->count); if (--mp->count || mp->nohomeok) { - unlock_page(page); - put_page(page); + folio_unlock(folio); + folio_put(folio); return; } if (test_bit(META_dirty, &mp->flag)) { - set_page_dirty(page); + folio_mark_dirty(folio); if (test_bit(META_sync, &mp->flag)) { clear_bit(META_sync, &mp->flag); - if (metapage_write_one(page)) + if (metapage_write_one(folio)) jfs_error(mp->sb, "metapage_write_one() failed\n"); - lock_page(page); + folio_lock(folio); } } else if (mp->lsn) /* discard_metapage doesn't remove it */ remove_from_logsync(mp); /* Try to keep metapages from using up too much memory */ - drop_metapage(page, mp); + drop_metapage(folio, mp); - unlock_page(page); - put_page(page); + folio_unlock(folio); + folio_put(folio); } void __invalidate_metapages(struct inode *ip, s64 addr, int len) @@ -798,7 +902,6 @@ void __invalidate_metapages(struct inode *ip, s64 addr, int len) struct address_space *mapping = JFS_SBI(ip->i_sb)->direct_inode->i_mapping; struct metapage *mp; - struct page *page; unsigned int offset; /* @@ -807,11 +910,12 @@ void __invalidate_metapages(struct inode *ip, s64 addr, int len) */ for (lblock = addr & ~(BlocksPerPage - 1); lblock < addr + len; lblock += BlocksPerPage) { - page = find_lock_page(mapping, lblock >> l2BlocksPerPage); - if (!page) + struct folio *folio = filemap_lock_folio(mapping, + lblock >> l2BlocksPerPage); + if (IS_ERR(folio)) continue; for (offset = 0; offset < PAGE_SIZE; offset += PSIZE) { - mp = page_to_mp(page, offset); + mp = folio_to_mp(folio, offset); if (!mp) continue; if (mp->index < addr) @@ -824,8 +928,8 @@ void __invalidate_metapages(struct inode *ip, s64 addr, int len) if (mp->lsn) remove_from_logsync(mp); } - unlock_page(page); - put_page(page); + folio_unlock(folio); + folio_put(folio); } } diff --git a/fs/jfs/jfs_metapage.h b/fs/jfs/jfs_metapage.h index 4179f9df4deb..2e5015c2705b 100644 --- a/fs/jfs/jfs_metapage.h +++ b/fs/jfs/jfs_metapage.h @@ -24,7 +24,7 @@ struct metapage { wait_queue_head_t wait; /* implementation */ - struct page *page; + struct folio *folio; struct super_block *sb; unsigned int logical_size; @@ -90,14 +90,14 @@ static inline void discard_metapage(struct metapage *mp) static inline void metapage_nohomeok(struct metapage *mp) { - struct page *page = mp->page; - lock_page(page); + struct folio *folio = mp->folio; + folio_lock(folio); if (!mp->nohomeok++) { mark_metapage_dirty(mp); - get_page(page); - wait_on_page_writeback(page); + folio_get(folio); + folio_wait_writeback(folio); } - unlock_page(page); + folio_unlock(folio); } /* @@ -107,7 +107,7 @@ static inline void metapage_nohomeok(struct metapage *mp) static inline void metapage_wait_for_io(struct metapage *mp) { if (test_bit(META_io, &mp->flag)) - wait_on_page_writeback(mp->page); + folio_wait_writeback(mp->folio); } /* @@ -116,7 +116,7 @@ static inline void metapage_wait_for_io(struct metapage *mp) static inline void _metapage_homeok(struct metapage *mp) { if (!--mp->nohomeok) - put_page(mp->page); + folio_put(mp->folio); } static inline void metapage_homeok(struct metapage *mp) diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index d68a4e6ac345..65a218eba8fa 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -187,13 +187,13 @@ static int jfs_create(struct mnt_idmap *idmap, struct inode *dip, * dentry - dentry of child directory * mode - create mode (rwxrwxrwx). * - * RETURN: Errors from subroutines + * RETURN: ERR_PTR() of errors from subroutines. * * note: * EACCES: user needs search+write permission on the parent directory */ -static int jfs_mkdir(struct mnt_idmap *idmap, struct inode *dip, - struct dentry *dentry, umode_t mode) +static struct dentry *jfs_mkdir(struct mnt_idmap *idmap, struct inode *dip, + struct dentry *dentry, umode_t mode) { int rc = 0; tid_t tid; /* transaction id */ @@ -308,7 +308,7 @@ static int jfs_mkdir(struct mnt_idmap *idmap, struct inode *dip, out1: jfs_info("jfs_mkdir: rc:%d", rc); - return rc; + return ERR_PTR(rc); } /* @@ -1576,7 +1576,8 @@ out: return result; } -static int jfs_ci_revalidate(struct dentry *dentry, unsigned int flags) +static int jfs_ci_revalidate(struct inode *dir, const struct qstr *name, + struct dentry *dentry, unsigned int flags) { /* * This is not negative dentry. Always valid. diff --git a/fs/jfs/super.c b/fs/jfs/super.c index e1be21ca5d6e..10368c188c5e 100644 --- a/fs/jfs/super.c +++ b/fs/jfs/super.c @@ -6,11 +6,11 @@ #include <linux/fs.h> #include <linux/module.h> -#include <linux/parser.h> #include <linux/completion.h> #include <linux/vfs.h> #include <linux/quotaops.h> -#include <linux/mount.h> +#include <linux/fs_context.h> +#include <linux/fs_parser.h> #include <linux/moduleparam.h> #include <linux/kthread.h> #include <linux/posix_acl.h> @@ -210,240 +210,195 @@ enum { Opt_discard, Opt_nodiscard, Opt_discard_minblk }; -static const match_table_t tokens = { - {Opt_integrity, "integrity"}, - {Opt_nointegrity, "nointegrity"}, - {Opt_iocharset, "iocharset=%s"}, - {Opt_resize, "resize=%u"}, - {Opt_resize_nosize, "resize"}, - {Opt_errors, "errors=%s"}, - {Opt_ignore, "noquota"}, - {Opt_quota, "quota"}, - {Opt_usrquota, "usrquota"}, - {Opt_grpquota, "grpquota"}, - {Opt_uid, "uid=%u"}, - {Opt_gid, "gid=%u"}, - {Opt_umask, "umask=%u"}, - {Opt_discard, "discard"}, - {Opt_nodiscard, "nodiscard"}, - {Opt_discard_minblk, "discard=%u"}, - {Opt_err, NULL} +static const struct constant_table jfs_param_errors[] = { + {"continue", JFS_ERR_CONTINUE}, + {"remount-ro", JFS_ERR_REMOUNT_RO}, + {"panic", JFS_ERR_PANIC}, + {} }; -static int parse_options(char *options, struct super_block *sb, s64 *newLVSize, - int *flag) -{ - void *nls_map = (void *)-1; /* -1: no change; NULL: none */ - char *p; - struct jfs_sb_info *sbi = JFS_SBI(sb); +static const struct fs_parameter_spec jfs_param_spec[] = { + fsparam_flag_no ("integrity", Opt_integrity), + fsparam_string ("iocharset", Opt_iocharset), + fsparam_u64 ("resize", Opt_resize), + fsparam_flag ("resize", Opt_resize_nosize), + fsparam_enum ("errors", Opt_errors, jfs_param_errors), + fsparam_flag ("quota", Opt_quota), + fsparam_flag ("noquota", Opt_ignore), + fsparam_flag ("usrquota", Opt_usrquota), + fsparam_flag ("grpquota", Opt_grpquota), + fsparam_uid ("uid", Opt_uid), + fsparam_gid ("gid", Opt_gid), + fsparam_u32oct ("umask", Opt_umask), + fsparam_flag ("discard", Opt_discard), + fsparam_u32 ("discard", Opt_discard_minblk), + fsparam_flag ("nodiscard", Opt_nodiscard), + {} +}; - *newLVSize = 0; - - if (!options) - return 1; - - while ((p = strsep(&options, ",")) != NULL) { - substring_t args[MAX_OPT_ARGS]; - int token; - if (!*p) - continue; - - token = match_token(p, tokens, args); - switch (token) { - case Opt_integrity: - *flag &= ~JFS_NOINTEGRITY; - break; - case Opt_nointegrity: - *flag |= JFS_NOINTEGRITY; - break; - case Opt_ignore: - /* Silently ignore the quota options */ - /* Don't do anything ;-) */ - break; - case Opt_iocharset: - if (nls_map && nls_map != (void *) -1) - unload_nls(nls_map); - if (!strcmp(args[0].from, "none")) - nls_map = NULL; - else { - nls_map = load_nls(args[0].from); - if (!nls_map) { - pr_err("JFS: charset not found\n"); - goto cleanup; - } - } - break; - case Opt_resize: - { - char *resize = args[0].from; - int rc = kstrtoll(resize, 0, newLVSize); +struct jfs_context { + int flag; + kuid_t uid; + kgid_t gid; + uint umask; + uint minblks_trim; + void *nls_map; + bool resize; + s64 newLVSize; +}; - if (rc) - goto cleanup; - break; - } - case Opt_resize_nosize: - { - *newLVSize = sb_bdev_nr_blocks(sb); - if (*newLVSize == 0) - pr_err("JFS: Cannot determine volume size\n"); - break; +static int jfs_parse_param(struct fs_context *fc, struct fs_parameter *param) +{ + struct jfs_context *ctx = fc->fs_private; + int reconfigure = (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE); + struct fs_parse_result result; + struct nls_table *nls_map; + int opt; + + opt = fs_parse(fc, jfs_param_spec, param, &result); + if (opt < 0) + return opt; + + switch (opt) { + case Opt_integrity: + if (result.negated) + ctx->flag |= JFS_NOINTEGRITY; + else + ctx->flag &= ~JFS_NOINTEGRITY; + break; + case Opt_ignore: + /* Silently ignore the quota options */ + /* Don't do anything ;-) */ + break; + case Opt_iocharset: + if (ctx->nls_map && ctx->nls_map != (void *) -1) { + unload_nls(ctx->nls_map); + ctx->nls_map = NULL; } - case Opt_errors: - { - char *errors = args[0].from; - if (!errors || !*errors) - goto cleanup; - if (!strcmp(errors, "continue")) { - *flag &= ~JFS_ERR_REMOUNT_RO; - *flag &= ~JFS_ERR_PANIC; - *flag |= JFS_ERR_CONTINUE; - } else if (!strcmp(errors, "remount-ro")) { - *flag &= ~JFS_ERR_CONTINUE; - *flag &= ~JFS_ERR_PANIC; - *flag |= JFS_ERR_REMOUNT_RO; - } else if (!strcmp(errors, "panic")) { - *flag &= ~JFS_ERR_CONTINUE; - *flag &= ~JFS_ERR_REMOUNT_RO; - *flag |= JFS_ERR_PANIC; - } else { - pr_err("JFS: %s is an invalid error handler\n", - errors); - goto cleanup; + if (!strcmp(param->string, "none")) + ctx->nls_map = NULL; + else { + nls_map = load_nls(param->string); + if (!nls_map) { + pr_err("JFS: charset not found\n"); + return -EINVAL; } - break; + ctx->nls_map = nls_map; } + break; + case Opt_resize: + if (!reconfigure) + return -EINVAL; + ctx->resize = true; + ctx->newLVSize = result.uint_64; + break; + case Opt_resize_nosize: + if (!reconfigure) + return -EINVAL; + ctx->resize = true; + break; + case Opt_errors: + ctx->flag &= ~JFS_ERR_MASK; + ctx->flag |= result.uint_32; + break; #ifdef CONFIG_QUOTA - case Opt_quota: - case Opt_usrquota: - *flag |= JFS_USRQUOTA; - break; - case Opt_grpquota: - *flag |= JFS_GRPQUOTA; - break; + case Opt_quota: + case Opt_usrquota: + ctx->flag |= JFS_USRQUOTA; + break; + case Opt_grpquota: + ctx->flag |= JFS_GRPQUOTA; + break; #else - case Opt_usrquota: - case Opt_grpquota: - case Opt_quota: - pr_err("JFS: quota operations not supported\n"); - break; + case Opt_usrquota: + case Opt_grpquota: + case Opt_quota: + pr_err("JFS: quota operations not supported\n"); + break; #endif - case Opt_uid: - { - char *uid = args[0].from; - uid_t val; - int rc = kstrtouint(uid, 0, &val); - - if (rc) - goto cleanup; - sbi->uid = make_kuid(current_user_ns(), val); - if (!uid_valid(sbi->uid)) - goto cleanup; - break; - } - - case Opt_gid: - { - char *gid = args[0].from; - gid_t val; - int rc = kstrtouint(gid, 0, &val); - - if (rc) - goto cleanup; - sbi->gid = make_kgid(current_user_ns(), val); - if (!gid_valid(sbi->gid)) - goto cleanup; - break; + case Opt_uid: + ctx->uid = result.uid; + break; + + case Opt_gid: + ctx->gid = result.gid; + break; + + case Opt_umask: + if (result.uint_32 & ~0777) { + pr_err("JFS: Invalid value of umask\n"); + return -EINVAL; } + ctx->umask = result.uint_32; + break; - case Opt_umask: - { - char *umask = args[0].from; - int rc = kstrtouint(umask, 8, &sbi->umask); - - if (rc) - goto cleanup; - if (sbi->umask & ~0777) { - pr_err("JFS: Invalid value of umask\n"); - goto cleanup; - } - break; - } + case Opt_discard: + /* if set to 1, even copying files will cause + * trimming :O + * -> user has more control over the online trimming + */ + ctx->minblks_trim = 64; + ctx->flag |= JFS_DISCARD; + break; - case Opt_discard: - /* if set to 1, even copying files will cause - * trimming :O - * -> user has more control over the online trimming - */ - sbi->minblks_trim = 64; - if (bdev_max_discard_sectors(sb->s_bdev)) - *flag |= JFS_DISCARD; - else - pr_err("JFS: discard option not supported on device\n"); - break; - - case Opt_nodiscard: - *flag &= ~JFS_DISCARD; - break; - - case Opt_discard_minblk: - { - char *minblks_trim = args[0].from; - int rc; - if (bdev_max_discard_sectors(sb->s_bdev)) { - *flag |= JFS_DISCARD; - rc = kstrtouint(minblks_trim, 0, - &sbi->minblks_trim); - if (rc) - goto cleanup; - } else - pr_err("JFS: discard option not supported on device\n"); - break; - } + case Opt_nodiscard: + ctx->flag &= ~JFS_DISCARD; + break; - default: - printk("jfs: Unrecognized mount option \"%s\" or missing value\n", - p); - goto cleanup; - } - } + case Opt_discard_minblk: + ctx->minblks_trim = result.uint_32; + ctx->flag |= JFS_DISCARD; + break; - if (nls_map != (void *) -1) { - /* Discard old (if remount) */ - unload_nls(sbi->nls_tab); - sbi->nls_tab = nls_map; + default: + return -EINVAL; } - return 1; -cleanup: - if (nls_map && nls_map != (void *) -1) - unload_nls(nls_map); return 0; } -static int jfs_remount(struct super_block *sb, int *flags, char *data) +static int jfs_reconfigure(struct fs_context *fc) { - s64 newLVSize = 0; + struct jfs_context *ctx = fc->fs_private; + struct super_block *sb = fc->root->d_sb; + int readonly = fc->sb_flags & SB_RDONLY; int rc = 0; - int flag = JFS_SBI(sb)->flag; + int flag = ctx->flag; int ret; sync_filesystem(sb); - if (!parse_options(data, sb, &newLVSize, &flag)) - return -EINVAL; - if (newLVSize) { + /* Transfer results of parsing to the sbi */ + JFS_SBI(sb)->flag = ctx->flag; + JFS_SBI(sb)->uid = ctx->uid; + JFS_SBI(sb)->gid = ctx->gid; + JFS_SBI(sb)->umask = ctx->umask; + JFS_SBI(sb)->minblks_trim = ctx->minblks_trim; + if (ctx->nls_map != (void *) -1) { + unload_nls(JFS_SBI(sb)->nls_tab); + JFS_SBI(sb)->nls_tab = ctx->nls_map; + } + ctx->nls_map = NULL; + + if (ctx->resize) { if (sb_rdonly(sb)) { pr_err("JFS: resize requires volume to be mounted read-write\n"); return -EROFS; } - rc = jfs_extendfs(sb, newLVSize, 0); + + if (!ctx->newLVSize) { + ctx->newLVSize = sb_bdev_nr_blocks(sb); + if (ctx->newLVSize == 0) + pr_err("JFS: Cannot determine volume size\n"); + } + + rc = jfs_extendfs(sb, ctx->newLVSize, 0); if (rc) return rc; } - if (sb_rdonly(sb) && !(*flags & SB_RDONLY)) { + if (sb_rdonly(sb) && !readonly) { /* * Invalidate any previously read metadata. fsck may have * changed the on-disk data since we mounted r/o @@ -459,7 +414,7 @@ static int jfs_remount(struct super_block *sb, int *flags, char *data) dquot_resume(sb, -1); return ret; } - if (!sb_rdonly(sb) && (*flags & SB_RDONLY)) { + if (!sb_rdonly(sb) && readonly) { rc = dquot_suspend(sb, -1); if (rc < 0) return rc; @@ -467,7 +422,7 @@ static int jfs_remount(struct super_block *sb, int *flags, char *data) JFS_SBI(sb)->flag = flag; return rc; } - if ((JFS_SBI(sb)->flag & JFS_NOINTEGRITY) != (flag & JFS_NOINTEGRITY)) + if ((JFS_SBI(sb)->flag & JFS_NOINTEGRITY) != (flag & JFS_NOINTEGRITY)) { if (!sb_rdonly(sb)) { rc = jfs_umount_rw(sb); if (rc) @@ -477,18 +432,20 @@ static int jfs_remount(struct super_block *sb, int *flags, char *data) ret = jfs_mount_rw(sb, 1); return ret; } + } JFS_SBI(sb)->flag = flag; return 0; } -static int jfs_fill_super(struct super_block *sb, void *data, int silent) +static int jfs_fill_super(struct super_block *sb, struct fs_context *fc) { + struct jfs_context *ctx = fc->fs_private; + int silent = fc->sb_flags & SB_SILENT; struct jfs_sb_info *sbi; struct inode *inode; int rc; - s64 newLVSize = 0; - int flag, ret = -EINVAL; + int ret = -EINVAL; jfs_info("In jfs_read_super: s_flags=0x%lx", sb->s_flags); @@ -501,24 +458,34 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent) sb->s_time_min = 0; sb->s_time_max = U32_MAX; sbi->sb = sb; - sbi->uid = INVALID_UID; - sbi->gid = INVALID_GID; - sbi->umask = -1; - /* initialize the mount flag and determine the default error handler */ - flag = JFS_ERR_REMOUNT_RO; - - if (!parse_options((char *) data, sb, &newLVSize, &flag)) - goto out_kfree; - sbi->flag = flag; + /* Transfer results of parsing to the sbi */ + sbi->flag = ctx->flag; + sbi->uid = ctx->uid; + sbi->gid = ctx->gid; + sbi->umask = ctx->umask; + if (ctx->nls_map != (void *) -1) { + unload_nls(sbi->nls_tab); + sbi->nls_tab = ctx->nls_map; + } + ctx->nls_map = NULL; + + if (sbi->flag & JFS_DISCARD) { + if (!bdev_max_discard_sectors(sb->s_bdev)) { + pr_err("JFS: discard option not supported on device\n"); + sbi->flag &= ~JFS_DISCARD; + } else { + sbi->minblks_trim = ctx->minblks_trim; + } + } #ifdef CONFIG_JFS_POSIX_ACL sb->s_flags |= SB_POSIXACL; #endif - if (newLVSize) { + if (ctx->resize) { pr_err("resize option for remount only\n"); - goto out_kfree; + goto out_unload; } /* @@ -608,7 +575,6 @@ out_mount_failed: sbi->direct_inode = NULL; out_unload: unload_nls(sbi->nls_tab); -out_kfree: kfree(sbi); return ret; } @@ -664,10 +630,9 @@ out: return rc; } -static struct dentry *jfs_do_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int jfs_get_tree(struct fs_context *fc) { - return mount_bdev(fs_type, flags, dev_name, data, jfs_fill_super); + return get_tree_bdev(fc, jfs_fill_super); } static int jfs_sync_fs(struct super_block *sb, int wait) @@ -801,7 +766,7 @@ static ssize_t jfs_quota_write(struct super_block *sb, int type, } lock_buffer(bh); memcpy(bh->b_data+offset, data, tocopy); - flush_dcache_page(bh->b_page); + flush_dcache_folio(bh->b_folio); set_buffer_uptodate(bh); mark_buffer_dirty(bh); unlock_buffer(bh); @@ -886,7 +851,6 @@ static const struct super_operations jfs_super_operations = { .freeze_fs = jfs_freeze, .unfreeze_fs = jfs_unfreeze, .statfs = jfs_statfs, - .remount_fs = jfs_remount, .show_options = jfs_show_options, #ifdef CONFIG_QUOTA .quota_read = jfs_quota_read, @@ -902,12 +866,71 @@ static const struct export_operations jfs_export_operations = { .get_parent = jfs_get_parent, }; +static void jfs_init_options(struct fs_context *fc, struct jfs_context *ctx) +{ + if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) { + struct super_block *sb = fc->root->d_sb; + + /* Copy over current option values and mount flags */ + ctx->uid = JFS_SBI(sb)->uid; + ctx->gid = JFS_SBI(sb)->gid; + ctx->umask = JFS_SBI(sb)->umask; + ctx->nls_map = (void *)-1; + ctx->minblks_trim = JFS_SBI(sb)->minblks_trim; + ctx->flag = JFS_SBI(sb)->flag; + + } else { + /* + * Initialize the mount flag and determine the default + * error handler + */ + ctx->flag = JFS_ERR_REMOUNT_RO; + ctx->uid = INVALID_UID; + ctx->gid = INVALID_GID; + ctx->umask = -1; + ctx->nls_map = (void *)-1; + } +} + +static void jfs_free_fc(struct fs_context *fc) +{ + struct jfs_context *ctx = fc->fs_private; + + if (ctx->nls_map != (void *) -1) + unload_nls(ctx->nls_map); + kfree(ctx); +} + +static const struct fs_context_operations jfs_context_ops = { + .parse_param = jfs_parse_param, + .get_tree = jfs_get_tree, + .reconfigure = jfs_reconfigure, + .free = jfs_free_fc, +}; + +static int jfs_init_fs_context(struct fs_context *fc) +{ + struct jfs_context *ctx; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + jfs_init_options(fc, ctx); + + fc->fs_private = ctx; + fc->ops = &jfs_context_ops; + + return 0; +} + static struct file_system_type jfs_fs_type = { .owner = THIS_MODULE, .name = "jfs", - .mount = jfs_do_mount, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV, + .init_fs_context = jfs_init_fs_context, + .parameters = jfs_param_spec, }; MODULE_ALIAS_FS("jfs"); diff --git a/fs/jfs/xattr.c b/fs/jfs/xattr.c index 0fb7afac298e..11d7f74d207b 100644 --- a/fs/jfs/xattr.c +++ b/fs/jfs/xattr.c @@ -434,6 +434,8 @@ static int ea_get(struct inode *inode, struct ea_buffer *ea_buf, int min_size) int rc; int quota_allocation = 0; + memset(&ea_buf->new_ea, 0, sizeof(ea_buf->new_ea)); + /* When fsck.jfs clears a bad ea, it doesn't clear the size */ if (ji->ea.flag == 0) ea_size = 0; @@ -557,9 +559,16 @@ static int ea_get(struct inode *inode, struct ea_buffer *ea_buf, int min_size) size_check: if (EALIST_SIZE(ea_buf->xattr) != ea_size) { - printk(KERN_ERR "ea_get: invalid extended attribute\n"); - print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 16, 1, - ea_buf->xattr, ea_size, 1); + if (unlikely(EALIST_SIZE(ea_buf->xattr) > INT_MAX)) { + printk(KERN_ERR "ea_get: extended attribute size too large: %u > INT_MAX\n", + EALIST_SIZE(ea_buf->xattr)); + } else { + int size = clamp_t(int, ea_size, 0, EALIST_SIZE(ea_buf->xattr)); + + printk(KERN_ERR "ea_get: invalid extended attribute\n"); + print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 16, 1, + ea_buf->xattr, size, 1); + } ea_release(inode, ea_buf); rc = -EIO; goto clean_up; @@ -795,7 +804,7 @@ ssize_t __jfs_getxattr(struct inode *inode, const char *name, void *data, size_t buf_size) { struct jfs_ea_list *ealist; - struct jfs_ea *ea; + struct jfs_ea *ea, *ealist_end; struct ea_buffer ea_buf; int xattr_size; ssize_t size; @@ -815,9 +824,16 @@ ssize_t __jfs_getxattr(struct inode *inode, const char *name, void *data, goto not_found; ealist = (struct jfs_ea_list *) ea_buf.xattr; + ealist_end = END_EALIST(ealist); /* Find the named attribute */ - for (ea = FIRST_EA(ealist); ea < END_EALIST(ealist); ea = NEXT_EA(ea)) + for (ea = FIRST_EA(ealist); ea < ealist_end; ea = NEXT_EA(ea)) { + if (unlikely(ea + 1 > ealist_end) || + unlikely(NEXT_EA(ea) > ealist_end)) { + size = -EUCLEAN; + goto release; + } + if ((namelen == ea->namelen) && memcmp(name, ea->name, namelen) == 0) { /* Found it */ @@ -832,6 +848,7 @@ ssize_t __jfs_getxattr(struct inode *inode, const char *name, void *data, memcpy(data, value, size); goto release; } + } not_found: size = -ENODATA; release: @@ -859,7 +876,7 @@ ssize_t jfs_listxattr(struct dentry * dentry, char *data, size_t buf_size) ssize_t size = 0; int xattr_size; struct jfs_ea_list *ealist; - struct jfs_ea *ea; + struct jfs_ea *ea, *ealist_end; struct ea_buffer ea_buf; down_read(&JFS_IP(inode)->xattr_sem); @@ -874,9 +891,16 @@ ssize_t jfs_listxattr(struct dentry * dentry, char *data, size_t buf_size) goto release; ealist = (struct jfs_ea_list *) ea_buf.xattr; + ealist_end = END_EALIST(ealist); /* compute required size of list */ - for (ea = FIRST_EA(ealist); ea < END_EALIST(ealist); ea = NEXT_EA(ea)) { + for (ea = FIRST_EA(ealist); ea < ealist_end; ea = NEXT_EA(ea)) { + if (unlikely(ea + 1 > ealist_end) || + unlikely(NEXT_EA(ea) > ealist_end)) { + size = -EUCLEAN; + goto release; + } + if (can_list(ea)) size += name_size(ea) + 1; } |