Skip to content

Commit 6545b24

Browse files
author
Phillip Lougher
committed
Squashfs: inode operations
Signed-off-by: Phillip Lougher <phillip@lougher.demon.co.uk>
1 parent fe0bdec commit 6545b24

File tree

1 file changed

+346
-0
lines changed

1 file changed

+346
-0
lines changed

fs/squashfs/inode.c

Lines changed: 346 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
1+
/*
2+
* Squashfs - a compressed read only filesystem for Linux
3+
*
4+
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
5+
* Phillip Lougher <phillip@lougher.demon.co.uk>
6+
*
7+
* This program is free software; you can redistribute it and/or
8+
* modify it under the terms of the GNU General Public License
9+
* as published by the Free Software Foundation; either version 2,
10+
* or (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program; if not, write to the Free Software
19+
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20+
*
21+
* inode.c
22+
*/
23+
24+
/*
25+
* This file implements code to create and read inodes from disk.
26+
*
27+
* Inodes in Squashfs are identified by a 48-bit inode which encodes the
28+
* location of the compressed metadata block containing the inode, and the byte
29+
* offset into that block where the inode is placed (<block, offset>).
30+
*
31+
* To maximise compression there are different inodes for each file type
32+
* (regular file, directory, device, etc.), the inode contents and length
33+
* varying with the type.
34+
*
35+
* To further maximise compression, two types of regular file inode and
36+
* directory inode are defined: inodes optimised for frequently occurring
37+
* regular files and directories, and extended types where extra
38+
* information has to be stored.
39+
*/
40+
41+
#include <linux/fs.h>
42+
#include <linux/vfs.h>
43+
#include <linux/zlib.h>
44+
45+
#include "squashfs_fs.h"
46+
#include "squashfs_fs_sb.h"
47+
#include "squashfs_fs_i.h"
48+
#include "squashfs.h"
49+
50+
/*
51+
* Initialise VFS inode with the base inode information common to all
52+
* Squashfs inode types. Sqsh_ino contains the unswapped base inode
53+
* off disk.
54+
*/
55+
static int squashfs_new_inode(struct super_block *sb, struct inode *inode,
56+
struct squashfs_base_inode *sqsh_ino)
57+
{
58+
int err;
59+
60+
err = squashfs_get_id(sb, le16_to_cpu(sqsh_ino->uid), &inode->i_uid);
61+
if (err)
62+
return err;
63+
64+
err = squashfs_get_id(sb, le16_to_cpu(sqsh_ino->guid), &inode->i_gid);
65+
if (err)
66+
return err;
67+
68+
inode->i_ino = le32_to_cpu(sqsh_ino->inode_number);
69+
inode->i_mtime.tv_sec = le32_to_cpu(sqsh_ino->mtime);
70+
inode->i_atime.tv_sec = inode->i_mtime.tv_sec;
71+
inode->i_ctime.tv_sec = inode->i_mtime.tv_sec;
72+
inode->i_mode = le16_to_cpu(sqsh_ino->mode);
73+
inode->i_size = 0;
74+
75+
return err;
76+
}
77+
78+
79+
struct inode *squashfs_iget(struct super_block *sb, long long ino,
80+
unsigned int ino_number)
81+
{
82+
struct inode *inode = iget_locked(sb, ino_number);
83+
int err;
84+
85+
TRACE("Entered squashfs_iget\n");
86+
87+
if (!inode)
88+
return ERR_PTR(-ENOMEM);
89+
if (!(inode->i_state & I_NEW))
90+
return inode;
91+
92+
err = squashfs_read_inode(inode, ino);
93+
if (err) {
94+
iget_failed(inode);
95+
return ERR_PTR(err);
96+
}
97+
98+
unlock_new_inode(inode);
99+
return inode;
100+
}
101+
102+
103+
/*
104+
* Initialise VFS inode by reading inode from inode table (compressed
105+
* metadata). The format and amount of data read depends on type.
106+
*/
107+
int squashfs_read_inode(struct inode *inode, long long ino)
108+
{
109+
struct super_block *sb = inode->i_sb;
110+
struct squashfs_sb_info *msblk = sb->s_fs_info;
111+
u64 block = SQUASHFS_INODE_BLK(ino) + msblk->inode_table;
112+
int err, type, offset = SQUASHFS_INODE_OFFSET(ino);
113+
union squashfs_inode squashfs_ino;
114+
struct squashfs_base_inode *sqshb_ino = &squashfs_ino.base;
115+
116+
TRACE("Entered squashfs_read_inode\n");
117+
118+
/*
119+
* Read inode base common to all inode types.
120+
*/
121+
err = squashfs_read_metadata(sb, sqshb_ino, &block,
122+
&offset, sizeof(*sqshb_ino));
123+
if (err < 0)
124+
goto failed_read;
125+
126+
err = squashfs_new_inode(sb, inode, sqshb_ino);
127+
if (err)
128+
goto failed_read;
129+
130+
block = SQUASHFS_INODE_BLK(ino) + msblk->inode_table;
131+
offset = SQUASHFS_INODE_OFFSET(ino);
132+
133+
type = le16_to_cpu(sqshb_ino->inode_type);
134+
switch (type) {
135+
case SQUASHFS_REG_TYPE: {
136+
unsigned int frag_offset, frag_size, frag;
137+
u64 frag_blk;
138+
struct squashfs_reg_inode *sqsh_ino = &squashfs_ino.reg;
139+
140+
err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
141+
sizeof(*sqsh_ino));
142+
if (err < 0)
143+
goto failed_read;
144+
145+
frag = le32_to_cpu(sqsh_ino->fragment);
146+
if (frag != SQUASHFS_INVALID_FRAG) {
147+
frag_offset = le32_to_cpu(sqsh_ino->offset);
148+
frag_size = squashfs_frag_lookup(sb, frag, &frag_blk);
149+
if (frag_size < 0) {
150+
err = frag_size;
151+
goto failed_read;
152+
}
153+
} else {
154+
frag_blk = SQUASHFS_INVALID_BLK;
155+
frag_size = 0;
156+
frag_offset = 0;
157+
}
158+
159+
inode->i_nlink = 1;
160+
inode->i_size = le32_to_cpu(sqsh_ino->file_size);
161+
inode->i_fop = &generic_ro_fops;
162+
inode->i_mode |= S_IFREG;
163+
inode->i_blocks = ((inode->i_size - 1) >> 9) + 1;
164+
squashfs_i(inode)->fragment_block = frag_blk;
165+
squashfs_i(inode)->fragment_size = frag_size;
166+
squashfs_i(inode)->fragment_offset = frag_offset;
167+
squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block);
168+
squashfs_i(inode)->block_list_start = block;
169+
squashfs_i(inode)->offset = offset;
170+
inode->i_data.a_ops = &squashfs_aops;
171+
172+
TRACE("File inode %x:%x, start_block %llx, block_list_start "
173+
"%llx, offset %x\n", SQUASHFS_INODE_BLK(ino),
174+
offset, squashfs_i(inode)->start, block, offset);
175+
break;
176+
}
177+
case SQUASHFS_LREG_TYPE: {
178+
unsigned int frag_offset, frag_size, frag;
179+
u64 frag_blk;
180+
struct squashfs_lreg_inode *sqsh_ino = &squashfs_ino.lreg;
181+
182+
err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
183+
sizeof(*sqsh_ino));
184+
if (err < 0)
185+
goto failed_read;
186+
187+
frag = le32_to_cpu(sqsh_ino->fragment);
188+
if (frag != SQUASHFS_INVALID_FRAG) {
189+
frag_offset = le32_to_cpu(sqsh_ino->offset);
190+
frag_size = squashfs_frag_lookup(sb, frag, &frag_blk);
191+
if (frag_size < 0) {
192+
err = frag_size;
193+
goto failed_read;
194+
}
195+
} else {
196+
frag_blk = SQUASHFS_INVALID_BLK;
197+
frag_size = 0;
198+
frag_offset = 0;
199+
}
200+
201+
inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
202+
inode->i_size = le64_to_cpu(sqsh_ino->file_size);
203+
inode->i_fop = &generic_ro_fops;
204+
inode->i_mode |= S_IFREG;
205+
inode->i_blocks = ((inode->i_size -
206+
le64_to_cpu(sqsh_ino->sparse) - 1) >> 9) + 1;
207+
208+
squashfs_i(inode)->fragment_block = frag_blk;
209+
squashfs_i(inode)->fragment_size = frag_size;
210+
squashfs_i(inode)->fragment_offset = frag_offset;
211+
squashfs_i(inode)->start = le64_to_cpu(sqsh_ino->start_block);
212+
squashfs_i(inode)->block_list_start = block;
213+
squashfs_i(inode)->offset = offset;
214+
inode->i_data.a_ops = &squashfs_aops;
215+
216+
TRACE("File inode %x:%x, start_block %llx, block_list_start "
217+
"%llx, offset %x\n", SQUASHFS_INODE_BLK(ino),
218+
offset, squashfs_i(inode)->start, block, offset);
219+
break;
220+
}
221+
case SQUASHFS_DIR_TYPE: {
222+
struct squashfs_dir_inode *sqsh_ino = &squashfs_ino.dir;
223+
224+
err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
225+
sizeof(*sqsh_ino));
226+
if (err < 0)
227+
goto failed_read;
228+
229+
inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
230+
inode->i_size = le16_to_cpu(sqsh_ino->file_size);
231+
inode->i_op = &squashfs_dir_inode_ops;
232+
inode->i_fop = &squashfs_dir_ops;
233+
inode->i_mode |= S_IFDIR;
234+
squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block);
235+
squashfs_i(inode)->offset = le16_to_cpu(sqsh_ino->offset);
236+
squashfs_i(inode)->dir_idx_cnt = 0;
237+
squashfs_i(inode)->parent = le32_to_cpu(sqsh_ino->parent_inode);
238+
239+
TRACE("Directory inode %x:%x, start_block %llx, offset %x\n",
240+
SQUASHFS_INODE_BLK(ino), offset,
241+
squashfs_i(inode)->start,
242+
le16_to_cpu(sqsh_ino->offset));
243+
break;
244+
}
245+
case SQUASHFS_LDIR_TYPE: {
246+
struct squashfs_ldir_inode *sqsh_ino = &squashfs_ino.ldir;
247+
248+
err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
249+
sizeof(*sqsh_ino));
250+
if (err < 0)
251+
goto failed_read;
252+
253+
inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
254+
inode->i_size = le32_to_cpu(sqsh_ino->file_size);
255+
inode->i_op = &squashfs_dir_inode_ops;
256+
inode->i_fop = &squashfs_dir_ops;
257+
inode->i_mode |= S_IFDIR;
258+
squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block);
259+
squashfs_i(inode)->offset = le16_to_cpu(sqsh_ino->offset);
260+
squashfs_i(inode)->dir_idx_start = block;
261+
squashfs_i(inode)->dir_idx_offset = offset;
262+
squashfs_i(inode)->dir_idx_cnt = le16_to_cpu(sqsh_ino->i_count);
263+
squashfs_i(inode)->parent = le32_to_cpu(sqsh_ino->parent_inode);
264+
265+
TRACE("Long directory inode %x:%x, start_block %llx, offset "
266+
"%x\n", SQUASHFS_INODE_BLK(ino), offset,
267+
squashfs_i(inode)->start,
268+
le16_to_cpu(sqsh_ino->offset));
269+
break;
270+
}
271+
case SQUASHFS_SYMLINK_TYPE:
272+
case SQUASHFS_LSYMLINK_TYPE: {
273+
struct squashfs_symlink_inode *sqsh_ino = &squashfs_ino.symlink;
274+
275+
err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
276+
sizeof(*sqsh_ino));
277+
if (err < 0)
278+
goto failed_read;
279+
280+
inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
281+
inode->i_size = le32_to_cpu(sqsh_ino->symlink_size);
282+
inode->i_op = &page_symlink_inode_operations;
283+
inode->i_data.a_ops = &squashfs_symlink_aops;
284+
inode->i_mode |= S_IFLNK;
285+
squashfs_i(inode)->start = block;
286+
squashfs_i(inode)->offset = offset;
287+
288+
TRACE("Symbolic link inode %x:%x, start_block %llx, offset "
289+
"%x\n", SQUASHFS_INODE_BLK(ino), offset,
290+
block, offset);
291+
break;
292+
}
293+
case SQUASHFS_BLKDEV_TYPE:
294+
case SQUASHFS_CHRDEV_TYPE:
295+
case SQUASHFS_LBLKDEV_TYPE:
296+
case SQUASHFS_LCHRDEV_TYPE: {
297+
struct squashfs_dev_inode *sqsh_ino = &squashfs_ino.dev;
298+
unsigned int rdev;
299+
300+
err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
301+
sizeof(*sqsh_ino));
302+
if (err < 0)
303+
goto failed_read;
304+
305+
if (type == SQUASHFS_CHRDEV_TYPE)
306+
inode->i_mode |= S_IFCHR;
307+
else
308+
inode->i_mode |= S_IFBLK;
309+
inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
310+
rdev = le32_to_cpu(sqsh_ino->rdev);
311+
init_special_inode(inode, inode->i_mode, new_decode_dev(rdev));
312+
313+
TRACE("Device inode %x:%x, rdev %x\n",
314+
SQUASHFS_INODE_BLK(ino), offset, rdev);
315+
break;
316+
}
317+
case SQUASHFS_FIFO_TYPE:
318+
case SQUASHFS_SOCKET_TYPE:
319+
case SQUASHFS_LFIFO_TYPE:
320+
case SQUASHFS_LSOCKET_TYPE: {
321+
struct squashfs_ipc_inode *sqsh_ino = &squashfs_ino.ipc;
322+
323+
err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
324+
sizeof(*sqsh_ino));
325+
if (err < 0)
326+
goto failed_read;
327+
328+
if (type == SQUASHFS_FIFO_TYPE)
329+
inode->i_mode |= S_IFIFO;
330+
else
331+
inode->i_mode |= S_IFSOCK;
332+
inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
333+
init_special_inode(inode, inode->i_mode, 0);
334+
break;
335+
}
336+
default:
337+
ERROR("Unknown inode type %d in squashfs_iget!\n", type);
338+
return -EINVAL;
339+
}
340+
341+
return 0;
342+
343+
failed_read:
344+
ERROR("Unable to read inode 0x%llx\n", ino);
345+
return err;
346+
}

0 commit comments

Comments
 (0)