@@ -307,7 +307,8 @@ static int fill_meta_index(struct inode *inode, int index,
307307all_done :
308308 * index_block = cur_index_block ;
309309 * index_offset = cur_offset ;
310- * data_block = cur_data_block ;
310+ if (data_block )
311+ * data_block = cur_data_block ;
311312
312313 /*
313314 * Scale cache index (cache slot entry) to index
@@ -324,17 +325,15 @@ static int fill_meta_index(struct inode *inode, int index,
324325 * Get the on-disk location and compressed size of the datablock
325326 * specified by index. Fill_meta_index() does most of the work.
326327 */
327- static int read_blocklist (struct inode * inode , int index , u64 * block )
328+ static int read_blocklist_ptrs (struct inode * inode , int index , u64 * start ,
329+ int * offset , u64 * block )
328330{
329- u64 start ;
330331 long long blks ;
331- int offset ;
332332 __le32 size ;
333- int res = fill_meta_index (inode , index , & start , & offset , block );
333+ int res = fill_meta_index (inode , index , start , offset , block );
334334
335- TRACE ("read_blocklist: res %d, index %d, start 0x%llx, offset"
336- " 0x%x, block 0x%llx\n" , res , index , start , offset ,
337- * block );
335+ TRACE ("read_blocklist: res %d, index %d, start 0x%llx, offset 0x%x, block 0x%llx\n" ,
336+ res , index , * start , * offset , block ? * block : 0 );
338337
339338 if (res < 0 )
340339 return res ;
@@ -346,22 +345,31 @@ static int read_blocklist(struct inode *inode, int index, u64 *block)
346345 * extra block indexes needed.
347346 */
348347 if (res < index ) {
349- blks = read_indexes (inode -> i_sb , index - res , & start , & offset );
348+ blks = read_indexes (inode -> i_sb , index - res , start , offset );
350349 if (blks < 0 )
351350 return (int ) blks ;
352- * block += blks ;
351+ if (block )
352+ * block += blks ;
353353 }
354354
355355 /*
356356 * Read length of block specified by index.
357357 */
358- res = squashfs_read_metadata (inode -> i_sb , & size , & start , & offset ,
358+ res = squashfs_read_metadata (inode -> i_sb , & size , start , offset ,
359359 sizeof (size ));
360360 if (res < 0 )
361361 return res ;
362362 return squashfs_block_size (size );
363363}
364364
365+ static inline int read_blocklist (struct inode * inode , int index , u64 * block )
366+ {
367+ u64 start ;
368+ int offset ;
369+
370+ return read_blocklist_ptrs (inode , index , & start , & offset , block );
371+ }
372+
365373static bool squashfs_fill_page (struct folio * folio ,
366374 struct squashfs_cache_entry * buffer , size_t offset ,
367375 size_t avail )
@@ -658,7 +666,114 @@ static void squashfs_readahead(struct readahead_control *ractl)
658666 kfree (pages );
659667}
660668
669+ static loff_t seek_hole_data (struct file * file , loff_t offset , int whence )
670+ {
671+ struct inode * inode = file -> f_mapping -> host ;
672+ struct super_block * sb = inode -> i_sb ;
673+ struct squashfs_sb_info * msblk = sb -> s_fs_info ;
674+ u64 start , index = offset >> msblk -> block_log ;
675+ u64 file_end = (i_size_read (inode ) + msblk -> block_size - 1 ) >> msblk -> block_log ;
676+ int s_offset , length ;
677+ __le32 * blist = NULL ;
678+
679+ /* reject offset if negative or beyond file end */
680+ if ((unsigned long long )offset >= i_size_read (inode ))
681+ return - ENXIO ;
682+
683+ /* is offset within tailend and is tailend packed into a fragment? */
684+ if (index + 1 == file_end &&
685+ squashfs_i (inode )-> fragment_block != SQUASHFS_INVALID_BLK ) {
686+ if (whence == SEEK_DATA )
687+ return offset ;
688+
689+ /* there is an implicit hole at the end of any file */
690+ return i_size_read (inode );
691+ }
692+
693+ length = read_blocklist_ptrs (inode , index , & start , & s_offset , NULL );
694+ if (length < 0 )
695+ return length ;
696+
697+ /* nothing more to do if offset matches desired whence value */
698+ if ((length == 0 && whence == SEEK_HOLE ) ||
699+ (length && whence == SEEK_DATA ))
700+ return offset ;
701+
702+ /* skip scanning forwards if we're at file end */
703+ if (++ index == file_end )
704+ goto not_found ;
705+
706+ blist = kmalloc (SQUASHFS_SCAN_INDEXES << 2 , GFP_KERNEL );
707+ if (blist == NULL ) {
708+ ERROR ("%s: Failed to allocate block_list\n" , __func__ );
709+ return - ENOMEM ;
710+ }
711+
712+ while (index < file_end ) {
713+ int i , indexes = min (file_end - index , SQUASHFS_SCAN_INDEXES );
714+
715+ offset = squashfs_read_metadata (sb , blist , & start , & s_offset , indexes << 2 );
716+ if (offset < 0 )
717+ goto finished ;
718+
719+ for (i = 0 ; i < indexes ; i ++ ) {
720+ length = squashfs_block_size (blist [i ]);
721+ if (length < 0 ) {
722+ offset = length ;
723+ goto finished ;
724+ }
725+
726+ /* does this block match desired whence value? */
727+ if ((length == 0 && whence == SEEK_HOLE ) ||
728+ (length && whence == SEEK_DATA )) {
729+ offset = (index + i ) << msblk -> block_log ;
730+ goto finished ;
731+ }
732+ }
733+
734+ index += indexes ;
735+ }
736+
737+ not_found :
738+ /* whence value determines what happens */
739+ if (whence == SEEK_DATA )
740+ offset = - ENXIO ;
741+ else
742+ /* there is an implicit hole at the end of any file */
743+ offset = i_size_read (inode );
744+
745+ finished :
746+ kfree (blist );
747+ return offset ;
748+ }
749+
750+ static loff_t squashfs_llseek (struct file * file , loff_t offset , int whence )
751+ {
752+ struct inode * inode = file -> f_mapping -> host ;
753+
754+ switch (whence ) {
755+ default :
756+ return generic_file_llseek (file , offset , whence );
757+ case SEEK_DATA :
758+ case SEEK_HOLE :
759+ offset = seek_hole_data (file , offset , whence );
760+ break ;
761+ }
762+
763+ if (offset < 0 )
764+ return offset ;
765+
766+ return vfs_setpos (file , offset , inode -> i_sb -> s_maxbytes );
767+ }
768+
661769const struct address_space_operations squashfs_aops = {
662770 .read_folio = squashfs_read_folio ,
663771 .readahead = squashfs_readahead
664772};
773+
774+ const struct file_operations squashfs_file_operations = {
775+ .llseek = squashfs_llseek ,
776+ .read_iter = generic_file_read_iter ,
777+ .mmap_prepare = generic_file_readonly_mmap_prepare ,
778+ .splice_read = filemap_splice_read
779+ };
0 commit comments