Skip to content

Commit ebfd68c

Browse files
committed
zonefs: Fix O_APPEND async write handling
zonefs updates the size of a sequential zone file inode only on completion of direct writes. When executing asynchronous append writes (with a file open with O_APPEND or using RWF_APPEND), the use of the current inode size in generic_write_checks() to set an iocb offset thus leads to unaligned write if an application issues an append write operation with another write already being executed. Fix this problem by introducing zonefs_write_checks() as a modified version of generic_write_checks() using the file inode wp_offset for an append write iocb offset. Also introduce zonefs_write_check_limits() to replace generic_write_check_limits() call. This zonefs special helper makes sure that the maximum file limit used is the maximum size of the file being accessed. Since zonefs_write_checks() already truncates the iov_iter, the calls to iov_iter_truncate() in zonefs_file_dio_write() and zonefs_file_buffered_write() are removed. Fixes: 8dcc1a9 ("fs: New zonefs file system") Cc: <[email protected]> Reviewed-by: Johannes Thumshirn <[email protected]> Signed-off-by: Damien Le Moal <[email protected]>
1 parent 1601ea0 commit ebfd68c

File tree

1 file changed

+68
-10
lines changed

1 file changed

+68
-10
lines changed

fs/zonefs/super.c

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,68 @@ static ssize_t zonefs_file_dio_append(struct kiocb *iocb, struct iov_iter *from)
743743
return ret;
744744
}
745745

746+
/*
747+
* Do not exceed the LFS limits nor the file zone size. If pos is under the
748+
* limit it becomes a short access. If it exceeds the limit, return -EFBIG.
749+
*/
750+
static loff_t zonefs_write_check_limits(struct file *file, loff_t pos,
751+
loff_t count)
752+
{
753+
struct inode *inode = file_inode(file);
754+
struct zonefs_inode_info *zi = ZONEFS_I(inode);
755+
loff_t limit = rlimit(RLIMIT_FSIZE);
756+
loff_t max_size = zi->i_max_size;
757+
758+
if (limit != RLIM_INFINITY) {
759+
if (pos >= limit) {
760+
send_sig(SIGXFSZ, current, 0);
761+
return -EFBIG;
762+
}
763+
count = min(count, limit - pos);
764+
}
765+
766+
if (!(file->f_flags & O_LARGEFILE))
767+
max_size = min_t(loff_t, MAX_NON_LFS, max_size);
768+
769+
if (unlikely(pos >= max_size))
770+
return -EFBIG;
771+
772+
return min(count, max_size - pos);
773+
}
774+
775+
static ssize_t zonefs_write_checks(struct kiocb *iocb, struct iov_iter *from)
776+
{
777+
struct file *file = iocb->ki_filp;
778+
struct inode *inode = file_inode(file);
779+
struct zonefs_inode_info *zi = ZONEFS_I(inode);
780+
loff_t count;
781+
782+
if (IS_SWAPFILE(inode))
783+
return -ETXTBSY;
784+
785+
if (!iov_iter_count(from))
786+
return 0;
787+
788+
if ((iocb->ki_flags & IOCB_NOWAIT) && !(iocb->ki_flags & IOCB_DIRECT))
789+
return -EINVAL;
790+
791+
if (iocb->ki_flags & IOCB_APPEND) {
792+
if (zi->i_ztype != ZONEFS_ZTYPE_SEQ)
793+
return -EINVAL;
794+
mutex_lock(&zi->i_truncate_mutex);
795+
iocb->ki_pos = zi->i_wpoffset;
796+
mutex_unlock(&zi->i_truncate_mutex);
797+
}
798+
799+
count = zonefs_write_check_limits(file, iocb->ki_pos,
800+
iov_iter_count(from));
801+
if (count < 0)
802+
return count;
803+
804+
iov_iter_truncate(from, count);
805+
return iov_iter_count(from);
806+
}
807+
746808
/*
747809
* Handle direct writes. For sequential zone files, this is the only possible
748810
* write path. For these files, check that the user is issuing writes
@@ -760,8 +822,7 @@ static ssize_t zonefs_file_dio_write(struct kiocb *iocb, struct iov_iter *from)
760822
struct super_block *sb = inode->i_sb;
761823
bool sync = is_sync_kiocb(iocb);
762824
bool append = false;
763-
size_t count;
764-
ssize_t ret;
825+
ssize_t ret, count;
765826

766827
/*
767828
* For async direct IOs to sequential zone files, refuse IOCB_NOWAIT
@@ -779,12 +840,11 @@ static ssize_t zonefs_file_dio_write(struct kiocb *iocb, struct iov_iter *from)
779840
inode_lock(inode);
780841
}
781842

782-
ret = generic_write_checks(iocb, from);
783-
if (ret <= 0)
843+
count = zonefs_write_checks(iocb, from);
844+
if (count <= 0) {
845+
ret = count;
784846
goto inode_unlock;
785-
786-
iov_iter_truncate(from, zi->i_max_size - iocb->ki_pos);
787-
count = iov_iter_count(from);
847+
}
788848

789849
if ((iocb->ki_pos | count) & (sb->s_blocksize - 1)) {
790850
ret = -EINVAL;
@@ -844,12 +904,10 @@ static ssize_t zonefs_file_buffered_write(struct kiocb *iocb,
844904
inode_lock(inode);
845905
}
846906

847-
ret = generic_write_checks(iocb, from);
907+
ret = zonefs_write_checks(iocb, from);
848908
if (ret <= 0)
849909
goto inode_unlock;
850910

851-
iov_iter_truncate(from, zi->i_max_size - iocb->ki_pos);
852-
853911
ret = iomap_file_buffered_write(iocb, from, &zonefs_iomap_ops);
854912
if (ret > 0)
855913
iocb->ki_pos += ret;

0 commit comments

Comments
 (0)