return 0;
}
+static loff_t exfat_file_llseek(struct file *file, loff_t offset, int whence)
+{
+ struct inode *inode = file->f_mapping->host;
+
+ switch (whence) {
+ case SEEK_HOLE:
+ inode_lock_shared(inode);
+ offset = iomap_seek_hole(inode, offset, &exfat_iomap_ops);
+ inode_unlock_shared(inode);
+ break;
+ case SEEK_DATA:
+ inode_lock_shared(inode);
+ offset = iomap_seek_data(inode, offset, &exfat_iomap_ops);
+ inode_unlock_shared(inode);
+ break;
+ default:
+ return generic_file_llseek(file, offset, whence);
+ }
+ if (offset < 0)
+ return offset;
+ return vfs_setpos(file, offset, inode->i_sb->s_maxbytes);
+}
+
const struct file_operations exfat_file_operations = {
.open = exfat_file_open,
- .llseek = generic_file_llseek,
+ .llseek = exfat_file_llseek,
.read_iter = exfat_file_read_iter,
.write_iter = exfat_file_write_iter,
.unlocked_ioctl = exfat_ioctl,
iomap->flags |= IOMAP_F_ZERO_TAIL;
}
} else {
+ /*
+ * valid_size is tracked in byte granularity and
+ * marks the exact boundary between valid data and
+ * holes (or unwritten space).
+ *
+ * When IOMAP_REPORT is set (used by lseek(SEEK_HOLE)
+ * and SEEK_DATA), we return IOMAP_HOLE. This allows
+ * iomap_seek_hole_iter() to directly return the
+ * precise byte position.
+ *
+ * For normal I/O paths (without IOMAP_REPORT) we
+ * return IOMAP_UNWRITTEN so the write path can
+ * distinguish it from a real hole.
+ */
if (offset >= ei->valid_size) {
- iomap->type = IOMAP_UNWRITTEN;
+ iomap->type = flags & IOMAP_REPORT ?
+ IOMAP_HOLE : IOMAP_UNWRITTEN;
} else if (offset + iomap->length > ei->valid_size) {
- iomap->length = round_up(ei->valid_size,
- i_blocksize(inode)) -
- iomap->offset;
+ if (flags & IOMAP_REPORT) {
+ /*
+ * For SEEK_HOLE/SEEK_DATA, clip the length
+ * to the exact byte boundary (valid_size).
+ * This ensures the caller gets the precise
+ * hole position in byte units.
+ */
+ iomap->length = ei->valid_size - iomap->offset;
+ } else
+ iomap->length = round_up(ei->valid_size,
+ i_blocksize(inode)) -
+ iomap->offset;
}
}