Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
dadigang
Ventoy
Commits
05a1b863
Commit
05a1b863
authored
Apr 05, 2020
by
longpanda
Browse files
initial commit
parent
2090c6fa
Changes
263
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
7449 additions
and
0 deletions
+7449
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/inode.c
.../squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/inode.c
+346
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/namei.c
.../squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/namei.c
+242
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/squashfs.h
...uashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/squashfs.h
+90
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/squashfs_fs.h
...hfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/squashfs_fs.h
+381
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/squashfs_fs_i.h
...s-tools-4.4/kernel/kernel-2.6/fs/squashfs/squashfs_fs_i.h
+45
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/squashfs_fs_sb.h
...-tools-4.4/kernel/kernel-2.6/fs/squashfs/squashfs_fs_sb.h
+76
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/super.c
.../squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/super.c
+440
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/symlink.c
...quashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/symlink.c
+118
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/include/linux/squashfs_fs.h
...s-tools-4.4/kernel/kernel-2.6/include/linux/squashfs_fs.h
+380
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/include/linux/squashfs_fs_i.h
...tools-4.4/kernel/kernel-2.6/include/linux/squashfs_fs_i.h
+45
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/include/linux/squashfs_fs_sb.h
...ools-4.4/kernel/kernel-2.6/include/linux/squashfs_fs_sb.h
+76
-0
SQUASHFS/squashfs-tools-4.4/squashfs-tools/Makefile
SQUASHFS/squashfs-tools-4.4/squashfs-tools/Makefile
+365
-0
SQUASHFS/squashfs-tools-4.4/squashfs-tools/action.c
SQUASHFS/squashfs-tools-4.4/squashfs-tools/action.c
+3266
-0
SQUASHFS/squashfs-tools-4.4/squashfs-tools/action.h
SQUASHFS/squashfs-tools-4.4/squashfs-tools/action.h
+328
-0
SQUASHFS/squashfs-tools-4.4/squashfs-tools/build.sh
SQUASHFS/squashfs-tools-4.4/squashfs-tools/build.sh
+35
-0
SQUASHFS/squashfs-tools-4.4/squashfs-tools/caches-queues-lists.c
...S/squashfs-tools-4.4/squashfs-tools/caches-queues-lists.c
+642
-0
SQUASHFS/squashfs-tools-4.4/squashfs-tools/caches-queues-lists.h
...S/squashfs-tools-4.4/squashfs-tools/caches-queues-lists.h
+199
-0
SQUASHFS/squashfs-tools-4.4/squashfs-tools/compressor.c
SQUASHFS/squashfs-tools-4.4/squashfs-tools/compressor.c
+145
-0
SQUASHFS/squashfs-tools-4.4/squashfs-tools/compressor.h
SQUASHFS/squashfs-tools-4.4/squashfs-tools/compressor.h
+124
-0
SQUASHFS/squashfs-tools-4.4/squashfs-tools/error.h
SQUASHFS/squashfs-tools-4.4/squashfs-tools/error.h
+106
-0
No files found.
Too many changes to show.
To preserve performance only
263 of 263+
files are displayed.
Plain diff
Email patch
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/inode.c
0 → 100644
View file @
05a1b863
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* inode.c
*/
/*
* This file implements code to create and read inodes from disk.
*
* Inodes in Squashfs are identified by a 48-bit inode which encodes the
* location of the compressed metadata block containing the inode, and the byte
* offset into that block where the inode is placed (<block, offset>).
*
* To maximise compression there are different inodes for each file type
* (regular file, directory, device, etc.), the inode contents and length
* varying with the type.
*
* To further maximise compression, two types of regular file inode and
* directory inode are defined: inodes optimised for frequently occurring
* regular files and directories, and extended types where extra
* information has to be stored.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/zlib.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
/*
* Initialise VFS inode with the base inode information common to all
* Squashfs inode types. Sqsh_ino contains the unswapped base inode
* off disk.
*/
static
int
squashfs_new_inode
(
struct
super_block
*
sb
,
struct
inode
*
inode
,
struct
squashfs_base_inode
*
sqsh_ino
)
{
int
err
;
err
=
squashfs_get_id
(
sb
,
le16_to_cpu
(
sqsh_ino
->
uid
),
&
inode
->
i_uid
);
if
(
err
)
return
err
;
err
=
squashfs_get_id
(
sb
,
le16_to_cpu
(
sqsh_ino
->
guid
),
&
inode
->
i_gid
);
if
(
err
)
return
err
;
inode
->
i_ino
=
le32_to_cpu
(
sqsh_ino
->
inode_number
);
inode
->
i_mtime
.
tv_sec
=
le32_to_cpu
(
sqsh_ino
->
mtime
);
inode
->
i_atime
.
tv_sec
=
inode
->
i_mtime
.
tv_sec
;
inode
->
i_ctime
.
tv_sec
=
inode
->
i_mtime
.
tv_sec
;
inode
->
i_mode
=
le16_to_cpu
(
sqsh_ino
->
mode
);
inode
->
i_size
=
0
;
return
err
;
}
struct
inode
*
squashfs_iget
(
struct
super_block
*
sb
,
long
long
ino
,
unsigned
int
ino_number
)
{
struct
inode
*
inode
=
iget_locked
(
sb
,
ino_number
);
int
err
;
TRACE
(
"Entered squashfs_iget
\n
"
);
if
(
!
inode
)
return
ERR_PTR
(
-
ENOMEM
);
if
(
!
(
inode
->
i_state
&
I_NEW
))
return
inode
;
err
=
squashfs_read_inode
(
inode
,
ino
);
if
(
err
)
{
iget_failed
(
inode
);
return
ERR_PTR
(
err
);
}
unlock_new_inode
(
inode
);
return
inode
;
}
/*
* Initialise VFS inode by reading inode from inode table (compressed
* metadata). The format and amount of data read depends on type.
*/
int
squashfs_read_inode
(
struct
inode
*
inode
,
long
long
ino
)
{
struct
super_block
*
sb
=
inode
->
i_sb
;
struct
squashfs_sb_info
*
msblk
=
sb
->
s_fs_info
;
u64
block
=
SQUASHFS_INODE_BLK
(
ino
)
+
msblk
->
inode_table
;
int
err
,
type
,
offset
=
SQUASHFS_INODE_OFFSET
(
ino
);
union
squashfs_inode
squashfs_ino
;
struct
squashfs_base_inode
*
sqshb_ino
=
&
squashfs_ino
.
base
;
TRACE
(
"Entered squashfs_read_inode
\n
"
);
/*
* Read inode base common to all inode types.
*/
err
=
squashfs_read_metadata
(
sb
,
sqshb_ino
,
&
block
,
&
offset
,
sizeof
(
*
sqshb_ino
));
if
(
err
<
0
)
goto
failed_read
;
err
=
squashfs_new_inode
(
sb
,
inode
,
sqshb_ino
);
if
(
err
)
goto
failed_read
;
block
=
SQUASHFS_INODE_BLK
(
ino
)
+
msblk
->
inode_table
;
offset
=
SQUASHFS_INODE_OFFSET
(
ino
);
type
=
le16_to_cpu
(
sqshb_ino
->
inode_type
);
switch
(
type
)
{
case
SQUASHFS_REG_TYPE
:
{
unsigned
int
frag_offset
,
frag_size
,
frag
;
u64
frag_blk
;
struct
squashfs_reg_inode
*
sqsh_ino
=
&
squashfs_ino
.
reg
;
err
=
squashfs_read_metadata
(
sb
,
sqsh_ino
,
&
block
,
&
offset
,
sizeof
(
*
sqsh_ino
));
if
(
err
<
0
)
goto
failed_read
;
frag
=
le32_to_cpu
(
sqsh_ino
->
fragment
);
if
(
frag
!=
SQUASHFS_INVALID_FRAG
)
{
frag_offset
=
le32_to_cpu
(
sqsh_ino
->
offset
);
frag_size
=
squashfs_frag_lookup
(
sb
,
frag
,
&
frag_blk
);
if
(
frag_size
<
0
)
{
err
=
frag_size
;
goto
failed_read
;
}
}
else
{
frag_blk
=
SQUASHFS_INVALID_BLK
;
frag_size
=
0
;
frag_offset
=
0
;
}
inode
->
i_nlink
=
1
;
inode
->
i_size
=
le32_to_cpu
(
sqsh_ino
->
file_size
);
inode
->
i_fop
=
&
generic_ro_fops
;
inode
->
i_mode
|=
S_IFREG
;
inode
->
i_blocks
=
((
inode
->
i_size
-
1
)
>>
9
)
+
1
;
squashfs_i
(
inode
)
->
fragment_block
=
frag_blk
;
squashfs_i
(
inode
)
->
fragment_size
=
frag_size
;
squashfs_i
(
inode
)
->
fragment_offset
=
frag_offset
;
squashfs_i
(
inode
)
->
start
=
le32_to_cpu
(
sqsh_ino
->
start_block
);
squashfs_i
(
inode
)
->
block_list_start
=
block
;
squashfs_i
(
inode
)
->
offset
=
offset
;
inode
->
i_data
.
a_ops
=
&
squashfs_aops
;
TRACE
(
"File inode %x:%x, start_block %llx, block_list_start "
"%llx, offset %x
\n
"
,
SQUASHFS_INODE_BLK
(
ino
),
offset
,
squashfs_i
(
inode
)
->
start
,
block
,
offset
);
break
;
}
case
SQUASHFS_LREG_TYPE
:
{
unsigned
int
frag_offset
,
frag_size
,
frag
;
u64
frag_blk
;
struct
squashfs_lreg_inode
*
sqsh_ino
=
&
squashfs_ino
.
lreg
;
err
=
squashfs_read_metadata
(
sb
,
sqsh_ino
,
&
block
,
&
offset
,
sizeof
(
*
sqsh_ino
));
if
(
err
<
0
)
goto
failed_read
;
frag
=
le32_to_cpu
(
sqsh_ino
->
fragment
);
if
(
frag
!=
SQUASHFS_INVALID_FRAG
)
{
frag_offset
=
le32_to_cpu
(
sqsh_ino
->
offset
);
frag_size
=
squashfs_frag_lookup
(
sb
,
frag
,
&
frag_blk
);
if
(
frag_size
<
0
)
{
err
=
frag_size
;
goto
failed_read
;
}
}
else
{
frag_blk
=
SQUASHFS_INVALID_BLK
;
frag_size
=
0
;
frag_offset
=
0
;
}
inode
->
i_nlink
=
le32_to_cpu
(
sqsh_ino
->
nlink
);
inode
->
i_size
=
le64_to_cpu
(
sqsh_ino
->
file_size
);
inode
->
i_fop
=
&
generic_ro_fops
;
inode
->
i_mode
|=
S_IFREG
;
inode
->
i_blocks
=
((
inode
->
i_size
-
le64_to_cpu
(
sqsh_ino
->
sparse
)
-
1
)
>>
9
)
+
1
;
squashfs_i
(
inode
)
->
fragment_block
=
frag_blk
;
squashfs_i
(
inode
)
->
fragment_size
=
frag_size
;
squashfs_i
(
inode
)
->
fragment_offset
=
frag_offset
;
squashfs_i
(
inode
)
->
start
=
le64_to_cpu
(
sqsh_ino
->
start_block
);
squashfs_i
(
inode
)
->
block_list_start
=
block
;
squashfs_i
(
inode
)
->
offset
=
offset
;
inode
->
i_data
.
a_ops
=
&
squashfs_aops
;
TRACE
(
"File inode %x:%x, start_block %llx, block_list_start "
"%llx, offset %x
\n
"
,
SQUASHFS_INODE_BLK
(
ino
),
offset
,
squashfs_i
(
inode
)
->
start
,
block
,
offset
);
break
;
}
case
SQUASHFS_DIR_TYPE
:
{
struct
squashfs_dir_inode
*
sqsh_ino
=
&
squashfs_ino
.
dir
;
err
=
squashfs_read_metadata
(
sb
,
sqsh_ino
,
&
block
,
&
offset
,
sizeof
(
*
sqsh_ino
));
if
(
err
<
0
)
goto
failed_read
;
inode
->
i_nlink
=
le32_to_cpu
(
sqsh_ino
->
nlink
);
inode
->
i_size
=
le16_to_cpu
(
sqsh_ino
->
file_size
);
inode
->
i_op
=
&
squashfs_dir_inode_ops
;
inode
->
i_fop
=
&
squashfs_dir_ops
;
inode
->
i_mode
|=
S_IFDIR
;
squashfs_i
(
inode
)
->
start
=
le32_to_cpu
(
sqsh_ino
->
start_block
);
squashfs_i
(
inode
)
->
offset
=
le16_to_cpu
(
sqsh_ino
->
offset
);
squashfs_i
(
inode
)
->
dir_idx_cnt
=
0
;
squashfs_i
(
inode
)
->
parent
=
le32_to_cpu
(
sqsh_ino
->
parent_inode
);
TRACE
(
"Directory inode %x:%x, start_block %llx, offset %x
\n
"
,
SQUASHFS_INODE_BLK
(
ino
),
offset
,
squashfs_i
(
inode
)
->
start
,
le16_to_cpu
(
sqsh_ino
->
offset
));
break
;
}
case
SQUASHFS_LDIR_TYPE
:
{
struct
squashfs_ldir_inode
*
sqsh_ino
=
&
squashfs_ino
.
ldir
;
err
=
squashfs_read_metadata
(
sb
,
sqsh_ino
,
&
block
,
&
offset
,
sizeof
(
*
sqsh_ino
));
if
(
err
<
0
)
goto
failed_read
;
inode
->
i_nlink
=
le32_to_cpu
(
sqsh_ino
->
nlink
);
inode
->
i_size
=
le32_to_cpu
(
sqsh_ino
->
file_size
);
inode
->
i_op
=
&
squashfs_dir_inode_ops
;
inode
->
i_fop
=
&
squashfs_dir_ops
;
inode
->
i_mode
|=
S_IFDIR
;
squashfs_i
(
inode
)
->
start
=
le32_to_cpu
(
sqsh_ino
->
start_block
);
squashfs_i
(
inode
)
->
offset
=
le16_to_cpu
(
sqsh_ino
->
offset
);
squashfs_i
(
inode
)
->
dir_idx_start
=
block
;
squashfs_i
(
inode
)
->
dir_idx_offset
=
offset
;
squashfs_i
(
inode
)
->
dir_idx_cnt
=
le16_to_cpu
(
sqsh_ino
->
i_count
);
squashfs_i
(
inode
)
->
parent
=
le32_to_cpu
(
sqsh_ino
->
parent_inode
);
TRACE
(
"Long directory inode %x:%x, start_block %llx, offset "
"%x
\n
"
,
SQUASHFS_INODE_BLK
(
ino
),
offset
,
squashfs_i
(
inode
)
->
start
,
le16_to_cpu
(
sqsh_ino
->
offset
));
break
;
}
case
SQUASHFS_SYMLINK_TYPE
:
case
SQUASHFS_LSYMLINK_TYPE
:
{
struct
squashfs_symlink_inode
*
sqsh_ino
=
&
squashfs_ino
.
symlink
;
err
=
squashfs_read_metadata
(
sb
,
sqsh_ino
,
&
block
,
&
offset
,
sizeof
(
*
sqsh_ino
));
if
(
err
<
0
)
goto
failed_read
;
inode
->
i_nlink
=
le32_to_cpu
(
sqsh_ino
->
nlink
);
inode
->
i_size
=
le32_to_cpu
(
sqsh_ino
->
symlink_size
);
inode
->
i_op
=
&
page_symlink_inode_operations
;
inode
->
i_data
.
a_ops
=
&
squashfs_symlink_aops
;
inode
->
i_mode
|=
S_IFLNK
;
squashfs_i
(
inode
)
->
start
=
block
;
squashfs_i
(
inode
)
->
offset
=
offset
;
TRACE
(
"Symbolic link inode %x:%x, start_block %llx, offset "
"%x
\n
"
,
SQUASHFS_INODE_BLK
(
ino
),
offset
,
block
,
offset
);
break
;
}
case
SQUASHFS_BLKDEV_TYPE
:
case
SQUASHFS_CHRDEV_TYPE
:
case
SQUASHFS_LBLKDEV_TYPE
:
case
SQUASHFS_LCHRDEV_TYPE
:
{
struct
squashfs_dev_inode
*
sqsh_ino
=
&
squashfs_ino
.
dev
;
unsigned
int
rdev
;
err
=
squashfs_read_metadata
(
sb
,
sqsh_ino
,
&
block
,
&
offset
,
sizeof
(
*
sqsh_ino
));
if
(
err
<
0
)
goto
failed_read
;
if
(
type
==
SQUASHFS_CHRDEV_TYPE
)
inode
->
i_mode
|=
S_IFCHR
;
else
inode
->
i_mode
|=
S_IFBLK
;
inode
->
i_nlink
=
le32_to_cpu
(
sqsh_ino
->
nlink
);
rdev
=
le32_to_cpu
(
sqsh_ino
->
rdev
);
init_special_inode
(
inode
,
inode
->
i_mode
,
new_decode_dev
(
rdev
));
TRACE
(
"Device inode %x:%x, rdev %x
\n
"
,
SQUASHFS_INODE_BLK
(
ino
),
offset
,
rdev
);
break
;
}
case
SQUASHFS_FIFO_TYPE
:
case
SQUASHFS_SOCKET_TYPE
:
case
SQUASHFS_LFIFO_TYPE
:
case
SQUASHFS_LSOCKET_TYPE
:
{
struct
squashfs_ipc_inode
*
sqsh_ino
=
&
squashfs_ino
.
ipc
;
err
=
squashfs_read_metadata
(
sb
,
sqsh_ino
,
&
block
,
&
offset
,
sizeof
(
*
sqsh_ino
));
if
(
err
<
0
)
goto
failed_read
;
if
(
type
==
SQUASHFS_FIFO_TYPE
)
inode
->
i_mode
|=
S_IFIFO
;
else
inode
->
i_mode
|=
S_IFSOCK
;
inode
->
i_nlink
=
le32_to_cpu
(
sqsh_ino
->
nlink
);
init_special_inode
(
inode
,
inode
->
i_mode
,
0
);
break
;
}
default:
ERROR
(
"Unknown inode type %d in squashfs_iget!
\n
"
,
type
);
return
-
EINVAL
;
}
return
0
;
failed_read:
ERROR
(
"Unable to read inode 0x%llx
\n
"
,
ino
);
return
err
;
}
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/namei.c
0 → 100644
View file @
05a1b863
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* namei.c
*/
/*
* This file implements code to do filename lookup in directories.
*
* Like inodes, directories are packed into compressed metadata blocks, stored
* in a directory table. Directories are accessed using the start address of
* the metablock containing the directory and the offset into the
* decompressed block (<block, offset>).
*
* Directories are organised in a slightly complex way, and are not simply
* a list of file names. The organisation takes advantage of the
* fact that (in most cases) the inodes of the files will be in the same
* compressed metadata block, and therefore, can share the start block.
* Directories are therefore organised in a two level list, a directory
* header containing the shared start block value, and a sequence of directory
* entries, each of which share the shared start block. A new directory header
* is written once/if the inode start block changes. The directory
* header/directory entry list is repeated as many times as necessary.
*
* Directories are sorted, and can contain a directory index to speed up
* file lookup. Directory indexes store one entry per metablock, each entry
* storing the index/filename mapping to the first directory header
* in each metadata block. Directories are sorted in alphabetical order,
* and at lookup the index is scanned linearly looking for the first filename
* alphabetically larger than the filename being looked up. At this point the
* location of the metadata block the filename is in has been found.
* The general idea of the index is ensure only one metadata block needs to be
* decompressed to do a lookup irrespective of the length of the directory.
* This scheme has the advantage that it doesn't require extra memory overhead
* and doesn't require much extra storage on disk.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/dcache.h>
#include <linux/zlib.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
/*
* Lookup name in the directory index, returning the location of the metadata
* block containing it, and the directory index this represents.
*
* If we get an error reading the index then return the part of the index
* (if any) we have managed to read - the index isn't essential, just
* quicker.
*/
static
int
get_dir_index_using_name
(
struct
super_block
*
sb
,
u64
*
next_block
,
int
*
next_offset
,
u64
index_start
,
int
index_offset
,
int
i_count
,
const
char
*
name
,
int
len
)
{
struct
squashfs_sb_info
*
msblk
=
sb
->
s_fs_info
;
int
i
,
size
,
length
=
0
,
err
;
struct
squashfs_dir_index
*
index
;
char
*
str
;
TRACE
(
"Entered get_dir_index_using_name, i_count %d
\n
"
,
i_count
);
index
=
kmalloc
(
sizeof
(
*
index
)
+
SQUASHFS_NAME_LEN
*
2
+
2
,
GFP_KERNEL
);
if
(
index
==
NULL
)
{
ERROR
(
"Failed to allocate squashfs_dir_index
\n
"
);
goto
out
;
}
str
=
&
index
->
name
[
SQUASHFS_NAME_LEN
+
1
];
strncpy
(
str
,
name
,
len
);
str
[
len
]
=
'\0'
;
for
(
i
=
0
;
i
<
i_count
;
i
++
)
{
err
=
squashfs_read_metadata
(
sb
,
index
,
&
index_start
,
&
index_offset
,
sizeof
(
*
index
));
if
(
err
<
0
)
break
;
size
=
le32_to_cpu
(
index
->
size
)
+
1
;
err
=
squashfs_read_metadata
(
sb
,
index
->
name
,
&
index_start
,
&
index_offset
,
size
);
if
(
err
<
0
)
break
;
index
->
name
[
size
]
=
'\0'
;
if
(
strcmp
(
index
->
name
,
str
)
>
0
)
break
;
length
=
le32_to_cpu
(
index
->
index
);
*
next_block
=
le32_to_cpu
(
index
->
start_block
)
+
msblk
->
directory_table
;
}
*
next_offset
=
(
length
+
*
next_offset
)
%
SQUASHFS_METADATA_SIZE
;
kfree
(
index
);
out:
/*
* Return index (f_pos) of the looked up metadata block. Translate
* from internal f_pos to external f_pos which is offset by 3 because
* we invent "." and ".." entries which are not actually stored in the
* directory.
*/
return
length
+
3
;
}
static
struct
dentry
*
squashfs_lookup
(
struct
inode
*
dir
,
struct
dentry
*
dentry
,
struct
nameidata
*
nd
)
{
const
unsigned
char
*
name
=
dentry
->
d_name
.
name
;
int
len
=
dentry
->
d_name
.
len
;
struct
inode
*
inode
=
NULL
;
struct
squashfs_sb_info
*
msblk
=
dir
->
i_sb
->
s_fs_info
;
struct
squashfs_dir_header
dirh
;
struct
squashfs_dir_entry
*
dire
;
u64
block
=
squashfs_i
(
dir
)
->
start
+
msblk
->
directory_table
;
int
offset
=
squashfs_i
(
dir
)
->
offset
;
int
err
,
length
=
0
,
dir_count
,
size
;
TRACE
(
"Entered squashfs_lookup [%llx:%x]
\n
"
,
block
,
offset
);
dire
=
kmalloc
(
sizeof
(
*
dire
)
+
SQUASHFS_NAME_LEN
+
1
,
GFP_KERNEL
);
if
(
dire
==
NULL
)
{
ERROR
(
"Failed to allocate squashfs_dir_entry
\n
"
);
return
ERR_PTR
(
-
ENOMEM
);
}
if
(
len
>
SQUASHFS_NAME_LEN
)
{
err
=
-
ENAMETOOLONG
;
goto
failed
;
}
length
=
get_dir_index_using_name
(
dir
->
i_sb
,
&
block
,
&
offset
,
squashfs_i
(
dir
)
->
dir_idx_start
,
squashfs_i
(
dir
)
->
dir_idx_offset
,
squashfs_i
(
dir
)
->
dir_idx_cnt
,
name
,
len
);
while
(
length
<
i_size_read
(
dir
))
{
/*
* Read directory header.
*/
err
=
squashfs_read_metadata
(
dir
->
i_sb
,
&
dirh
,
&
block
,
&
offset
,
sizeof
(
dirh
));
if
(
err
<
0
)
goto
read_failure
;
length
+=
sizeof
(
dirh
);
dir_count
=
le32_to_cpu
(
dirh
.
count
)
+
1
;
while
(
dir_count
--
)
{
/*
* Read directory entry.
*/
err
=
squashfs_read_metadata
(
dir
->
i_sb
,
dire
,
&
block
,
&
offset
,
sizeof
(
*
dire
));
if
(
err
<
0
)
goto
read_failure
;
size
=
le16_to_cpu
(
dire
->
size
)
+
1
;
err
=
squashfs_read_metadata
(
dir
->
i_sb
,
dire
->
name
,
&
block
,
&
offset
,
size
);
if
(
err
<
0
)
goto
read_failure
;
length
+=
sizeof
(
*
dire
)
+
size
;
if
(
name
[
0
]
<
dire
->
name
[
0
])
goto
exit_lookup
;
if
(
len
==
size
&&
!
strncmp
(
name
,
dire
->
name
,
len
))
{
unsigned
int
blk
,
off
,
ino_num
;
long
long
ino
;
blk
=
le32_to_cpu
(
dirh
.
start_block
);
off
=
le16_to_cpu
(
dire
->
offset
);
ino_num
=
le32_to_cpu
(
dirh
.
inode_number
)
+
(
short
)
le16_to_cpu
(
dire
->
inode_number
);
ino
=
SQUASHFS_MKINODE
(
blk
,
off
);
TRACE
(
"calling squashfs_iget for directory "
"entry %s, inode %x:%x, %d
\n
"
,
name
,
blk
,
off
,
ino_num
);
inode
=
squashfs_iget
(
dir
->
i_sb
,
ino
,
ino_num
);
if
(
IS_ERR
(
inode
))
{
err
=
PTR_ERR
(
inode
);
goto
failed
;
}
goto
exit_lookup
;
}
}
}
exit_lookup:
kfree
(
dire
);
if
(
inode
)
return
d_splice_alias
(
inode
,
dentry
);
d_add
(
dentry
,
inode
);
return
ERR_PTR
(
0
);
read_failure:
ERROR
(
"Unable to read directory block [%llx:%x]
\n
"
,
squashfs_i
(
dir
)
->
start
+
msblk
->
directory_table
,
squashfs_i
(
dir
)
->
offset
);
failed:
kfree
(
dire
);
return
ERR_PTR
(
err
);
}
const
struct
inode_operations
squashfs_dir_inode_ops
=
{
.
lookup
=
squashfs_lookup
};
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/squashfs.h
0 → 100644
View file @
05a1b863
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* squashfs.h
*/
#define TRACE(s, args...) pr_debug("SQUASHFS: "s, ## args)
#define ERROR(s, args...) pr_err("SQUASHFS error: "s, ## args)
#define WARNING(s, args...) pr_warning("SQUASHFS: "s, ## args)
static
inline
struct
squashfs_inode_info
*
squashfs_i
(
struct
inode
*
inode
)
{
return
list_entry
(
inode
,
struct
squashfs_inode_info
,
vfs_inode
);
}
/* block.c */
extern
int
squashfs_read_data
(
struct
super_block
*
,
void
**
,
u64
,
int
,
u64
*
,
int
);
/* cache.c */
extern
struct
squashfs_cache
*
squashfs_cache_init
(
char
*
,
int
,
int
);
extern
void
squashfs_cache_delete
(
struct
squashfs_cache
*
);
extern
struct
squashfs_cache_entry
*
squashfs_cache_get
(
struct
super_block
*
,
struct
squashfs_cache
*
,
u64
,
int
);
extern
void
squashfs_cache_put
(
struct
squashfs_cache_entry
*
);
extern
int
squashfs_copy_data
(
void
*
,
struct
squashfs_cache_entry
*
,
int
,
int
);
extern
int
squashfs_read_metadata
(
struct
super_block
*
,
void
*
,
u64
*
,
int
*
,
int
);
extern
struct
squashfs_cache_entry
*
squashfs_get_fragment
(
struct
super_block
*
,
u64
,
int
);
extern
struct
squashfs_cache_entry
*
squashfs_get_datablock
(
struct
super_block
*
,
u64
,
int
);
extern
int
squashfs_read_table
(
struct
super_block
*
,
void
*
,
u64
,
int
);
/* export.c */
extern
__le64
*
squashfs_read_inode_lookup_table
(
struct
super_block
*
,
u64
,
unsigned
int
);
/* fragment.c */
extern
int
squashfs_frag_lookup
(
struct
super_block
*
,
unsigned
int
,
u64
*
);
extern
__le64
*
squashfs_read_fragment_index_table
(
struct
super_block
*
,
u64
,
unsigned
int
);
/* id.c */
extern
int
squashfs_get_id
(
struct
super_block
*
,
unsigned
int
,
unsigned
int
*
);
extern
__le64
*
squashfs_read_id_index_table
(
struct
super_block
*
,
u64
,
unsigned
short
);
/* inode.c */
extern
struct
inode
*
squashfs_iget
(
struct
super_block
*
,
long
long
,
unsigned
int
);
extern
int
squashfs_read_inode
(
struct
inode
*
,
long
long
);
/*
* Inodes and files operations
*/
/* dir.c */
extern
const
struct
file_operations
squashfs_dir_ops
;
/* export.c */
extern
const
struct
export_operations
squashfs_export_ops
;
/* file.c */
extern
const
struct
address_space_operations
squashfs_aops
;
/* namei.c */
extern
const
struct
inode_operations
squashfs_dir_inode_ops
;
/* symlink.c */
extern
const
struct
address_space_operations
squashfs_symlink_aops
;
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/squashfs_fs.h
0 → 100644
View file @
05a1b863
#ifndef SQUASHFS_FS
#define SQUASHFS_FS
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* squashfs_fs.h
*/
#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE
#define SQUASHFS_MAJOR 4
#define SQUASHFS_MINOR 0
#define SQUASHFS_MAGIC 0x73717368
#define SQUASHFS_START 0
/* size of metadata (inode and directory) blocks */
#define SQUASHFS_METADATA_SIZE 8192
#define SQUASHFS_METADATA_LOG 13
/* default size of data blocks */
#define SQUASHFS_FILE_SIZE 131072
#define SQUASHFS_FILE_LOG 17
#define SQUASHFS_FILE_MAX_SIZE 1048576
#define SQUASHFS_FILE_MAX_LOG 20
/* Max number of uids and gids */
#define SQUASHFS_IDS 65536
/* Max length of filename (not 255) */
#define SQUASHFS_NAME_LEN 256
#define SQUASHFS_INVALID_FRAG (0xffffffffU)
#define SQUASHFS_INVALID_BLK (-1LL)
/* Filesystem flags */
#define SQUASHFS_NOI 0
#define SQUASHFS_NOD 1
#define SQUASHFS_NOF 3
#define SQUASHFS_NO_FRAG 4
#define SQUASHFS_ALWAYS_FRAG 5
#define SQUASHFS_DUPLICATE 6
#define SQUASHFS_EXPORT 7
#define SQUASHFS_BIT(flag, bit) ((flag >> bit) & 1)
#define SQUASHFS_UNCOMPRESSED_INODES(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOI)
#define SQUASHFS_UNCOMPRESSED_DATA(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOD)
#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOF)
#define SQUASHFS_NO_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NO_FRAG)
#define SQUASHFS_ALWAYS_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_ALWAYS_FRAG)
#define SQUASHFS_DUPLICATES(flags) SQUASHFS_BIT(flags, \
SQUASHFS_DUPLICATE)
#define SQUASHFS_EXPORTABLE(flags) SQUASHFS_BIT(flags, \
SQUASHFS_EXPORT)
/* Max number of types and file types */
#define SQUASHFS_DIR_TYPE 1
#define SQUASHFS_REG_TYPE 2
#define SQUASHFS_SYMLINK_TYPE 3
#define SQUASHFS_BLKDEV_TYPE 4
#define SQUASHFS_CHRDEV_TYPE 5
#define SQUASHFS_FIFO_TYPE 6
#define SQUASHFS_SOCKET_TYPE 7
#define SQUASHFS_LDIR_TYPE 8
#define SQUASHFS_LREG_TYPE 9
#define SQUASHFS_LSYMLINK_TYPE 10
#define SQUASHFS_LBLKDEV_TYPE 11
#define SQUASHFS_LCHRDEV_TYPE 12
#define SQUASHFS_LFIFO_TYPE 13
#define SQUASHFS_LSOCKET_TYPE 14
/* Flag whether block is compressed or uncompressed, bit is set if block is
* uncompressed */
#define SQUASHFS_COMPRESSED_BIT (1 << 15)
#define SQUASHFS_COMPRESSED_SIZE(B) (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \
(B) & ~SQUASHFS_COMPRESSED_BIT : SQUASHFS_COMPRESSED_BIT)
#define SQUASHFS_COMPRESSED(B) (!((B) & SQUASHFS_COMPRESSED_BIT))
#define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24)
#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B) ((B) & \
~SQUASHFS_COMPRESSED_BIT_BLOCK)
#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK))
/*
* Inode number ops. Inodes consist of a compressed block number, and an
* uncompressed offset within that block
*/
#define SQUASHFS_INODE_BLK(A) ((unsigned int) ((A) >> 16))
#define SQUASHFS_INODE_OFFSET(A) ((unsigned int) ((A) & 0xffff))
#define SQUASHFS_MKINODE(A, B) ((long long)(((long long) (A)\
<< 16) + (B)))
/* Translate between VFS mode and squashfs mode */
#define SQUASHFS_MODE(A) ((A) & 0xfff)
/* fragment and fragment table defines */
#define SQUASHFS_FRAGMENT_BYTES(A) \
((A) * sizeof(struct squashfs_fragment_entry))
#define SQUASHFS_FRAGMENT_INDEX(A) (SQUASHFS_FRAGMENT_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A) (SQUASHFS_FRAGMENT_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEXES(A) ((SQUASHFS_FRAGMENT_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_BYTES(A) (SQUASHFS_FRAGMENT_INDEXES(A) *\
sizeof(u64))
/* inode lookup table defines */
#define SQUASHFS_LOOKUP_BYTES(A) ((A) * sizeof(u64))
#define SQUASHFS_LOOKUP_BLOCK(A) (SQUASHFS_LOOKUP_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCK_OFFSET(A) (SQUASHFS_LOOKUP_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCKS(A) ((SQUASHFS_LOOKUP_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCK_BYTES(A) (SQUASHFS_LOOKUP_BLOCKS(A) *\
sizeof(u64))
/* uid/gid lookup table defines */
#define SQUASHFS_ID_BYTES(A) ((A) * sizeof(unsigned int))
#define SQUASHFS_ID_BLOCK(A) (SQUASHFS_ID_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_ID_BLOCK_OFFSET(A) (SQUASHFS_ID_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_ID_BLOCKS(A) ((SQUASHFS_ID_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_ID_BLOCK_BYTES(A) (SQUASHFS_ID_BLOCKS(A) *\
sizeof(u64))
/* cached data constants for filesystem */
#define SQUASHFS_CACHED_BLKS 8
#define SQUASHFS_MAX_FILE_SIZE_LOG 64
#define SQUASHFS_MAX_FILE_SIZE (1LL << \
(SQUASHFS_MAX_FILE_SIZE_LOG - 2))
#define SQUASHFS_MARKER_BYTE 0xff
/* meta index cache */
#define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int))
#define SQUASHFS_META_ENTRIES 127
#define SQUASHFS_META_SLOTS 8
struct
meta_entry
{
u64
data_block
;
unsigned
int
index_block
;
unsigned
short
offset
;
unsigned
short
pad
;
};
struct
meta_index
{
unsigned
int
inode_number
;
unsigned
int
offset
;
unsigned
short
entries
;
unsigned
short
skip
;
unsigned
short
locked
;
unsigned
short
pad
;
struct
meta_entry
meta_entry
[
SQUASHFS_META_ENTRIES
];
};
/*
* definitions for structures on disk
*/
#define ZLIB_COMPRESSION 1
struct
squashfs_super_block
{
__le32
s_magic
;
__le32
inodes
;
__le32
mkfs_time
;
__le32
block_size
;
__le32
fragments
;
__le16
compression
;
__le16
block_log
;
__le16
flags
;
__le16
no_ids
;
__le16
s_major
;
__le16
s_minor
;
__le64
root_inode
;
__le64
bytes_used
;
__le64
id_table_start
;
__le64
xattr_table_start
;
__le64
inode_table_start
;
__le64
directory_table_start
;
__le64
fragment_table_start
;
__le64
lookup_table_start
;
};
struct
squashfs_dir_index
{
__le32
index
;
__le32
start_block
;
__le32
size
;
unsigned
char
name
[
0
];
};
struct
squashfs_base_inode
{
__le16
inode_type
;
__le16
mode
;
__le16
uid
;
__le16
guid
;
__le32
mtime
;
__le32
inode_number
;
};
struct
squashfs_ipc_inode
{
__le16
inode_type
;
__le16
mode
;
__le16
uid
;
__le16
guid
;
__le32
mtime
;
__le32
inode_number
;
__le32
nlink
;
};
struct
squashfs_dev_inode
{
__le16
inode_type
;
__le16
mode
;
__le16
uid
;
__le16
guid
;
__le32
mtime
;
__le32
inode_number
;
__le32
nlink
;
__le32
rdev
;
};
struct
squashfs_symlink_inode
{
__le16
inode_type
;
__le16
mode
;
__le16
uid
;
__le16
guid
;
__le32
mtime
;
__le32
inode_number
;
__le32
nlink
;
__le32
symlink_size
;
char
symlink
[
0
];
};
struct
squashfs_reg_inode
{
__le16
inode_type
;
__le16
mode
;
__le16
uid
;
__le16
guid
;
__le32
mtime
;
__le32
inode_number
;
__le32
start_block
;
__le32
fragment
;
__le32
offset
;
__le32
file_size
;
__le16
block_list
[
0
];
};
struct
squashfs_lreg_inode
{
__le16
inode_type
;
__le16
mode
;
__le16
uid
;
__le16
guid
;
__le32
mtime
;
__le32
inode_number
;
__le64
start_block
;
__le64
file_size
;
__le64
sparse
;
__le32
nlink
;
__le32
fragment
;
__le32
offset
;
__le32
xattr
;
__le16
block_list
[
0
];
};
struct
squashfs_dir_inode
{
__le16
inode_type
;
__le16
mode
;
__le16
uid
;
__le16
guid
;
__le32
mtime
;
__le32
inode_number
;
__le32
start_block
;
__le32
nlink
;
__le16
file_size
;
__le16
offset
;
__le32
parent_inode
;
};
struct
squashfs_ldir_inode
{
__le16
inode_type
;
__le16
mode
;
__le16
uid
;
__le16
guid
;
__le32
mtime
;
__le32
inode_number
;
__le32
nlink
;
__le32
file_size
;
__le32
start_block
;
__le32
parent_inode
;
__le16
i_count
;
__le16
offset
;
__le32
xattr
;
struct
squashfs_dir_index
index
[
0
];
};
union
squashfs_inode
{
struct
squashfs_base_inode
base
;
struct
squashfs_dev_inode
dev
;
struct
squashfs_symlink_inode
symlink
;
struct
squashfs_reg_inode
reg
;
struct
squashfs_lreg_inode
lreg
;
struct
squashfs_dir_inode
dir
;
struct
squashfs_ldir_inode
ldir
;
struct
squashfs_ipc_inode
ipc
;
};
struct
squashfs_dir_entry
{
__le16
offset
;
__le16
inode_number
;
__le16
type
;
__le16
size
;
char
name
[
0
];
};
struct
squashfs_dir_header
{
__le32
count
;
__le32
start_block
;
__le32
inode_number
;
};
struct
squashfs_fragment_entry
{
__le64
start_block
;
__le32
size
;
unsigned
int
unused
;
};
#endif
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/squashfs_fs_i.h
0 → 100644
View file @
05a1b863
#ifndef SQUASHFS_FS_I
#define SQUASHFS_FS_I
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* squashfs_fs_i.h
*/
struct
squashfs_inode_info
{
u64
start
;
int
offset
;
union
{
struct
{
u64
fragment_block
;
int
fragment_size
;
int
fragment_offset
;
u64
block_list_start
;
};
struct
{
u64
dir_idx_start
;
int
dir_idx_offset
;
int
dir_idx_cnt
;
int
parent
;
};
};
struct
inode
vfs_inode
;
};
#endif
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/squashfs_fs_sb.h
0 → 100644
View file @
05a1b863
#ifndef SQUASHFS_FS_SB
#define SQUASHFS_FS_SB
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* squashfs_fs_sb.h
*/
#include "squashfs_fs.h"
struct
squashfs_cache
{
char
*
name
;
int
entries
;
int
next_blk
;
int
num_waiters
;
int
unused
;
int
block_size
;
int
pages
;
spinlock_t
lock
;
wait_queue_head_t
wait_queue
;
struct
squashfs_cache_entry
*
entry
;
};
struct
squashfs_cache_entry
{
u64
block
;
int
length
;
int
refcount
;
u64
next_index
;
int
pending
;
int
error
;
int
num_waiters
;
wait_queue_head_t
wait_queue
;
struct
squashfs_cache
*
cache
;
void
**
data
;
};
struct
squashfs_sb_info
{
int
devblksize
;
int
devblksize_log2
;
struct
squashfs_cache
*
block_cache
;
struct
squashfs_cache
*
fragment_cache
;
struct
squashfs_cache
*
read_page
;
int
next_meta_index
;
__le64
*
id_table
;
__le64
*
fragment_index
;
unsigned
int
*
fragment_index_2
;
struct
mutex
read_data_mutex
;
struct
mutex
meta_index_mutex
;
struct
meta_index
*
meta_index
;
z_stream
stream
;
__le64
*
inode_lookup_table
;
u64
inode_table
;
u64
directory_table
;
unsigned
int
block_size
;
unsigned
short
block_log
;
long
long
bytes_used
;
unsigned
int
inodes
;
};
#endif
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/super.c
0 → 100644
View file @
05a1b863
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* super.c
*/
/*
* This file implements code to read the superblock, read and initialise
* in-memory structures at mount time, and all the VFS glue code to register
* the filesystem.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/pagemap.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/zlib.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
static
struct
file_system_type
squashfs_fs_type
;
static
struct
super_operations
squashfs_super_ops
;
static
int
supported_squashfs_filesystem
(
short
major
,
short
minor
,
short
comp
)
{
if
(
major
<
SQUASHFS_MAJOR
)
{
ERROR
(
"Major/Minor mismatch, older Squashfs %d.%d "
"filesystems are unsupported
\n
"
,
major
,
minor
);
return
-
EINVAL
;
}
else
if
(
major
>
SQUASHFS_MAJOR
||
minor
>
SQUASHFS_MINOR
)
{
ERROR
(
"Major/Minor mismatch, trying to mount newer "
"%d.%d filesystem
\n
"
,
major
,
minor
);
ERROR
(
"Please update your kernel
\n
"
);
return
-
EINVAL
;
}
if
(
comp
!=
ZLIB_COMPRESSION
)
return
-
EINVAL
;
return
0
;
}
static
int
squashfs_fill_super
(
struct
super_block
*
sb
,
void
*
data
,
int
silent
)
{
struct
squashfs_sb_info
*
msblk
;
struct
squashfs_super_block
*
sblk
=
NULL
;
char
b
[
BDEVNAME_SIZE
];
struct
inode
*
root
;
long
long
root_inode
;
unsigned
short
flags
;
unsigned
int
fragments
;
u64
lookup_table_start
;
int
err
;
TRACE
(
"Entered squashfs_fill_superblock
\n
"
);
sb
->
s_fs_info
=
kzalloc
(
sizeof
(
*
msblk
),
GFP_KERNEL
);
if
(
sb
->
s_fs_info
==
NULL
)
{
ERROR
(
"Failed to allocate squashfs_sb_info
\n
"
);
return
-
ENOMEM
;
}
msblk
=
sb
->
s_fs_info
;
msblk
->
stream
.
workspace
=
kmalloc
(
zlib_inflate_workspacesize
(),
GFP_KERNEL
);
if
(
msblk
->
stream
.
workspace
==
NULL
)
{
ERROR
(
"Failed to allocate zlib workspace
\n
"
);
goto
failure
;
}
sblk
=
kzalloc
(
sizeof
(
*
sblk
),
GFP_KERNEL
);
if
(
sblk
==
NULL
)
{
ERROR
(
"Failed to allocate squashfs_super_block
\n
"
);
goto
failure
;
}
msblk
->
devblksize
=
sb_min_blocksize
(
sb
,
BLOCK_SIZE
);
msblk
->
devblksize_log2
=
ffz
(
~
msblk
->
devblksize
);
mutex_init
(
&
msblk
->
read_data_mutex
);
mutex_init
(
&
msblk
->
meta_index_mutex
);
/*
* msblk->bytes_used is checked in squashfs_read_table to ensure reads
* are not beyond filesystem end. But as we're using
* squashfs_read_table here to read the superblock (including the value
* of bytes_used) we need to set it to an initial sensible dummy value
*/
msblk
->
bytes_used
=
sizeof
(
*
sblk
);
err
=
squashfs_read_table
(
sb
,
sblk
,
SQUASHFS_START
,
sizeof
(
*
sblk
));
if
(
err
<
0
)
{
ERROR
(
"unable to read squashfs_super_block
\n
"
);
goto
failed_mount
;
}
/* Check it is a SQUASHFS superblock */
sb
->
s_magic
=
le32_to_cpu
(
sblk
->
s_magic
);
if
(
sb
->
s_magic
!=
SQUASHFS_MAGIC
)
{
if
(
!
silent
)
ERROR
(
"Can't find a SQUASHFS superblock on %s
\n
"
,
bdevname
(
sb
->
s_bdev
,
b
));
err
=
-
EINVAL
;
goto
failed_mount
;
}
/* Check the MAJOR & MINOR versions and compression type */
err
=
supported_squashfs_filesystem
(
le16_to_cpu
(
sblk
->
s_major
),
le16_to_cpu
(
sblk
->
s_minor
),
le16_to_cpu
(
sblk
->
compression
));
if
(
err
<
0
)
goto
failed_mount
;
err
=
-
EINVAL
;
/*
* Check if there's xattrs in the filesystem. These are not
* supported in this version, so warn that they will be ignored.
*/
if
(
le64_to_cpu
(
sblk
->
xattr_table_start
)
!=
SQUASHFS_INVALID_BLK
)
ERROR
(
"Xattrs in filesystem, these will be ignored
\n
"
);
/* Check the filesystem does not extend beyond the end of the
block device */
msblk
->
bytes_used
=
le64_to_cpu
(
sblk
->
bytes_used
);
if
(
msblk
->
bytes_used
<
0
||
msblk
->
bytes_used
>
i_size_read
(
sb
->
s_bdev
->
bd_inode
))
goto
failed_mount
;
/* Check block size for sanity */
msblk
->
block_size
=
le32_to_cpu
(
sblk
->
block_size
);
if
(
msblk
->
block_size
>
SQUASHFS_FILE_MAX_SIZE
)
goto
failed_mount
;
msblk
->
block_log
=
le16_to_cpu
(
sblk
->
block_log
);
if
(
msblk
->
block_log
>
SQUASHFS_FILE_MAX_LOG
)
goto
failed_mount
;
/* Check the root inode for sanity */
root_inode
=
le64_to_cpu
(
sblk
->
root_inode
);
if
(
SQUASHFS_INODE_OFFSET
(
root_inode
)
>
SQUASHFS_METADATA_SIZE
)
goto
failed_mount
;
msblk
->
inode_table
=
le64_to_cpu
(
sblk
->
inode_table_start
);
msblk
->
directory_table
=
le64_to_cpu
(
sblk
->
directory_table_start
);
msblk
->
inodes
=
le32_to_cpu
(
sblk
->
inodes
);
flags
=
le16_to_cpu
(
sblk
->
flags
);
TRACE
(
"Found valid superblock on %s
\n
"
,
bdevname
(
sb
->
s_bdev
,
b
));
TRACE
(
"Inodes are %scompressed
\n
"
,
SQUASHFS_UNCOMPRESSED_INODES
(
flags
)
?
"un"
:
""
);
TRACE
(
"Data is %scompressed
\n
"
,
SQUASHFS_UNCOMPRESSED_DATA
(
flags
)
?
"un"
:
""
);
TRACE
(
"Filesystem size %lld bytes
\n
"
,
msblk
->
bytes_used
);
TRACE
(
"Block size %d
\n
"
,
msblk
->
block_size
);
TRACE
(
"Number of inodes %d
\n
"
,
msblk
->
inodes
);
TRACE
(
"Number of fragments %d
\n
"
,
le32_to_cpu
(
sblk
->
fragments
));
TRACE
(
"Number of ids %d
\n
"
,
le16_to_cpu
(
sblk
->
no_ids
));
TRACE
(
"sblk->inode_table_start %llx
\n
"
,
msblk
->
inode_table
);
TRACE
(
"sblk->directory_table_start %llx
\n
"
,
msblk
->
directory_table
);
TRACE
(
"sblk->fragment_table_start %llx
\n
"
,
(
u64
)
le64_to_cpu
(
sblk
->
fragment_table_start
));
TRACE
(
"sblk->id_table_start %llx
\n
"
,
(
u64
)
le64_to_cpu
(
sblk
->
id_table_start
));
sb
->
s_maxbytes
=
MAX_LFS_FILESIZE
;
sb
->
s_flags
|=
MS_RDONLY
;
sb
->
s_op
=
&
squashfs_super_ops
;
err
=
-
ENOMEM
;
msblk
->
block_cache
=
squashfs_cache_init
(
"metadata"
,
SQUASHFS_CACHED_BLKS
,
SQUASHFS_METADATA_SIZE
);
if
(
msblk
->
block_cache
==
NULL
)
goto
failed_mount
;
/* Allocate read_page block */
msblk
->
read_page
=
squashfs_cache_init
(
"data"
,
1
,
msblk
->
block_size
);
if
(
msblk
->
read_page
==
NULL
)
{
ERROR
(
"Failed to allocate read_page block
\n
"
);
goto
failed_mount
;
}
/* Allocate and read id index table */
msblk
->
id_table
=
squashfs_read_id_index_table
(
sb
,
le64_to_cpu
(
sblk
->
id_table_start
),
le16_to_cpu
(
sblk
->
no_ids
));
if
(
IS_ERR
(
msblk
->
id_table
))
{
err
=
PTR_ERR
(
msblk
->
id_table
);
msblk
->
id_table
=
NULL
;
goto
failed_mount
;
}
fragments
=
le32_to_cpu
(
sblk
->
fragments
);
if
(
fragments
==
0
)
goto
allocate_lookup_table
;
msblk
->
fragment_cache
=
squashfs_cache_init
(
"fragment"
,
SQUASHFS_CACHED_FRAGMENTS
,
msblk
->
block_size
);
if
(
msblk
->
fragment_cache
==
NULL
)
{
err
=
-
ENOMEM
;
goto
failed_mount
;
}
/* Allocate and read fragment index table */
msblk
->
fragment_index
=
squashfs_read_fragment_index_table
(
sb
,
le64_to_cpu
(
sblk
->
fragment_table_start
),
fragments
);
if
(
IS_ERR
(
msblk
->
fragment_index
))
{
err
=
PTR_ERR
(
msblk
->
fragment_index
);
msblk
->
fragment_index
=
NULL
;
goto
failed_mount
;
}
allocate_lookup_table:
lookup_table_start
=
le64_to_cpu
(
sblk
->
lookup_table_start
);
if
(
lookup_table_start
==
SQUASHFS_INVALID_BLK
)
goto
allocate_root
;
/* Allocate and read inode lookup table */
msblk
->
inode_lookup_table
=
squashfs_read_inode_lookup_table
(
sb
,
lookup_table_start
,
msblk
->
inodes
);
if
(
IS_ERR
(
msblk
->
inode_lookup_table
))
{
err
=
PTR_ERR
(
msblk
->
inode_lookup_table
);
msblk
->
inode_lookup_table
=
NULL
;
goto
failed_mount
;
}
sb
->
s_export_op
=
&
squashfs_export_ops
;
allocate_root:
root
=
new_inode
(
sb
);
if
(
!
root
)
{
err
=
-
ENOMEM
;
goto
failed_mount
;
}
err
=
squashfs_read_inode
(
root
,
root_inode
);
if
(
err
)
{
iget_failed
(
root
);
goto
failed_mount
;
}
insert_inode_hash
(
root
);
sb
->
s_root
=
d_alloc_root
(
root
);
if
(
sb
->
s_root
==
NULL
)
{
ERROR
(
"Root inode create failed
\n
"
);
err
=
-
ENOMEM
;
iput
(
root
);
goto
failed_mount
;
}
TRACE
(
"Leaving squashfs_fill_super
\n
"
);
kfree
(
sblk
);
return
0
;
failed_mount:
squashfs_cache_delete
(
msblk
->
block_cache
);
squashfs_cache_delete
(
msblk
->
fragment_cache
);
squashfs_cache_delete
(
msblk
->
read_page
);
kfree
(
msblk
->
inode_lookup_table
);
kfree
(
msblk
->
fragment_index
);
kfree
(
msblk
->
id_table
);
kfree
(
msblk
->
stream
.
workspace
);
kfree
(
sb
->
s_fs_info
);
sb
->
s_fs_info
=
NULL
;
kfree
(
sblk
);
return
err
;
failure:
kfree
(
msblk
->
stream
.
workspace
);
kfree
(
sb
->
s_fs_info
);
sb
->
s_fs_info
=
NULL
;
return
-
ENOMEM
;
}
static
int
squashfs_statfs
(
struct
dentry
*
dentry
,
struct
kstatfs
*
buf
)
{
struct
squashfs_sb_info
*
msblk
=
dentry
->
d_sb
->
s_fs_info
;
TRACE
(
"Entered squashfs_statfs
\n
"
);
buf
->
f_type
=
SQUASHFS_MAGIC
;
buf
->
f_bsize
=
msblk
->
block_size
;
buf
->
f_blocks
=
((
msblk
->
bytes_used
-
1
)
>>
msblk
->
block_log
)
+
1
;
buf
->
f_bfree
=
buf
->
f_bavail
=
0
;
buf
->
f_files
=
msblk
->
inodes
;
buf
->
f_ffree
=
0
;
buf
->
f_namelen
=
SQUASHFS_NAME_LEN
;
return
0
;
}
static
int
squashfs_remount
(
struct
super_block
*
sb
,
int
*
flags
,
char
*
data
)
{
*
flags
|=
MS_RDONLY
;
return
0
;
}
static
void
squashfs_put_super
(
struct
super_block
*
sb
)
{
if
(
sb
->
s_fs_info
)
{
struct
squashfs_sb_info
*
sbi
=
sb
->
s_fs_info
;
squashfs_cache_delete
(
sbi
->
block_cache
);
squashfs_cache_delete
(
sbi
->
fragment_cache
);
squashfs_cache_delete
(
sbi
->
read_page
);
kfree
(
sbi
->
id_table
);
kfree
(
sbi
->
fragment_index
);
kfree
(
sbi
->
meta_index
);
kfree
(
sbi
->
stream
.
workspace
);
kfree
(
sb
->
s_fs_info
);
sb
->
s_fs_info
=
NULL
;
}
}
static
int
squashfs_get_sb
(
struct
file_system_type
*
fs_type
,
int
flags
,
const
char
*
dev_name
,
void
*
data
,
struct
vfsmount
*
mnt
)
{
return
get_sb_bdev
(
fs_type
,
flags
,
dev_name
,
data
,
squashfs_fill_super
,
mnt
);
}
static
struct
kmem_cache
*
squashfs_inode_cachep
;
static
void
init_once
(
void
*
foo
)
{
struct
squashfs_inode_info
*
ei
=
foo
;
inode_init_once
(
&
ei
->
vfs_inode
);
}
static
int
__init
init_inodecache
(
void
)
{
squashfs_inode_cachep
=
kmem_cache_create
(
"squashfs_inode_cache"
,
sizeof
(
struct
squashfs_inode_info
),
0
,
SLAB_HWCACHE_ALIGN
|
SLAB_RECLAIM_ACCOUNT
,
init_once
);
return
squashfs_inode_cachep
?
0
:
-
ENOMEM
;
}
static
void
destroy_inodecache
(
void
)
{
kmem_cache_destroy
(
squashfs_inode_cachep
);
}
static
int
__init
init_squashfs_fs
(
void
)
{
int
err
=
init_inodecache
();
if
(
err
)
return
err
;
err
=
register_filesystem
(
&
squashfs_fs_type
);
if
(
err
)
{
destroy_inodecache
();
return
err
;
}
printk
(
KERN_INFO
"squashfs: version 4.0 (2009/01/03) "
"Phillip Lougher
\n
"
);
return
0
;
}
static
void
__exit
exit_squashfs_fs
(
void
)
{
unregister_filesystem
(
&
squashfs_fs_type
);
destroy_inodecache
();
}
static
struct
inode
*
squashfs_alloc_inode
(
struct
super_block
*
sb
)
{
struct
squashfs_inode_info
*
ei
=
kmem_cache_alloc
(
squashfs_inode_cachep
,
GFP_KERNEL
);
return
ei
?
&
ei
->
vfs_inode
:
NULL
;
}
static
void
squashfs_destroy_inode
(
struct
inode
*
inode
)
{
kmem_cache_free
(
squashfs_inode_cachep
,
squashfs_i
(
inode
));
}
static
struct
file_system_type
squashfs_fs_type
=
{
.
owner
=
THIS_MODULE
,
.
name
=
"squashfs"
,
.
get_sb
=
squashfs_get_sb
,
.
kill_sb
=
kill_block_super
,
.
fs_flags
=
FS_REQUIRES_DEV
};
static
struct
super_operations
squashfs_super_ops
=
{
.
alloc_inode
=
squashfs_alloc_inode
,
.
destroy_inode
=
squashfs_destroy_inode
,
.
statfs
=
squashfs_statfs
,
.
put_super
=
squashfs_put_super
,
.
remount_fs
=
squashfs_remount
};
module_init
(
init_squashfs_fs
);
module_exit
(
exit_squashfs_fs
);
MODULE_DESCRIPTION
(
"squashfs 4.0, a compressed read-only filesystem"
);
MODULE_AUTHOR
(
"Phillip Lougher <phillip@lougher.demon.co.uk>"
);
MODULE_LICENSE
(
"GPL"
);
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/symlink.c
0 → 100644
View file @
05a1b863
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* symlink.c
*/
/*
* This file implements code to handle symbolic links.
*
* The data contents of symbolic links are stored inside the symbolic
* link inode within the inode table. This allows the normally small symbolic
* link to be compressed as part of the inode table, achieving much greater
* compression than if the symbolic link was compressed individually.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/pagemap.h>
#include <linux/zlib.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
static
int
squashfs_symlink_readpage
(
struct
file
*
file
,
struct
page
*
page
)
{
struct
inode
*
inode
=
page
->
mapping
->
host
;
struct
super_block
*
sb
=
inode
->
i_sb
;
struct
squashfs_sb_info
*
msblk
=
sb
->
s_fs_info
;
int
index
=
page
->
index
<<
PAGE_CACHE_SHIFT
;
u64
block
=
squashfs_i
(
inode
)
->
start
;
int
offset
=
squashfs_i
(
inode
)
->
offset
;
int
length
=
min_t
(
int
,
i_size_read
(
inode
)
-
index
,
PAGE_CACHE_SIZE
);
int
bytes
,
copied
;
void
*
pageaddr
;
struct
squashfs_cache_entry
*
entry
;
TRACE
(
"Entered squashfs_symlink_readpage, page index %ld, start block "
"%llx, offset %x
\n
"
,
page
->
index
,
block
,
offset
);
/*
* Skip index bytes into symlink metadata.
*/
if
(
index
)
{
bytes
=
squashfs_read_metadata
(
sb
,
NULL
,
&
block
,
&
offset
,
index
);
if
(
bytes
<
0
)
{
ERROR
(
"Unable to read symlink [%llx:%x]
\n
"
,
squashfs_i
(
inode
)
->
start
,
squashfs_i
(
inode
)
->
offset
);
goto
error_out
;
}
}
/*
* Read length bytes from symlink metadata. Squashfs_read_metadata
* is not used here because it can sleep and we want to use
* kmap_atomic to map the page. Instead call the underlying
* squashfs_cache_get routine. As length bytes may overlap metadata
* blocks, we may need to call squashfs_cache_get multiple times.
*/
for
(
bytes
=
0
;
bytes
<
length
;
offset
=
0
,
bytes
+=
copied
)
{
entry
=
squashfs_cache_get
(
sb
,
msblk
->
block_cache
,
block
,
0
);
if
(
entry
->
error
)
{
ERROR
(
"Unable to read symlink [%llx:%x]
\n
"
,
squashfs_i
(
inode
)
->
start
,
squashfs_i
(
inode
)
->
offset
);
squashfs_cache_put
(
entry
);
goto
error_out
;
}
pageaddr
=
kmap_atomic
(
page
,
KM_USER0
);
copied
=
squashfs_copy_data
(
pageaddr
+
bytes
,
entry
,
offset
,
length
-
bytes
);
if
(
copied
==
length
-
bytes
)
memset
(
pageaddr
+
length
,
0
,
PAGE_CACHE_SIZE
-
length
);
else
block
=
entry
->
next_index
;
kunmap_atomic
(
pageaddr
,
KM_USER0
);
squashfs_cache_put
(
entry
);
}
flush_dcache_page
(
page
);
SetPageUptodate
(
page
);
unlock_page
(
page
);
return
0
;
error_out:
SetPageError
(
page
);
unlock_page
(
page
);
return
0
;
}
const
struct
address_space_operations
squashfs_symlink_aops
=
{
.
readpage
=
squashfs_symlink_readpage
};
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/include/linux/squashfs_fs.h
0 → 100644
View file @
05a1b863
#ifndef SQUASHFS_FS
#define SQUASHFS_FS
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* squashfs_fs.h
*/
#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE
#define SQUASHFS_MAJOR 4
#define SQUASHFS_MINOR 0
#define SQUASHFS_MAGIC 0x73717368
#define SQUASHFS_START 0
/* size of metadata (inode and directory) blocks */
#define SQUASHFS_METADATA_SIZE 8192
#define SQUASHFS_METADATA_LOG 13
/* default size of data blocks */
#define SQUASHFS_FILE_SIZE 131072
#define SQUASHFS_FILE_LOG 17
#define SQUASHFS_FILE_MAX_SIZE 1048576
/* Max number of uids and gids */
#define SQUASHFS_IDS 65536
/* Max length of filename (not 255) */
#define SQUASHFS_NAME_LEN 256
#define SQUASHFS_INVALID_FRAG (0xffffffffU)
#define SQUASHFS_INVALID_BLK (-1LL)
/* Filesystem flags */
#define SQUASHFS_NOI 0
#define SQUASHFS_NOD 1
#define SQUASHFS_NOF 3
#define SQUASHFS_NO_FRAG 4
#define SQUASHFS_ALWAYS_FRAG 5
#define SQUASHFS_DUPLICATE 6
#define SQUASHFS_EXPORT 7
#define SQUASHFS_BIT(flag, bit) ((flag >> bit) & 1)
#define SQUASHFS_UNCOMPRESSED_INODES(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOI)
#define SQUASHFS_UNCOMPRESSED_DATA(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOD)
#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOF)
#define SQUASHFS_NO_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NO_FRAG)
#define SQUASHFS_ALWAYS_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_ALWAYS_FRAG)
#define SQUASHFS_DUPLICATES(flags) SQUASHFS_BIT(flags, \
SQUASHFS_DUPLICATE)
#define SQUASHFS_EXPORTABLE(flags) SQUASHFS_BIT(flags, \
SQUASHFS_EXPORT)
/* Max number of types and file types */
#define SQUASHFS_DIR_TYPE 1
#define SQUASHFS_REG_TYPE 2
#define SQUASHFS_SYMLINK_TYPE 3
#define SQUASHFS_BLKDEV_TYPE 4
#define SQUASHFS_CHRDEV_TYPE 5
#define SQUASHFS_FIFO_TYPE 6
#define SQUASHFS_SOCKET_TYPE 7
#define SQUASHFS_LDIR_TYPE 8
#define SQUASHFS_LREG_TYPE 9
#define SQUASHFS_LSYMLINK_TYPE 10
#define SQUASHFS_LBLKDEV_TYPE 11
#define SQUASHFS_LCHRDEV_TYPE 12
#define SQUASHFS_LFIFO_TYPE 13
#define SQUASHFS_LSOCKET_TYPE 14
/* Flag whether block is compressed or uncompressed, bit is set if block is
* uncompressed */
#define SQUASHFS_COMPRESSED_BIT (1 << 15)
#define SQUASHFS_COMPRESSED_SIZE(B) (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \
(B) & ~SQUASHFS_COMPRESSED_BIT : SQUASHFS_COMPRESSED_BIT)
#define SQUASHFS_COMPRESSED(B) (!((B) & SQUASHFS_COMPRESSED_BIT))
#define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24)
#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B) ((B) & \
~SQUASHFS_COMPRESSED_BIT_BLOCK)
#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK))
/*
* Inode number ops. Inodes consist of a compressed block number, and an
* uncompressed offset within that block
*/
#define SQUASHFS_INODE_BLK(A) ((unsigned int) ((A) >> 16))
#define SQUASHFS_INODE_OFFSET(A) ((unsigned int) ((A) & 0xffff))
#define SQUASHFS_MKINODE(A, B) ((long long)(((long long) (A)\
<< 16) + (B)))
/* Translate between VFS mode and squashfs mode */
#define SQUASHFS_MODE(A) ((A) & 0xfff)
/* fragment and fragment table defines */
#define SQUASHFS_FRAGMENT_BYTES(A) \
((A) * sizeof(struct squashfs_fragment_entry))
#define SQUASHFS_FRAGMENT_INDEX(A) (SQUASHFS_FRAGMENT_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A) (SQUASHFS_FRAGMENT_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEXES(A) ((SQUASHFS_FRAGMENT_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_BYTES(A) (SQUASHFS_FRAGMENT_INDEXES(A) *\
sizeof(long long))
/* inode lookup table defines */
#define SQUASHFS_LOOKUP_BYTES(A) ((A) * sizeof(long long))
#define SQUASHFS_LOOKUP_BLOCK(A) (SQUASHFS_LOOKUP_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCK_OFFSET(A) (SQUASHFS_LOOKUP_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCKS(A) ((SQUASHFS_LOOKUP_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCK_BYTES(A) (SQUASHFS_LOOKUP_BLOCKS(A) *\
sizeof(long long))
/* uid/gid lookup table defines */
#define SQUASHFS_ID_BYTES(A) ((A) * sizeof(unsigned int))
#define SQUASHFS_ID_BLOCK(A) (SQUASHFS_ID_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_ID_BLOCK_OFFSET(A) (SQUASHFS_ID_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_ID_BLOCKS(A) ((SQUASHFS_ID_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_ID_BLOCK_BYTES(A) (SQUASHFS_ID_BLOCKS(A) *\
sizeof(long long))
/* cached data constants for filesystem */
#define SQUASHFS_CACHED_BLKS 8
#define SQUASHFS_MAX_FILE_SIZE_LOG 64
#define SQUASHFS_MAX_FILE_SIZE (1LL << \
(SQUASHFS_MAX_FILE_SIZE_LOG - 2))
#define SQUASHFS_MARKER_BYTE 0xff
/* meta index cache */
#define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int))
#define SQUASHFS_META_ENTRIES 127
#define SQUASHFS_META_SLOTS 8
struct
meta_entry
{
long
long
data_block
;
unsigned
int
index_block
;
unsigned
short
offset
;
unsigned
short
pad
;
};
struct
meta_index
{
unsigned
int
inode_number
;
unsigned
int
offset
;
unsigned
short
entries
;
unsigned
short
skip
;
unsigned
short
locked
;
unsigned
short
pad
;
struct
meta_entry
meta_entry
[
SQUASHFS_META_ENTRIES
];
};
/*
* definitions for structures on disk
*/
#define ZLIB_COMPRESSION 1
struct
squashfs_super_block
{
__le32
s_magic
;
__le32
inodes
;
__le32
mkfs_time
;
__le32
block_size
;
__le32
fragments
;
__le16
compression
;
__le16
block_log
;
__le16
flags
;
__le16
no_ids
;
__le16
s_major
;
__le16
s_minor
;
__le64
root_inode
;
__le64
bytes_used
;
__le64
id_table_start
;
__le64
xattr_table_start
;
__le64
inode_table_start
;
__le64
directory_table_start
;
__le64
fragment_table_start
;
__le64
lookup_table_start
;
};
struct
squashfs_dir_index
{
__le32
index
;
__le32
start_block
;
__le32
size
;
unsigned
char
name
[
0
];
};
struct
squashfs_base_inode
{
__le16
inode_type
;
__le16
mode
;
__le16
uid
;
__le16
guid
;
__le32
mtime
;
__le32
inode_number
;
};
struct
squashfs_ipc_inode
{
__le16
inode_type
;
__le16
mode
;
__le16
uid
;
__le16
guid
;
__le32
mtime
;
__le32
inode_number
;
__le32
nlink
;
};
struct
squashfs_dev_inode
{
__le16
inode_type
;
__le16
mode
;
__le16
uid
;
__le16
guid
;
__le32
mtime
;
__le32
inode_number
;
__le32
nlink
;
__le32
rdev
;
};
struct
squashfs_symlink_inode
{
__le16
inode_type
;
__le16
mode
;
__le16
uid
;
__le16
guid
;
__le32
mtime
;
__le32
inode_number
;
__le32
nlink
;
__le32
symlink_size
;
char
symlink
[
0
];
};
struct
squashfs_reg_inode
{
__le16
inode_type
;
__le16
mode
;
__le16
uid
;
__le16
guid
;
__le32
mtime
;
__le32
inode_number
;
__le32
start_block
;
__le32
fragment
;
__le32
offset
;
__le32
file_size
;
__le16
block_list
[
0
];
};
struct
squashfs_lreg_inode
{
__le16
inode_type
;
__le16
mode
;
__le16
uid
;
__le16
guid
;
__le32
mtime
;
__le32
inode_number
;
__le64
start_block
;
__le64
file_size
;
__le64
sparse
;
__le32
nlink
;
__le32
fragment
;
__le32
offset
;
__le32
xattr
;
__le16
block_list
[
0
];
};
struct
squashfs_dir_inode
{
__le16
inode_type
;
__le16
mode
;
__le16
uid
;
__le16
guid
;
__le32
mtime
;
__le32
inode_number
;
__le32
start_block
;
__le32
nlink
;
__le16
file_size
;
__le16
offset
;
__le32
parent_inode
;
};
struct
squashfs_ldir_inode
{
__le16
inode_type
;
__le16
mode
;
__le16
uid
;
__le16
guid
;
__le32
mtime
;
__le32
inode_number
;
__le32
nlink
;
__le32
file_size
;
__le32
start_block
;
__le32
parent_inode
;
__le16
i_count
;
__le16
offset
;
__le32
xattr
;
struct
squashfs_dir_index
index
[
0
];
};
union
squashfs_inode
{
struct
squashfs_base_inode
base
;
struct
squashfs_dev_inode
dev
;
struct
squashfs_symlink_inode
symlink
;
struct
squashfs_reg_inode
reg
;
struct
squashfs_lreg_inode
lreg
;
struct
squashfs_dir_inode
dir
;
struct
squashfs_ldir_inode
ldir
;
struct
squashfs_ipc_inode
ipc
;
};
struct
squashfs_dir_entry
{
__le16
offset
;
__le16
inode_number
;
__le16
type
;
__le16
size
;
char
name
[
0
];
};
struct
squashfs_dir_header
{
__le32
count
;
__le32
start_block
;
__le32
inode_number
;
};
struct
squashfs_fragment_entry
{
__le64
start_block
;
__le32
size
;
unsigned
int
unused
;
};
#endif
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/include/linux/squashfs_fs_i.h
0 → 100644
View file @
05a1b863
#ifndef SQUASHFS_FS_I
#define SQUASHFS_FS_I
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* squashfs_fs_i.h
*/
struct
squashfs_inode_info
{
long
long
start
;
int
offset
;
union
{
struct
{
long
long
fragment_block
;
int
fragment_size
;
int
fragment_offset
;
long
long
block_list_start
;
};
struct
{
long
long
dir_idx_start
;
int
dir_idx_offset
;
int
dir_idx_cnt
;
int
parent
;
};
};
struct
inode
vfs_inode
;
};
#endif
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/include/linux/squashfs_fs_sb.h
0 → 100644
View file @
05a1b863
#ifndef SQUASHFS_FS_SB
#define SQUASHFS_FS_SB
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* squashfs_fs_sb.h
*/
#include "squashfs_fs.h"
struct
squashfs_cache_entry
{
long
long
block
;
int
length
;
int
locked
;
long
long
next_index
;
char
pending
;
char
error
;
int
waiting
;
wait_queue_head_t
wait_queue
;
char
*
data
;
};
struct
squashfs_cache
{
char
*
name
;
int
entries
;
int
block_size
;
int
next_blk
;
int
waiting
;
int
unused
;
int
use_vmalloc
;
spinlock_t
lock
;
wait_queue_head_t
wait_queue
;
struct
squashfs_cache_entry
entry
[
0
];
};
struct
squashfs_sb_info
{
int
devblksize
;
int
devblksize_log2
;
struct
squashfs_cache
*
block_cache
;
struct
squashfs_cache
*
fragment_cache
;
int
next_meta_index
;
__le64
*
id_table
;
__le64
*
fragment_index
;
unsigned
int
*
fragment_index_2
;
char
*
read_page
;
struct
mutex
read_data_mutex
;
struct
mutex
read_page_mutex
;
struct
mutex
meta_index_mutex
;
struct
meta_index
*
meta_index
;
z_stream
stream
;
__le64
*
inode_lookup_table
;
long
long
inode_table
;
long
long
directory_table
;
unsigned
int
block_size
;
unsigned
short
block_log
;
long
long
bytes_used
;
unsigned
int
inodes
;
};
#endif
SQUASHFS/squashfs-tools-4.4/squashfs-tools/Makefile
0 → 100644
View file @
05a1b863
###############################################
# Compression build options #
###############################################
#
#
############# Building gzip support ###########
#
# Gzip support is by default enabled, and the compression type default
# (COMP_DEFAULT) is gzip.
#
# If you don't want/need gzip support then comment out the GZIP SUPPORT line
# below, and change COMP_DEFAULT to one of the compression types you have
# selected.
#
# Obviously, you must select at least one of the available gzip, lzma, lzo
# compression types.
#
GZIP_SUPPORT
=
1
########### Building XZ support #############
#
# LZMA2 compression.
#
# XZ Utils liblzma (http://tukaani.org/xz/) is supported
#
# Development packages (libraries and header files) should be
# supported by most modern distributions. Please refer to
# your distribution package manager.
#
# To build install the library and uncomment
# the XZ_SUPPORT line below.
#
XZ_SUPPORT
=
1
############ Building LZO support ##############
#
# The LZO library (http://www.oberhumer.com/opensource/lzo/) is supported.
#
# Development packages (libraries and header files) should be
# supported by most modern distributions. Please refer to
# your distribution package manager.
#
# To build install the library and uncomment
# the XZ_SUPPORT line below.
#
LZO_SUPPORT
=
1
########### Building LZ4 support #############
#
# Yann Collet's LZ4 tools are supported
# LZ4 homepage: http://fastcompression.blogspot.com/p/lz4.html
# LZ4 source repository: http://code.google.com/p/lz4
#
# Development packages (libraries and header files) should be
# supported by most modern distributions. Please refer to
# your distribution package manager.
#
# To build install and uncomment
# the LZ4_SUPPORT line below.
#
LZ4_SUPPORT
=
1
########### Building ZSTD support ############
#
# The ZSTD library is supported
# ZSTD homepage: http://zstd.net
# ZSTD source repository: https://github.com/facebook/zstd
#
# Development packages (libraries and header files) should be
# supported by most modern distributions. Please refer to
# your distribution package manager.
#
# To build install the library and uncomment
# the XZ_SUPPORT line below.
#
ZSTD_SUPPORT
=
1
######## Specifying default compression ########
#
# The next line specifies which compression algorithm is used by default
# in Mksquashfs. Obviously the compression algorithm must have been
# selected to be built
#
COMP_DEFAULT
=
gzip
###############################################
# Extended attribute (XATTRs) build options #
###############################################
#
# Building XATTR support for Mksquashfs and Unsquashfs
#
# If your C library or build/target environment doesn't support XATTRs then
# comment out the next line to build Mksquashfs and Unsquashfs without XATTR
# support
XATTR_SUPPORT
=
1
# Select whether you wish xattrs to be stored by Mksquashfs and extracted
# by Unsquashfs by default. If selected users can disable xattr support by
# using the -no-xattrs option
#
# If unselected, Mksquashfs/Unsquashfs won't store and extract xattrs by
# default. Users can enable xattrs by using the -xattrs option.
XATTR_DEFAULT
=
1
###############################################
# Reproducible Image options #
###############################################
#
# Select whether you wish reproducible builds by default. If selected users
# can disable reproducible builds using the not-reproducible option.
# If not selected, users can enable reproducible builds using the
# -reproducible option
REPRODUCIBLE_DEFAULT
=
1
###############################################
# Obsolete options #
###############################################
########### Building LZMA support #############
#
# LZMA1 compression.
#
# LZMA1 compression is obsolete, and the newer and better XZ (LZMA2)
# compression should be used in preference.
#
# Both XZ Utils liblzma (http://tukaani.org/xz/) and LZMA SDK
# (http://www.7-zip.org/sdk.html) are supported
#
# To build using XZ Utils liblzma - install the library and uncomment
# the LZMA_XZ_SUPPORT line below.
#
# To build using the LZMA SDK (4.65 used in development, other versions may
# work) - download and unpack it, uncomment and set LZMA_DIR to unpacked source,
# and uncomment the LZMA_SUPPORT line below.
#
LZMA_XZ_SUPPORT
=
1
#LZMA_SUPPORT = 1
#LZMA_DIR = ../../../../LZMA/lzma465
###############################################
# End of BUILD options section #
###############################################
INCLUDEDIR
=
-I
.
INSTALL_DIR
=
/usr/local/bin
MKSQUASHFS_OBJS
=
mksquashfs.o read_fs.o action.o swap.o pseudo.o compressor.o
\
sort.o progressbar.o read_file.o info.o restore.o process_fragments.o
\
caches-queues-lists.o
UNSQUASHFS_OBJS
=
unsquashfs.o unsquash-1.o unsquash-2.o unsquash-3.o
\
unsquash-4.o unsquash-123.o unsquash-34.o swap.o compressor.o unsquashfs_info.o
CFLAGS
?=
-Os
CFLAGS
+=
$(EXTRA_CFLAGS)
$(INCLUDEDIR)
-D_FILE_OFFSET_BITS
=
64
\
-D_LARGEFILE_SOURCE
-D_GNU_SOURCE
-DCOMP_DEFAULT
=
\"
$(COMP_DEFAULT)
\"
\
-Wall
LIBS
=
-lpthread
ifeq
($(GZIP_SUPPORT),1)
CFLAGS
+=
-DGZIP_SUPPORT
MKSQUASHFS_OBJS
+=
gzip_wrapper.o
UNSQUASHFS_OBJS
+=
gzip_wrapper.o
LIBS
+=
$(VTZLIB)
COMPRESSORS
+=
gzip
endif
ifeq
($(LZMA_SUPPORT),1)
LZMA_OBJS
=
$(LZMA_DIR)
/C/Alloc.o
$(LZMA_DIR)
/C/LzFind.o
\
$(LZMA_DIR)
/C/LzmaDec.o
$(LZMA_DIR)
/C/LzmaEnc.o
$(LZMA_DIR)
/C/LzmaLib.o
INCLUDEDIR
+=
-I
$(LZMA_DIR)
/C
CFLAGS
+=
-DLZMA_SUPPORT
MKSQUASHFS_OBJS
+=
lzma_wrapper.o
$(LZMA_OBJS)
UNSQUASHFS_OBJS
+=
lzma_wrapper.o
$(LZMA_OBJS)
COMPRESSORS
+=
lzma
endif
ifeq
($(LZMA_XZ_SUPPORT),1)
CFLAGS
+=
-DLZMA_SUPPORT
MKSQUASHFS_OBJS
+=
lzma_xz_wrapper.o
UNSQUASHFS_OBJS
+=
lzma_xz_wrapper.o
LIBS
+=
COMPRESSORS
+=
lzma
endif
ifeq
($(XZ_SUPPORT),1)
CFLAGS
+=
-DXZ_SUPPORT
-I
$(LZMA_LIBDIR)
/include
MKSQUASHFS_OBJS
+=
xz_wrapper.o
UNSQUASHFS_OBJS
+=
xz_wrapper.o
LIBS
+=
$(LZMA_LIBDIR)
/lib/liblzma.a
COMPRESSORS
+=
xz
endif
ifeq
($(LZO_SUPPORT),1)
CFLAGS
+=
-DLZO_SUPPORT
-I
$(LZO_LIBDIR)
/include
MKSQUASHFS_OBJS
+=
lzo_wrapper.o
UNSQUASHFS_OBJS
+=
lzo_wrapper.o
LIBS
+=
$(LZO_LIBDIR)
/lib/liblzo2.a
COMPRESSORS
+=
lzo
endif
ifeq
($(LZ4_SUPPORT),1)
CFLAGS
+=
-DLZ4_SUPPORT
-I
$(LZ4_LIBDIR)
/include
MKSQUASHFS_OBJS
+=
lz4_wrapper.o
UNSQUASHFS_OBJS
+=
lz4_wrapper.o
LIBS
+=
$(LZ4_LIBDIR)
/lib/liblz4.a
COMPRESSORS
+=
lz4
endif
ifeq
($(ZSTD_SUPPORT),1)
CFLAGS
+=
-DZSTD_SUPPORT
-I
$(ZSTD_LIBDIR)
/include
MKSQUASHFS_OBJS
+=
zstd_wrapper.o
UNSQUASHFS_OBJS
+=
zstd_wrapper.o
LIBS
+=
$(ZSTD_LIBDIR)
/lib/libzstd.a
COMPRESSORS
+=
zstd
endif
ifeq
($(XATTR_SUPPORT),1)
ifeq
($(XATTR_DEFAULT),1)
CFLAGS
+=
-DXATTR_SUPPORT
-DXATTR_DEFAULT
else
CFLAGS
+=
-DXATTR_SUPPORT
endif
MKSQUASHFS_OBJS
+=
xattr.o read_xattrs.o
UNSQUASHFS_OBJS
+=
read_xattrs.o unsquashfs_xattr.o
endif
ifeq
($(REPRODUCIBLE_DEFAULT),1)
CFLAGS
+=
-DREPRODUCIBLE_DEFAULT
endif
#
# If LZMA_SUPPORT is specified then LZMA_DIR must be specified too
#
ifeq
($(LZMA_SUPPORT),1)
ifndef
LZMA_DIR
$(error
"LZMA_SUPPORT requires LZMA_DIR to be also defined"
)
endif
endif
#
# Both LZMA_XZ_SUPPORT and LZMA_SUPPORT cannot be specified
#
ifeq
($(LZMA_XZ_SUPPORT),1)
ifeq
($(LZMA_SUPPORT),1)
$(error
"Both LZMA_XZ_SUPPORT and LZMA_SUPPORT cannot be specified"
)
endif
endif
#
# At least one compressor must have been selected
#
ifndef
COMPRESSORS
$(error
"No
compressor
selected!
Select
one
or
more
of
GZIP,
LZMA,
XZ,
LZO,
\
LZ4
or
ZSTD!
")
endif
#
# COMP_DEFAULT should be defined
#
ifndef COMP_DEFAULT
$(error "
COMP_DEFAULT
must
be
set
to
a
compressor!
")
endif
#
# COMP_DEFAULT must be a selected compressor
#
ifeq (, $(findstring $(COMP_DEFAULT), $(COMPRESSORS)))
$(error "
COMP_DEFAULT
is
set
to
${COMP_DEFAULT},
which
isn't
selected
to
be
\
built!")
endif
.PHONY
:
all
all
:
mksquashfs unsquashfs
mksquashfs
:
$(MKSQUASHFS_OBJS)
$(CC)
$(LDFLAGS)
$(EXTRA_LDFLAGS)
$(MKSQUASHFS_OBJS)
$(LIBS)
-o
$@
mksquashfs.o
:
Makefile mksquashfs.c squashfs_fs.h squashfs_swap.h mksquashfs.h
\
sort.h pseudo.h compressor.h xattr.h action.h error.h progressbar.h
\
info.h caches-queues-lists.h read_fs.h restore.h process_fragments.h
read_fs.o
:
read_fs.c squashfs_fs.h squashfs_swap.h compressor.h xattr.h
\
error.h mksquashfs.h
sort.o
:
sort.c squashfs_fs.h mksquashfs.h sort.h error.h progressbar.h
swap.o
:
swap.c
pseudo.o
:
pseudo.c pseudo.h error.h progressbar.h
compressor.o
:
Makefile compressor.c compressor.h squashfs_fs.h
xattr.o
:
xattr.c squashfs_fs.h squashfs_swap.h mksquashfs.h xattr.h error.h
\
progressbar.h
read_xattrs.o
:
read_xattrs.c squashfs_fs.h squashfs_swap.h xattr.h error.h
action.o
:
action.c squashfs_fs.h mksquashfs.h action.h error.h
progressbar.o
:
progressbar.c error.h
read_file.o
:
read_file.c error.h
info.o
:
info.c squashfs_fs.h mksquashfs.h error.h progressbar.h
\
caches-queues-lists.h
restore.o
:
restore.c caches-queues-lists.h squashfs_fs.h mksquashfs.h error.h
\
progressbar.h info.h
process_fragments.o
:
process_fragments.c process_fragments.h
caches-queues-lists.o
:
caches-queues-lists.c error.h caches-queues-lists.h
gzip_wrapper.o
:
gzip_wrapper.c squashfs_fs.h gzip_wrapper.h compressor.h
lzma_wrapper.o
:
lzma_wrapper.c compressor.h squashfs_fs.h
lzma_xz_wrapper.o
:
lzma_xz_wrapper.c compressor.h squashfs_fs.h
lzo_wrapper.o
:
lzo_wrapper.c squashfs_fs.h lzo_wrapper.h compressor.h
lz4_wrapper.o
:
lz4_wrapper.c squashfs_fs.h lz4_wrapper.h compressor.h
xz_wrapper.o
:
xz_wrapper.c squashfs_fs.h xz_wrapper.h compressor.h
unsquashfs
:
$(UNSQUASHFS_OBJS)
$(CC)
$(LDFLAGS)
$(EXTRA_LDFLAGS)
$(UNSQUASHFS_OBJS)
$(LIBS)
-o
$@
unsquashfs.o
:
unsquashfs.h unsquashfs.c squashfs_fs.h squashfs_swap.h
\
squashfs_compat.h xattr.h read_fs.h compressor.h
unsquash-1.o
:
unsquashfs.h unsquash-1.c squashfs_fs.h squashfs_compat.h
unsquash-2.o
:
unsquashfs.h unsquash-2.c squashfs_fs.h squashfs_compat.h
unsquash-3.o
:
unsquashfs.h unsquash-3.c squashfs_fs.h squashfs_compat.h
unsquash-4.o
:
unsquashfs.h unsquash-4.c squashfs_fs.h squashfs_swap.h
\
read_fs.h
unsquash-123.o
:
unsquashfs.h unsquash-123.c squashfs_fs.h squashfs_compat.h
unsquash-34.o
:
unsquashfs.h unsquash-34.c
unsquashfs_xattr.o
:
unsquashfs_xattr.c unsquashfs.h squashfs_fs.h xattr.h
unsquashfs_info.o
:
unsquashfs.h squashfs_fs.h
.PHONY
:
clean
clean
:
-
rm
-f
*
.o mksquashfs unsquashfs
.PHONY
:
install
install
:
mksquashfs unsquashfs
mkdir
-p
$(INSTALL_DIR)
cp
mksquashfs
$(INSTALL_DIR)
cp
unsquashfs
$(INSTALL_DIR)
SQUASHFS/squashfs-tools-4.4/squashfs-tools/action.c
0 → 100644
View file @
05a1b863
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2011, 2012, 2013, 2014
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* action.c
*/
#include <fcntl.h>
#include <dirent.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <sys/wait.h>
#include <regex.h>
#include <limits.h>
#include <errno.h>
#include "squashfs_fs.h"
#include "mksquashfs.h"
#include "action.h"
#include "error.h"
#include "fnmatch_compat.h"
/*
* code to parse actions
*/
static
char
*
cur_ptr
,
*
source
;
static
struct
action
*
fragment_spec
=
NULL
;
static
struct
action
*
exclude_spec
=
NULL
;
static
struct
action
*
empty_spec
=
NULL
;
static
struct
action
*
move_spec
=
NULL
;
static
struct
action
*
prune_spec
=
NULL
;
static
struct
action
*
other_spec
=
NULL
;
static
int
fragment_count
=
0
;
static
int
exclude_count
=
0
;
static
int
empty_count
=
0
;
static
int
move_count
=
0
;
static
int
prune_count
=
0
;
static
int
other_count
=
0
;
static
struct
action_entry
*
parsing_action
;
static
struct
file_buffer
*
def_fragment
=
NULL
;
static
struct
token_entry
token_table
[]
=
{
{
"("
,
TOK_OPEN_BRACKET
,
1
,
},
{
")"
,
TOK_CLOSE_BRACKET
,
1
},
{
"&&"
,
TOK_AND
,
2
},
{
"||"
,
TOK_OR
,
2
},
{
"!"
,
TOK_NOT
,
1
},
{
","
,
TOK_COMMA
,
1
},
{
"@"
,
TOK_AT
,
1
},
{
" "
,
TOK_WHITE_SPACE
,
1
},
{
"
\t
"
,
TOK_WHITE_SPACE
,
1
},
{
""
,
-
1
,
0
}
};
static
struct
test_entry
test_table
[];
static
struct
action_entry
action_table
[];
static
struct
expr
*
parse_expr
(
int
subexp
);
extern
char
*
pathname
(
struct
dir_ent
*
);
extern
char
*
subpathname
(
struct
dir_ent
*
);
extern
int
read_file
(
char
*
filename
,
char
*
type
,
int
(
parse_line
)(
char
*
));
/*
* Lexical analyser
*/
#define STR_SIZE 256
static
int
get_token
(
char
**
string
)
{
/* string buffer */
static
char
*
str
=
NULL
;
static
int
size
=
0
;
char
*
str_ptr
;
int
cur_size
,
i
,
quoted
;
while
(
1
)
{
if
(
*
cur_ptr
==
'\0'
)
return
TOK_EOF
;
for
(
i
=
0
;
token_table
[
i
].
token
!=
-
1
;
i
++
)
if
(
strncmp
(
cur_ptr
,
token_table
[
i
].
string
,
token_table
[
i
].
size
)
==
0
)
break
;
if
(
token_table
[
i
].
token
!=
TOK_WHITE_SPACE
)
break
;
cur_ptr
++
;
}
if
(
token_table
[
i
].
token
!=
-
1
)
{
cur_ptr
+=
token_table
[
i
].
size
;
return
token_table
[
i
].
token
;
}
/* string */
if
(
str
==
NULL
)
{
str
=
malloc
(
STR_SIZE
);
if
(
str
==
NULL
)
MEM_ERROR
();
size
=
STR_SIZE
;
}
/* Initialise string being read */
str_ptr
=
str
;
cur_size
=
0
;
quoted
=
0
;
while
(
1
)
{
while
(
*
cur_ptr
==
'"'
)
{
cur_ptr
++
;
quoted
=
!
quoted
;
}
if
(
*
cur_ptr
==
'\0'
)
{
/* inside quoted string EOF, otherwise end of string */
if
(
quoted
)
return
TOK_EOF
;
else
break
;
}
if
(
!
quoted
)
{
for
(
i
=
0
;
token_table
[
i
].
token
!=
-
1
;
i
++
)
if
(
strncmp
(
cur_ptr
,
token_table
[
i
].
string
,
token_table
[
i
].
size
)
==
0
)
break
;
if
(
token_table
[
i
].
token
!=
-
1
)
break
;
}
if
(
*
cur_ptr
==
'\\'
)
{
cur_ptr
++
;
if
(
*
cur_ptr
==
'\0'
)
return
TOK_EOF
;
}
if
(
cur_size
+
2
>
size
)
{
char
*
tmp
;
size
=
(
cur_size
+
1
+
STR_SIZE
)
&
~
(
STR_SIZE
-
1
);
tmp
=
realloc
(
str
,
size
);
if
(
tmp
==
NULL
)
MEM_ERROR
();
str_ptr
=
str_ptr
-
str
+
tmp
;
str
=
tmp
;
}
*
str_ptr
++
=
*
cur_ptr
++
;
cur_size
++
;
}
*
str_ptr
=
'\0'
;
*
string
=
str
;
return
TOK_STRING
;
}
static
int
peek_token
(
char
**
string
)
{
char
*
saved
=
cur_ptr
;
int
token
=
get_token
(
string
);
cur_ptr
=
saved
;
return
token
;
}
/*
* Expression parser
*/
static
void
free_parse_tree
(
struct
expr
*
expr
)
{
if
(
expr
->
type
==
ATOM_TYPE
)
{
int
i
;
for
(
i
=
0
;
i
<
expr
->
atom
.
test
->
args
;
i
++
)
free
(
expr
->
atom
.
argv
[
i
]);
free
(
expr
->
atom
.
argv
);
}
else
if
(
expr
->
type
==
UNARY_TYPE
)
free_parse_tree
(
expr
->
unary_op
.
expr
);
else
{
free_parse_tree
(
expr
->
expr_op
.
lhs
);
free_parse_tree
(
expr
->
expr_op
.
rhs
);
}
free
(
expr
);
}
static
struct
expr
*
create_expr
(
struct
expr
*
lhs
,
int
op
,
struct
expr
*
rhs
)
{
struct
expr
*
expr
;
if
(
rhs
==
NULL
)
{
free_parse_tree
(
lhs
);
return
NULL
;
}
expr
=
malloc
(
sizeof
(
*
expr
));
if
(
expr
==
NULL
)
MEM_ERROR
();
expr
->
type
=
OP_TYPE
;
expr
->
expr_op
.
lhs
=
lhs
;
expr
->
expr_op
.
rhs
=
rhs
;
expr
->
expr_op
.
op
=
op
;
return
expr
;
}
static
struct
expr
*
create_unary_op
(
struct
expr
*
lhs
,
int
op
)
{
struct
expr
*
expr
;
if
(
lhs
==
NULL
)
return
NULL
;
expr
=
malloc
(
sizeof
(
*
expr
));
if
(
expr
==
NULL
)
MEM_ERROR
();
expr
->
type
=
UNARY_TYPE
;
expr
->
unary_op
.
expr
=
lhs
;
expr
->
unary_op
.
op
=
op
;
return
expr
;
}
static
struct
expr
*
parse_test
(
char
*
name
)
{
char
*
string
,
**
argv
=
NULL
;
int
token
,
args
=
0
;
int
i
;
struct
test_entry
*
test
;
struct
expr
*
expr
;
for
(
i
=
0
;
test_table
[
i
].
args
!=
-
1
;
i
++
)
if
(
strcmp
(
name
,
test_table
[
i
].
name
)
==
0
)
break
;
test
=
&
test_table
[
i
];
if
(
test
->
args
==
-
1
)
{
SYNTAX_ERROR
(
"Non-existent test
\"
%s
\"\n
"
,
name
);
return
NULL
;
}
if
(
parsing_action
->
type
==
EXCLUDE_ACTION
&&
!
test
->
exclude_ok
)
{
fprintf
(
stderr
,
"Failed to parse action
\"
%s
\"\n
"
,
source
);
fprintf
(
stderr
,
"Test
\"
%s
\"
cannot be used in exclude "
"actions
\n
"
,
name
);
fprintf
(
stderr
,
"Use prune action instead ...
\n
"
);
return
NULL
;
}
expr
=
malloc
(
sizeof
(
*
expr
));
if
(
expr
==
NULL
)
MEM_ERROR
();
expr
->
type
=
ATOM_TYPE
;
expr
->
atom
.
test
=
test
;
expr
->
atom
.
data
=
NULL
;
/*
* If the test has no arguments, then go straight to checking if there's
* enough arguments
*/
token
=
peek_token
(
&
string
);
if
(
token
!=
TOK_OPEN_BRACKET
)
goto
skip_args
;
get_token
(
&
string
);
/*
* speculatively read all the arguments, and then see if the
* number of arguments read is the number expected, this handles
* tests with a variable number of arguments
*/
token
=
get_token
(
&
string
);
if
(
token
==
TOK_CLOSE_BRACKET
)
goto
skip_args
;
while
(
1
)
{
if
(
token
!=
TOK_STRING
)
{
SYNTAX_ERROR
(
"Unexpected token
\"
%s
\"
, expected "
"argument
\n
"
,
TOK_TO_STR
(
token
,
string
));
goto
failed
;
}
argv
=
realloc
(
argv
,
(
args
+
1
)
*
sizeof
(
char
*
));
if
(
argv
==
NULL
)
MEM_ERROR
();
argv
[
args
++
]
=
strdup
(
string
);
token
=
get_token
(
&
string
);
if
(
token
==
TOK_CLOSE_BRACKET
)
break
;
if
(
token
!=
TOK_COMMA
)
{
SYNTAX_ERROR
(
"Unexpected token
\"
%s
\"
, expected "
"
\"
,
\"
or
\"
)
\"\n
"
,
TOK_TO_STR
(
token
,
string
));
goto
failed
;
}
token
=
get_token
(
&
string
);
}
skip_args:
/*
* expected number of arguments?
*/
if
(
test
->
args
!=
-
2
&&
args
!=
test
->
args
)
{
SYNTAX_ERROR
(
"Unexpected number of arguments, expected %d, "
"got %d
\n
"
,
test
->
args
,
args
);
goto
failed
;
}
expr
->
atom
.
args
=
args
;
expr
->
atom
.
argv
=
argv
;
if
(
test
->
parse_args
)
{
int
res
=
test
->
parse_args
(
test
,
&
expr
->
atom
);
if
(
res
==
0
)
goto
failed
;
}
return
expr
;
failed:
free
(
argv
);
free
(
expr
);
return
NULL
;
}
static
struct
expr
*
get_atom
()
{
char
*
string
;
int
token
=
get_token
(
&
string
);
switch
(
token
)
{
case
TOK_NOT
:
return
create_unary_op
(
get_atom
(),
token
);
case
TOK_OPEN_BRACKET
:
return
parse_expr
(
1
);
case
TOK_STRING
:
return
parse_test
(
string
);
default:
SYNTAX_ERROR
(
"Unexpected token
\"
%s
\"
, expected test "
"operation,
\"
!
\"
, or
\"
(
\"\n
"
,
TOK_TO_STR
(
token
,
string
));
return
NULL
;
}
}
static
struct
expr
*
parse_expr
(
int
subexp
)
{
struct
expr
*
expr
=
get_atom
();
while
(
expr
)
{
char
*
string
;
int
op
=
get_token
(
&
string
);
if
(
op
==
TOK_EOF
)
{
if
(
subexp
)
{
free_parse_tree
(
expr
);
SYNTAX_ERROR
(
"Expected
\"
&&
\"
,
\"
||
\"
or "
"
\"
)
\"
, got EOF
\n
"
);
return
NULL
;
}
break
;
}
if
(
op
==
TOK_CLOSE_BRACKET
)
{
if
(
!
subexp
)
{
free_parse_tree
(
expr
);
SYNTAX_ERROR
(
"Unexpected
\"
)
\"
, expected "
"
\"
&&
\"
,
\"
!!
\"
or EOF
\n
"
);
return
NULL
;
}
break
;
}
if
(
op
!=
TOK_AND
&&
op
!=
TOK_OR
)
{
free_parse_tree
(
expr
);
SYNTAX_ERROR
(
"Unexpected token
\"
%s
\"
, expected "
"
\"
&&
\"
or
\"
||
\"\n
"
,
TOK_TO_STR
(
op
,
string
));
return
NULL
;
}
expr
=
create_expr
(
expr
,
op
,
get_atom
());
}
return
expr
;
}
/*
* Action parser
*/
int
parse_action
(
char
*
s
,
int
verbose
)
{
char
*
string
,
**
argv
=
NULL
;
int
i
,
token
,
args
=
0
;
struct
expr
*
expr
;
struct
action_entry
*
action
;
void
*
data
=
NULL
;
struct
action
**
spec_list
;
int
spec_count
;
cur_ptr
=
source
=
s
;
token
=
get_token
(
&
string
);
if
(
token
!=
TOK_STRING
)
{
SYNTAX_ERROR
(
"Unexpected token
\"
%s
\"
, expected name
\n
"
,
TOK_TO_STR
(
token
,
string
));
return
0
;
}
for
(
i
=
0
;
action_table
[
i
].
args
!=
-
1
;
i
++
)
if
(
strcmp
(
string
,
action_table
[
i
].
name
)
==
0
)
break
;
if
(
action_table
[
i
].
args
==
-
1
)
{
SYNTAX_ERROR
(
"Non-existent action
\"
%s
\"\n
"
,
string
);
return
0
;
}
action
=
&
action_table
[
i
];
token
=
get_token
(
&
string
);
if
(
token
==
TOK_AT
)
goto
skip_args
;
if
(
token
!=
TOK_OPEN_BRACKET
)
{
SYNTAX_ERROR
(
"Unexpected token
\"
%s
\"
, expected
\"
(
\"\n
"
,
TOK_TO_STR
(
token
,
string
));
goto
failed
;
}
/*
* speculatively read all the arguments, and then see if the
* number of arguments read is the number expected, this handles
* actions with a variable number of arguments
*/
token
=
get_token
(
&
string
);
if
(
token
==
TOK_CLOSE_BRACKET
)
goto
skip_args
;
while
(
1
)
{
if
(
token
!=
TOK_STRING
)
{
SYNTAX_ERROR
(
"Unexpected token
\"
%s
\"
, expected "
"argument
\n
"
,
TOK_TO_STR
(
token
,
string
));
goto
failed
;
}
argv
=
realloc
(
argv
,
(
args
+
1
)
*
sizeof
(
char
*
));
if
(
argv
==
NULL
)
MEM_ERROR
();
argv
[
args
++
]
=
strdup
(
string
);
token
=
get_token
(
&
string
);
if
(
token
==
TOK_CLOSE_BRACKET
)
break
;
if
(
token
!=
TOK_COMMA
)
{
SYNTAX_ERROR
(
"Unexpected token
\"
%s
\"
, expected "
"
\"
,
\"
or
\"
)
\"\n
"
,
TOK_TO_STR
(
token
,
string
));
goto
failed
;
}
token
=
get_token
(
&
string
);
}
skip_args:
/*
* expected number of arguments?
*/
if
(
action
->
args
!=
-
2
&&
args
!=
action
->
args
)
{
SYNTAX_ERROR
(
"Unexpected number of arguments, expected %d, "
"got %d
\n
"
,
action
->
args
,
args
);
goto
failed
;
}
if
(
action
->
parse_args
)
{
int
res
=
action
->
parse_args
(
action
,
args
,
argv
,
&
data
);
if
(
res
==
0
)
goto
failed
;
}
if
(
token
==
TOK_CLOSE_BRACKET
)
token
=
get_token
(
&
string
);
if
(
token
!=
TOK_AT
)
{
SYNTAX_ERROR
(
"Unexpected token
\"
%s
\"
, expected
\"
@
\"\n
"
,
TOK_TO_STR
(
token
,
string
));
goto
failed
;
}
parsing_action
=
action
;
expr
=
parse_expr
(
0
);
if
(
expr
==
NULL
)
goto
failed
;
/*
* choose action list and increment action counter
*/
switch
(
action
->
type
)
{
case
FRAGMENT_ACTION
:
spec_count
=
fragment_count
++
;
spec_list
=
&
fragment_spec
;
break
;
case
EXCLUDE_ACTION
:
spec_count
=
exclude_count
++
;
spec_list
=
&
exclude_spec
;
break
;
case
EMPTY_ACTION
:
spec_count
=
empty_count
++
;
spec_list
=
&
empty_spec
;
break
;
case
MOVE_ACTION
:
spec_count
=
move_count
++
;
spec_list
=
&
move_spec
;
break
;
case
PRUNE_ACTION
:
spec_count
=
prune_count
++
;
spec_list
=
&
prune_spec
;
break
;
default:
spec_count
=
other_count
++
;
spec_list
=
&
other_spec
;
}
*
spec_list
=
realloc
(
*
spec_list
,
(
spec_count
+
1
)
*
sizeof
(
struct
action
));
if
(
*
spec_list
==
NULL
)
MEM_ERROR
();
(
*
spec_list
)[
spec_count
].
type
=
action
->
type
;
(
*
spec_list
)[
spec_count
].
action
=
action
;
(
*
spec_list
)[
spec_count
].
args
=
args
;
(
*
spec_list
)[
spec_count
].
argv
=
argv
;
(
*
spec_list
)[
spec_count
].
expr
=
expr
;
(
*
spec_list
)[
spec_count
].
data
=
data
;
(
*
spec_list
)[
spec_count
].
verbose
=
verbose
;
return
1
;
failed:
free
(
argv
);
return
0
;
}
/*
* Evaluate expressions
*/
#define ALLOC_SZ 128
#define LOG_ENABLE 0
#define LOG_DISABLE 1
#define LOG_PRINT 2
#define LOG_ENABLED 3
char
*
_expr_log
(
char
*
string
,
int
cmnd
)
{
static
char
*
expr_msg
=
NULL
;
static
int
cur_size
=
0
,
alloc_size
=
0
;
int
size
;
switch
(
cmnd
)
{
case
LOG_ENABLE
:
expr_msg
=
malloc
(
ALLOC_SZ
);
alloc_size
=
ALLOC_SZ
;
cur_size
=
0
;
return
expr_msg
;
case
LOG_DISABLE
:
free
(
expr_msg
);
alloc_size
=
cur_size
=
0
;
return
expr_msg
=
NULL
;
case
LOG_ENABLED
:
return
expr_msg
;
default:
if
(
expr_msg
==
NULL
)
return
NULL
;
break
;
}
/* if string is empty append '\0' */
size
=
strlen
(
string
)
?
:
1
;
if
(
alloc_size
-
cur_size
<
size
)
{
/* buffer too small, expand */
alloc_size
=
(
cur_size
+
size
+
ALLOC_SZ
-
1
)
&
~
(
ALLOC_SZ
-
1
);
expr_msg
=
realloc
(
expr_msg
,
alloc_size
);
if
(
expr_msg
==
NULL
)
MEM_ERROR
();
}
memcpy
(
expr_msg
+
cur_size
,
string
,
size
);
cur_size
+=
size
;
return
expr_msg
;
}
char
*
expr_log_cmnd
(
int
cmnd
)
{
return
_expr_log
(
NULL
,
cmnd
);
}
char
*
expr_log
(
char
*
string
)
{
return
_expr_log
(
string
,
LOG_PRINT
);
}
void
expr_log_atom
(
struct
atom
*
atom
)
{
int
i
;
if
(
atom
->
test
->
handle_logging
)
return
;
expr_log
(
atom
->
test
->
name
);
if
(
atom
->
args
)
{
expr_log
(
"("
);
for
(
i
=
0
;
i
<
atom
->
args
;
i
++
)
{
expr_log
(
atom
->
argv
[
i
]);
if
(
i
+
1
<
atom
->
args
)
expr_log
(
","
);
}
expr_log
(
")"
);
}
}
void
expr_log_match
(
int
match
)
{
if
(
match
)
expr_log
(
"=True"
);
else
expr_log
(
"=False"
);
}
static
int
eval_expr_log
(
struct
expr
*
expr
,
struct
action_data
*
action_data
)
{
int
match
;
switch
(
expr
->
type
)
{
case
ATOM_TYPE
:
expr_log_atom
(
&
expr
->
atom
);
match
=
expr
->
atom
.
test
->
fn
(
&
expr
->
atom
,
action_data
);
expr_log_match
(
match
);
break
;
case
UNARY_TYPE
:
expr_log
(
"!"
);
match
=
!
eval_expr_log
(
expr
->
unary_op
.
expr
,
action_data
);
break
;
default:
expr_log
(
"("
);
match
=
eval_expr_log
(
expr
->
expr_op
.
lhs
,
action_data
);
if
((
expr
->
expr_op
.
op
==
TOK_AND
&&
match
)
||
(
expr
->
expr_op
.
op
==
TOK_OR
&&
!
match
))
{
expr_log
(
token_table
[
expr
->
expr_op
.
op
].
string
);
match
=
eval_expr_log
(
expr
->
expr_op
.
rhs
,
action_data
);
}
expr_log
(
")"
);
break
;
}
return
match
;
}
static
int
eval_expr
(
struct
expr
*
expr
,
struct
action_data
*
action_data
)
{
int
match
;
switch
(
expr
->
type
)
{
case
ATOM_TYPE
:
match
=
expr
->
atom
.
test
->
fn
(
&
expr
->
atom
,
action_data
);
break
;
case
UNARY_TYPE
:
match
=
!
eval_expr
(
expr
->
unary_op
.
expr
,
action_data
);
break
;
default:
match
=
eval_expr
(
expr
->
expr_op
.
lhs
,
action_data
);
if
((
expr
->
expr_op
.
op
==
TOK_AND
&&
match
)
||
(
expr
->
expr_op
.
op
==
TOK_OR
&&
!
match
))
match
=
eval_expr
(
expr
->
expr_op
.
rhs
,
action_data
);
break
;
}
return
match
;
}
static
int
eval_expr_top
(
struct
action
*
action
,
struct
action_data
*
action_data
)
{
if
(
action
->
verbose
)
{
int
match
,
n
;
expr_log_cmnd
(
LOG_ENABLE
);
if
(
action_data
->
subpath
)
expr_log
(
action_data
->
subpath
);
expr_log
(
"="
);
expr_log
(
action
->
action
->
name
);
if
(
action
->
args
)
{
expr_log
(
"("
);
for
(
n
=
0
;
n
<
action
->
args
;
n
++
)
{
expr_log
(
action
->
argv
[
n
]);
if
(
n
+
1
<
action
->
args
)
expr_log
(
","
);
}
expr_log
(
")"
);
}
expr_log
(
"@"
);
match
=
eval_expr_log
(
action
->
expr
,
action_data
);
/*
* Print the evaluated expression log, if the
* result matches the logging specified
*/
if
((
match
&&
(
action
->
verbose
&
ACTION_LOG_TRUE
))
||
(
!
match
&&
(
action
->
verbose
&
ACTION_LOG_FALSE
)))
progressbar_info
(
"%s
\n
"
,
expr_log
(
""
));
expr_log_cmnd
(
LOG_DISABLE
);
return
match
;
}
else
return
eval_expr
(
action
->
expr
,
action_data
);
}
/*
* Read action file, passing each line to parse_action() for
* parsing.
*
* One action per line, of the form
* action(arg1,arg2)@expr(arg1,arg2)....
*
* Actions can be split across multiple lines using "\".
*
* Blank lines and comment lines indicated by # are supported.
*/
int
parse_action_true
(
char
*
s
)
{
return
parse_action
(
s
,
ACTION_LOG_TRUE
);
}
int
parse_action_false
(
char
*
s
)
{
return
parse_action
(
s
,
ACTION_LOG_FALSE
);
}
int
parse_action_verbose
(
char
*
s
)
{
return
parse_action
(
s
,
ACTION_LOG_VERBOSE
);
}
int
parse_action_nonverbose
(
char
*
s
)
{
return
parse_action
(
s
,
ACTION_LOG_NONE
);
}
int
read_action_file
(
char
*
filename
,
int
verbose
)
{
switch
(
verbose
)
{
case
ACTION_LOG_TRUE
:
return
read_file
(
filename
,
"action"
,
parse_action_true
);
case
ACTION_LOG_FALSE
:
return
read_file
(
filename
,
"action"
,
parse_action_false
);
case
ACTION_LOG_VERBOSE
:
return
read_file
(
filename
,
"action"
,
parse_action_verbose
);
default:
return
read_file
(
filename
,
"action"
,
parse_action_nonverbose
);
}
}
/*
* helper to evaluate whether action/test acts on this file type
*/
static
int
file_type_match
(
int
st_mode
,
int
type
)
{
switch
(
type
)
{
case
ACTION_DIR
:
return
S_ISDIR
(
st_mode
);
case
ACTION_REG
:
return
S_ISREG
(
st_mode
);
case
ACTION_ALL
:
return
S_ISREG
(
st_mode
)
||
S_ISDIR
(
st_mode
)
||
S_ISCHR
(
st_mode
)
||
S_ISBLK
(
st_mode
)
||
S_ISFIFO
(
st_mode
)
||
S_ISSOCK
(
st_mode
);
case
ACTION_LNK
:
return
S_ISLNK
(
st_mode
);
case
ACTION_ALL_LNK
:
default:
return
1
;
}
}
/*
* General action evaluation code
*/
int
actions
()
{
return
other_count
;
}
void
eval_actions
(
struct
dir_info
*
root
,
struct
dir_ent
*
dir_ent
)
{
int
i
,
match
;
struct
action_data
action_data
;
int
st_mode
=
dir_ent
->
inode
->
buf
.
st_mode
;
action_data
.
name
=
dir_ent
->
name
;
action_data
.
pathname
=
strdup
(
pathname
(
dir_ent
));
action_data
.
subpath
=
strdup
(
subpathname
(
dir_ent
));
action_data
.
buf
=
&
dir_ent
->
inode
->
buf
;
action_data
.
depth
=
dir_ent
->
our_dir
->
depth
;
action_data
.
dir_ent
=
dir_ent
;
action_data
.
root
=
root
;
for
(
i
=
0
;
i
<
other_count
;
i
++
)
{
struct
action
*
action
=
&
other_spec
[
i
];
if
(
!
file_type_match
(
st_mode
,
action
->
action
->
file_types
))
/* action does not operate on this file type */
continue
;
match
=
eval_expr_top
(
action
,
&
action_data
);
if
(
match
)
action
->
action
->
run_action
(
action
,
dir_ent
);
}
free
(
action_data
.
pathname
);
free
(
action_data
.
subpath
);
}
/*
* Fragment specific action code
*/
void
*
eval_frag_actions
(
struct
dir_info
*
root
,
struct
dir_ent
*
dir_ent
)
{
int
i
,
match
;
struct
action_data
action_data
;
action_data
.
name
=
dir_ent
->
name
;
action_data
.
pathname
=
strdup
(
pathname
(
dir_ent
));
action_data
.
subpath
=
strdup
(
subpathname
(
dir_ent
));
action_data
.
buf
=
&
dir_ent
->
inode
->
buf
;
action_data
.
depth
=
dir_ent
->
our_dir
->
depth
;
action_data
.
dir_ent
=
dir_ent
;
action_data
.
root
=
root
;
for
(
i
=
0
;
i
<
fragment_count
;
i
++
)
{
match
=
eval_expr_top
(
&
fragment_spec
[
i
],
&
action_data
);
if
(
match
)
{
free
(
action_data
.
pathname
);
free
(
action_data
.
subpath
);
return
&
fragment_spec
[
i
].
data
;
}
}
free
(
action_data
.
pathname
);
free
(
action_data
.
subpath
);
return
&
def_fragment
;
}
void
*
get_frag_action
(
void
*
fragment
)
{
struct
action
*
spec_list_end
=
&
fragment_spec
[
fragment_count
];
struct
action
*
action
;
if
(
fragment
==
NULL
)
return
&
def_fragment
;
if
(
fragment_count
==
0
)
return
NULL
;
if
(
fragment
==
&
def_fragment
)
action
=
&
fragment_spec
[
0
]
-
1
;
else
action
=
fragment
-
offsetof
(
struct
action
,
data
);
if
(
++
action
==
spec_list_end
)
return
NULL
;
return
&
action
->
data
;
}
/*
* Exclude specific action code
*/
int
exclude_actions
()
{
return
exclude_count
;
}
int
eval_exclude_actions
(
char
*
name
,
char
*
pathname
,
char
*
subpath
,
struct
stat
*
buf
,
int
depth
,
struct
dir_ent
*
dir_ent
)
{
int
i
,
match
=
0
;
struct
action_data
action_data
;
action_data
.
name
=
name
;
action_data
.
pathname
=
pathname
;
action_data
.
subpath
=
subpath
;
action_data
.
buf
=
buf
;
action_data
.
depth
=
depth
;
action_data
.
dir_ent
=
dir_ent
;
for
(
i
=
0
;
i
<
exclude_count
&&
!
match
;
i
++
)
match
=
eval_expr_top
(
&
exclude_spec
[
i
],
&
action_data
);
return
match
;
}
/*
* Fragment specific action code
*/
static
void
frag_action
(
struct
action
*
action
,
struct
dir_ent
*
dir_ent
)
{
struct
inode_info
*
inode
=
dir_ent
->
inode
;
inode
->
no_fragments
=
0
;
}
static
void
no_frag_action
(
struct
action
*
action
,
struct
dir_ent
*
dir_ent
)
{
struct
inode_info
*
inode
=
dir_ent
->
inode
;
inode
->
no_fragments
=
1
;
}
static
void
always_frag_action
(
struct
action
*
action
,
struct
dir_ent
*
dir_ent
)
{
struct
inode_info
*
inode
=
dir_ent
->
inode
;
inode
->
always_use_fragments
=
1
;
}
static
void
no_always_frag_action
(
struct
action
*
action
,
struct
dir_ent
*
dir_ent
)
{
struct
inode_info
*
inode
=
dir_ent
->
inode
;
inode
->
always_use_fragments
=
0
;
}
/*
* Compression specific action code
*/
static
void
comp_action
(
struct
action
*
action
,
struct
dir_ent
*
dir_ent
)
{
struct
inode_info
*
inode
=
dir_ent
->
inode
;
inode
->
noD
=
inode
->
noF
=
0
;
}
static
void
uncomp_action
(
struct
action
*
action
,
struct
dir_ent
*
dir_ent
)
{
struct
inode_info
*
inode
=
dir_ent
->
inode
;
inode
->
noD
=
inode
->
noF
=
1
;
}
/*
* Uid/gid specific action code
*/
static
long
long
parse_uid
(
char
*
arg
)
{
char
*
b
;
long
long
uid
=
strtoll
(
arg
,
&
b
,
10
);
if
(
*
b
==
'\0'
)
{
if
(
uid
<
0
||
uid
>=
(
1LL
<<
32
))
{
SYNTAX_ERROR
(
"Uid out of range
\n
"
);
return
-
1
;
}
}
else
{
struct
passwd
*
passwd
=
getpwnam
(
arg
);
if
(
passwd
)
uid
=
passwd
->
pw_uid
;
else
{
SYNTAX_ERROR
(
"Invalid uid or unknown user
\n
"
);
return
-
1
;
}
}
return
uid
;
}
static
long
long
parse_gid
(
char
*
arg
)
{
char
*
b
;
long
long
gid
=
strtoll
(
arg
,
&
b
,
10
);
if
(
*
b
==
'\0'
)
{
if
(
gid
<
0
||
gid
>=
(
1LL
<<
32
))
{
SYNTAX_ERROR
(
"Gid out of range
\n
"
);
return
-
1
;
}
}
else
{
struct
group
*
group
=
getgrnam
(
arg
);
if
(
group
)
gid
=
group
->
gr_gid
;
else
{
SYNTAX_ERROR
(
"Invalid gid or unknown group
\n
"
);
return
-
1
;
}
}
return
gid
;
}
static
int
parse_uid_args
(
struct
action_entry
*
action
,
int
args
,
char
**
argv
,
void
**
data
)
{
long
long
uid
;
struct
uid_info
*
uid_info
;
uid
=
parse_uid
(
argv
[
0
]);
if
(
uid
==
-
1
)
return
0
;
uid_info
=
malloc
(
sizeof
(
struct
uid_info
));
if
(
uid_info
==
NULL
)
MEM_ERROR
();
uid_info
->
uid
=
uid
;
*
data
=
uid_info
;
return
1
;
}
static
int
parse_gid_args
(
struct
action_entry
*
action
,
int
args
,
char
**
argv
,
void
**
data
)
{
long
long
gid
;
struct
gid_info
*
gid_info
;
gid
=
parse_gid
(
argv
[
0
]);
if
(
gid
==
-
1
)
return
0
;
gid_info
=
malloc
(
sizeof
(
struct
gid_info
));
if
(
gid_info
==
NULL
)
MEM_ERROR
();
gid_info
->
gid
=
gid
;
*
data
=
gid_info
;
return
1
;
}
static
int
parse_guid_args
(
struct
action_entry
*
action
,
int
args
,
char
**
argv
,
void
**
data
)
{
long
long
uid
,
gid
;
struct
guid_info
*
guid_info
;
uid
=
parse_uid
(
argv
[
0
]);
if
(
uid
==
-
1
)
return
0
;
gid
=
parse_gid
(
argv
[
1
]);
if
(
gid
==
-
1
)
return
0
;
guid_info
=
malloc
(
sizeof
(
struct
guid_info
));
if
(
guid_info
==
NULL
)
MEM_ERROR
();
guid_info
->
uid
=
uid
;
guid_info
->
gid
=
gid
;
*
data
=
guid_info
;
return
1
;
}
static
void
uid_action
(
struct
action
*
action
,
struct
dir_ent
*
dir_ent
)
{
struct
inode_info
*
inode
=
dir_ent
->
inode
;
struct
uid_info
*
uid_info
=
action
->
data
;
inode
->
buf
.
st_uid
=
uid_info
->
uid
;
}
static
void
gid_action
(
struct
action
*
action
,
struct
dir_ent
*
dir_ent
)
{
struct
inode_info
*
inode
=
dir_ent
->
inode
;
struct
gid_info
*
gid_info
=
action
->
data
;
inode
->
buf
.
st_gid
=
gid_info
->
gid
;
}
static
void
guid_action
(
struct
action
*
action
,
struct
dir_ent
*
dir_ent
)
{
struct
inode_info
*
inode
=
dir_ent
->
inode
;
struct
guid_info
*
guid_info
=
action
->
data
;
inode
->
buf
.
st_uid
=
guid_info
->
uid
;
inode
->
buf
.
st_gid
=
guid_info
->
gid
;
}
/*
* Mode specific action code
*/
static
int
parse_octal_mode_args
(
int
args
,
char
**
argv
,
void
**
data
)
{
int
n
,
bytes
;
unsigned
int
mode
;
struct
mode_data
*
mode_data
;
/* octal mode number? */
n
=
sscanf
(
argv
[
0
],
"%o%n"
,
&
mode
,
&
bytes
);
if
(
n
==
0
)
return
-
1
;
/* not an octal number arg */
/* check there's no trailing junk */
if
(
argv
[
0
][
bytes
]
!=
'\0'
)
{
SYNTAX_ERROR
(
"Unexpected trailing bytes after octal "
"mode number
\n
"
);
return
0
;
/* bad octal number arg */
}
/* check there's only one argument */
if
(
args
>
1
)
{
SYNTAX_ERROR
(
"Octal mode number is first argument, "
"expected one argument, got %d
\n
"
,
args
);
return
0
;
/* bad octal number arg */
}
/* check mode is within range */
if
(
mode
>
07777
)
{
SYNTAX_ERROR
(
"Octal mode %o is out of range
\n
"
,
mode
);
return
0
;
/* bad octal number arg */
}
mode_data
=
malloc
(
sizeof
(
struct
mode_data
));
if
(
mode_data
==
NULL
)
MEM_ERROR
();
mode_data
->
operation
=
ACTION_MODE_OCT
;
mode_data
->
mode
=
mode
;
mode_data
->
next
=
NULL
;
*
data
=
mode_data
;
return
1
;
}
/*
* Parse symbolic mode of format [ugoa]*[[+-=]PERMS]+
* PERMS = [rwxXst]+ or [ugo]
*/
static
int
parse_sym_mode_arg
(
char
*
arg
,
struct
mode_data
**
head
,
struct
mode_data
**
cur
)
{
struct
mode_data
*
mode_data
;
int
mode
;
int
mask
=
0
;
int
op
;
char
X
;
if
(
arg
[
0
]
!=
'u'
&&
arg
[
0
]
!=
'g'
&&
arg
[
0
]
!=
'o'
&&
arg
[
0
]
!=
'a'
)
{
/* no ownership specifiers, default to a */
mask
=
0777
;
goto
parse_operation
;
}
/* parse ownership specifiers */
while
(
1
)
{
switch
(
*
arg
)
{
case
'u'
:
mask
|=
04700
;
break
;
case
'g'
:
mask
|=
02070
;
break
;
case
'o'
:
mask
|=
01007
;
break
;
case
'a'
:
mask
=
07777
;
break
;
default:
goto
parse_operation
;
}
arg
++
;
}
parse_operation:
/* trap a symbolic mode with just an ownership specification */
if
(
*
arg
==
'\0'
)
{
SYNTAX_ERROR
(
"Expected one of '+', '-' or '=', got EOF
\n
"
);
goto
failed
;
}
while
(
*
arg
!=
'\0'
)
{
mode
=
0
;
X
=
0
;
switch
(
*
arg
)
{
case
'+'
:
op
=
ACTION_MODE_ADD
;
break
;
case
'-'
:
op
=
ACTION_MODE_REM
;
break
;
case
'='
:
op
=
ACTION_MODE_SET
;
break
;
default:
SYNTAX_ERROR
(
"Expected one of '+', '-' or '=', got "
"'%c'
\n
"
,
*
arg
);
goto
failed
;
}
arg
++
;
/* Parse PERMS */
if
(
*
arg
==
'u'
||
*
arg
==
'g'
||
*
arg
==
'o'
)
{
/* PERMS = [ugo] */
mode
=
-
*
arg
;
arg
++
;
}
else
{
/* PERMS = [rwxXst]* */
while
(
1
)
{
switch
(
*
arg
)
{
case
'r'
:
mode
|=
0444
;
break
;
case
'w'
:
mode
|=
0222
;
break
;
case
'x'
:
mode
|=
0111
;
break
;
case
's'
:
mode
|=
06000
;
break
;
case
't'
:
mode
|=
01000
;
break
;
case
'X'
:
X
=
1
;
break
;
case
'+'
:
case
'-'
:
case
'='
:
case
'\0'
:
mode
&=
mask
;
goto
perms_parsed
;
default:
SYNTAX_ERROR
(
"Unrecognised permission "
"'%c'
\n
"
,
*
arg
);
goto
failed
;
}
arg
++
;
}
}
perms_parsed:
mode_data
=
malloc
(
sizeof
(
*
mode_data
));
if
(
mode_data
==
NULL
)
MEM_ERROR
();
mode_data
->
operation
=
op
;
mode_data
->
mode
=
mode
;
mode_data
->
mask
=
mask
;
mode_data
->
X
=
X
;
mode_data
->
next
=
NULL
;
if
(
*
cur
)
{
(
*
cur
)
->
next
=
mode_data
;
*
cur
=
mode_data
;
}
else
*
head
=
*
cur
=
mode_data
;
}
return
1
;
failed:
return
0
;
}
static
int
parse_sym_mode_args
(
struct
action_entry
*
action
,
int
args
,
char
**
argv
,
void
**
data
)
{
int
i
,
res
=
1
;
struct
mode_data
*
head
=
NULL
,
*
cur
=
NULL
;
for
(
i
=
0
;
i
<
args
&&
res
;
i
++
)
res
=
parse_sym_mode_arg
(
argv
[
i
],
&
head
,
&
cur
);
*
data
=
head
;
return
res
;
}
static
int
parse_mode_args
(
struct
action_entry
*
action
,
int
args
,
char
**
argv
,
void
**
data
)
{
int
res
;
if
(
args
==
0
)
{
SYNTAX_ERROR
(
"Mode action expects one or more arguments
\n
"
);
return
0
;
}
res
=
parse_octal_mode_args
(
args
,
argv
,
data
);
if
(
res
>=
0
)
/* Got an octal mode argument */
return
res
;
else
/* not an octal mode argument */
return
parse_sym_mode_args
(
action
,
args
,
argv
,
data
);
}
static
int
mode_execute
(
struct
mode_data
*
mode_data
,
int
st_mode
)
{
int
mode
=
0
;
for
(;
mode_data
;
mode_data
=
mode_data
->
next
)
{
if
(
mode_data
->
mode
<
0
)
{
/* 'u', 'g' or 'o' */
switch
(
-
mode_data
->
mode
)
{
case
'u'
:
mode
=
(
st_mode
>>
6
)
&
07
;
break
;
case
'g'
:
mode
=
(
st_mode
>>
3
)
&
07
;
break
;
case
'o'
:
mode
=
st_mode
&
07
;
break
;
}
mode
=
((
mode
<<
6
)
|
(
mode
<<
3
)
|
mode
)
&
mode_data
->
mask
;
}
else
if
(
mode_data
->
X
&&
((
st_mode
&
S_IFMT
)
==
S_IFDIR
||
(
st_mode
&
0111
)))
/* X permission, only takes effect if inode is a
* directory or x is set for some owner */
mode
=
mode_data
->
mode
|
(
0111
&
mode_data
->
mask
);
else
mode
=
mode_data
->
mode
;
switch
(
mode_data
->
operation
)
{
case
ACTION_MODE_OCT
:
st_mode
=
(
st_mode
&
S_IFMT
)
|
mode
;
break
;
case
ACTION_MODE_SET
:
st_mode
=
(
st_mode
&
~
mode_data
->
mask
)
|
mode
;
break
;
case
ACTION_MODE_ADD
:
st_mode
|=
mode
;
break
;
case
ACTION_MODE_REM
:
st_mode
&=
~
mode
;
}
}
return
st_mode
;
}
static
void
mode_action
(
struct
action
*
action
,
struct
dir_ent
*
dir_ent
)
{
dir_ent
->
inode
->
buf
.
st_mode
=
mode_execute
(
action
->
data
,
dir_ent
->
inode
->
buf
.
st_mode
);
}
/*
* Empty specific action code
*/
int
empty_actions
()
{
return
empty_count
;
}
static
int
parse_empty_args
(
struct
action_entry
*
action
,
int
args
,
char
**
argv
,
void
**
data
)
{
struct
empty_data
*
empty_data
;
int
val
;
if
(
args
>=
2
)
{
SYNTAX_ERROR
(
"Empty action expects zero or one argument
\n
"
);
return
0
;
}
if
(
args
==
0
||
strcmp
(
argv
[
0
],
"all"
)
==
0
)
val
=
EMPTY_ALL
;
else
if
(
strcmp
(
argv
[
0
],
"source"
)
==
0
)
val
=
EMPTY_SOURCE
;
else
if
(
strcmp
(
argv
[
0
],
"excluded"
)
==
0
)
val
=
EMPTY_EXCLUDED
;
else
{
SYNTAX_ERROR
(
"Empty action expects zero arguments, or one"
"argument containing
\"
all
\"
,
\"
source
\"
, or
\"
excluded
\"
"
"
\n
"
);
return
0
;
}
empty_data
=
malloc
(
sizeof
(
*
empty_data
));
if
(
empty_data
==
NULL
)
MEM_ERROR
();
empty_data
->
val
=
val
;
*
data
=
empty_data
;
return
1
;
}
int
eval_empty_actions
(
struct
dir_info
*
root
,
struct
dir_ent
*
dir_ent
)
{
int
i
,
match
=
0
;
struct
action_data
action_data
;
struct
empty_data
*
data
;
struct
dir_info
*
dir
=
dir_ent
->
dir
;
/*
* Empty action only works on empty directories
*/
if
(
dir
->
count
!=
0
)
return
0
;
action_data
.
name
=
dir_ent
->
name
;
action_data
.
pathname
=
strdup
(
pathname
(
dir_ent
));
action_data
.
subpath
=
strdup
(
subpathname
(
dir_ent
));
action_data
.
buf
=
&
dir_ent
->
inode
->
buf
;
action_data
.
depth
=
dir_ent
->
our_dir
->
depth
;
action_data
.
dir_ent
=
dir_ent
;
action_data
.
root
=
root
;
for
(
i
=
0
;
i
<
empty_count
&&
!
match
;
i
++
)
{
data
=
empty_spec
[
i
].
data
;
/*
* determine the cause of the empty directory and evaluate
* the empty action specified. Three empty actions:
* - EMPTY_SOURCE: empty action triggers only if the directory
* was originally empty, i.e directories that are empty
* only due to excluding are ignored.
* - EMPTY_EXCLUDED: empty action triggers only if the directory
* is empty because of excluding, i.e. directories that
* were originally empty are ignored.
* - EMPTY_ALL (the default): empty action triggers if the
* directory is empty, irrespective of the reason, i.e.
* the directory could have been originally empty or could
* be empty due to excluding.
*/
if
((
data
->
val
==
EMPTY_EXCLUDED
&&
!
dir
->
excluded
)
||
(
data
->
val
==
EMPTY_SOURCE
&&
dir
->
excluded
))
continue
;
match
=
eval_expr_top
(
&
empty_spec
[
i
],
&
action_data
);
}
free
(
action_data
.
pathname
);
free
(
action_data
.
subpath
);
return
match
;
}
/*
* Move specific action code
*/
static
struct
move_ent
*
move_list
=
NULL
;
int
move_actions
()
{
return
move_count
;
}
static
char
*
move_pathname
(
struct
move_ent
*
move
)
{
struct
dir_info
*
dest
;
char
*
name
,
*
pathname
;
int
res
;
dest
=
(
move
->
ops
&
ACTION_MOVE_MOVE
)
?
move
->
dest
:
move
->
dir_ent
->
our_dir
;
name
=
(
move
->
ops
&
ACTION_MOVE_RENAME
)
?
move
->
name
:
move
->
dir_ent
->
name
;
if
(
dest
->
subpath
[
0
]
!=
'\0'
)
res
=
asprintf
(
&
pathname
,
"%s/%s"
,
dest
->
subpath
,
name
);
else
res
=
asprintf
(
&
pathname
,
"/%s"
,
name
);
if
(
res
==
-
1
)
BAD_ERROR
(
"asprintf failed in move_pathname
\n
"
);
return
pathname
;
}
static
char
*
get_comp
(
char
**
pathname
)
{
char
*
path
=
*
pathname
,
*
start
;
while
(
*
path
==
'/'
)
path
++
;
if
(
*
path
==
'\0'
)
return
NULL
;
start
=
path
;
while
(
*
path
!=
'/'
&&
*
path
!=
'\0'
)
path
++
;
*
pathname
=
path
;
return
strndup
(
start
,
path
-
start
);
}
static
struct
dir_ent
*
lookup_comp
(
char
*
comp
,
struct
dir_info
*
dest
)
{
struct
dir_ent
*
dir_ent
;
for
(
dir_ent
=
dest
->
list
;
dir_ent
;
dir_ent
=
dir_ent
->
next
)
if
(
strcmp
(
comp
,
dir_ent
->
name
)
==
0
)
break
;
return
dir_ent
;
}
void
eval_move
(
struct
action_data
*
action_data
,
struct
move_ent
*
move
,
struct
dir_info
*
root
,
struct
dir_ent
*
dir_ent
,
char
*
pathname
)
{
struct
dir_info
*
dest
,
*
source
=
dir_ent
->
our_dir
;
struct
dir_ent
*
comp_ent
;
char
*
comp
,
*
path
=
pathname
;
/*
* Walk pathname to get the destination directory
*
* Like the mv command, if the last component exists and it
* is a directory, then move the file into that directory,
* otherwise, move the file into parent directory of the last
* component and rename to the last component.
*/
if
(
pathname
[
0
]
==
'/'
)
/* absolute pathname, walk from root directory */
dest
=
root
;
else
/* relative pathname, walk from current directory */
dest
=
source
;
for
(
comp
=
get_comp
(
&
pathname
);
comp
;
free
(
comp
),
comp
=
get_comp
(
&
pathname
))
{
if
(
strcmp
(
comp
,
"."
)
==
0
)
continue
;
if
(
strcmp
(
comp
,
".."
)
==
0
)
{
/* if we're in the root directory then ignore */
if
(
dest
->
depth
>
1
)
dest
=
dest
->
dir_ent
->
our_dir
;
continue
;
}
/*
* Look up comp in current directory, if it exists and it is a
* directory continue walking the pathname, otherwise exit,
* we've walked as far as we can go, normally this is because
* we've arrived at the leaf component which we are going to
* rename source to
*/
comp_ent
=
lookup_comp
(
comp
,
dest
);
if
(
comp_ent
==
NULL
||
(
comp_ent
->
inode
->
buf
.
st_mode
&
S_IFMT
)
!=
S_IFDIR
)
break
;
dest
=
comp_ent
->
dir
;
}
if
(
comp
)
{
/* Leaf component? If so we're renaming to this */
char
*
remainder
=
get_comp
(
&
pathname
);
free
(
remainder
);
if
(
remainder
)
{
/*
* trying to move source to a subdirectory of
* comp, but comp either doesn't exist, or it isn't
* a directory, which is impossible
*/
if
(
comp_ent
==
NULL
)
ERROR
(
"Move action: cannot move %s to %s, no "
"such directory %s
\n
"
,
action_data
->
subpath
,
path
,
comp
);
else
ERROR
(
"Move action: cannot move %s to %s, %s "
"is not a directory
\n
"
,
action_data
->
subpath
,
path
,
comp
);
free
(
comp
);
return
;
}
/*
* Multiple move actions triggering on one file can be merged
* if one is a RENAME and the other is a MOVE. Multiple RENAMEs
* can only merge if they're doing the same thing
*/
if
(
move
->
ops
&
ACTION_MOVE_RENAME
)
{
if
(
strcmp
(
comp
,
move
->
name
)
!=
0
)
{
char
*
conf_path
=
move_pathname
(
move
);
ERROR
(
"Move action: Cannot move %s to %s, "
"conflicting move, already moving "
"to %s via another move action!
\n
"
,
action_data
->
subpath
,
path
,
conf_path
);
free
(
conf_path
);
free
(
comp
);
return
;
}
free
(
comp
);
}
else
{
move
->
name
=
comp
;
move
->
ops
|=
ACTION_MOVE_RENAME
;
}
}
if
(
dest
!=
source
)
{
/*
* Multiple move actions triggering on one file can be merged
* if one is a RENAME and the other is a MOVE. Multiple MOVEs
* can only merge if they're doing the same thing
*/
if
(
move
->
ops
&
ACTION_MOVE_MOVE
)
{
if
(
dest
!=
move
->
dest
)
{
char
*
conf_path
=
move_pathname
(
move
);
ERROR
(
"Move action: Cannot move %s to %s, "
"conflicting move, already moving "
"to %s via another move action!
\n
"
,
action_data
->
subpath
,
path
,
conf_path
);
free
(
conf_path
);
return
;
}
}
else
{
move
->
dest
=
dest
;
move
->
ops
|=
ACTION_MOVE_MOVE
;
}
}
}
static
int
subdirectory
(
struct
dir_info
*
source
,
struct
dir_info
*
dest
)
{
if
(
source
==
NULL
)
return
0
;
return
strlen
(
source
->
subpath
)
<=
strlen
(
dest
->
subpath
)
&&
(
dest
->
subpath
[
strlen
(
source
->
subpath
)]
==
'/'
||
dest
->
subpath
[
strlen
(
source
->
subpath
)]
==
'\0'
)
&&
strncmp
(
source
->
subpath
,
dest
->
subpath
,
strlen
(
source
->
subpath
))
==
0
;
}
void
eval_move_actions
(
struct
dir_info
*
root
,
struct
dir_ent
*
dir_ent
)
{
int
i
;
struct
action_data
action_data
;
struct
move_ent
*
move
=
NULL
;
action_data
.
name
=
dir_ent
->
name
;
action_data
.
pathname
=
strdup
(
pathname
(
dir_ent
));
action_data
.
subpath
=
strdup
(
subpathname
(
dir_ent
));
action_data
.
buf
=
&
dir_ent
->
inode
->
buf
;
action_data
.
depth
=
dir_ent
->
our_dir
->
depth
;
action_data
.
dir_ent
=
dir_ent
;
action_data
.
root
=
root
;
/*
* Evaluate each move action against the current file. For any
* move actions that match don't actually perform the move now, but,
* store it, and execute all the stored move actions together once the
* directory scan is complete. This is done to ensure each separate
* move action does not nondeterministically interfere with other move
* actions. Each move action is considered to act independently, and
* each move action sees the directory tree in the same state.
*/
for
(
i
=
0
;
i
<
move_count
;
i
++
)
{
struct
action
*
action
=
&
move_spec
[
i
];
int
match
=
eval_expr_top
(
action
,
&
action_data
);
if
(
match
)
{
if
(
move
==
NULL
)
{
move
=
malloc
(
sizeof
(
*
move
));
if
(
move
==
NULL
)
MEM_ERROR
();
move
->
ops
=
0
;
move
->
dir_ent
=
dir_ent
;
}
eval_move
(
&
action_data
,
move
,
root
,
dir_ent
,
action
->
argv
[
0
]);
}
}
if
(
move
)
{
struct
dir_ent
*
comp_ent
;
struct
dir_info
*
dest
;
char
*
name
;
/*
* Move contains the result of all triggered move actions.
* Check the destination doesn't already exist
*/
if
(
move
->
ops
==
0
)
{
free
(
move
);
goto
finish
;
}
dest
=
(
move
->
ops
&
ACTION_MOVE_MOVE
)
?
move
->
dest
:
dir_ent
->
our_dir
;
name
=
(
move
->
ops
&
ACTION_MOVE_RENAME
)
?
move
->
name
:
dir_ent
->
name
;
comp_ent
=
lookup_comp
(
name
,
dest
);
if
(
comp_ent
)
{
char
*
conf_path
=
move_pathname
(
move
);
ERROR
(
"Move action: Cannot move %s to %s, "
"destination already exists
\n
"
,
action_data
.
subpath
,
conf_path
);
free
(
conf_path
);
free
(
move
);
goto
finish
;
}
/*
* If we're moving a directory, check we're not moving it to a
* subdirectory of itself
*/
if
(
subdirectory
(
dir_ent
->
dir
,
dest
))
{
char
*
conf_path
=
move_pathname
(
move
);
ERROR
(
"Move action: Cannot move %s to %s, this is a "
"subdirectory of itself
\n
"
,
action_data
.
subpath
,
conf_path
);
free
(
conf_path
);
free
(
move
);
goto
finish
;
}
move
->
next
=
move_list
;
move_list
=
move
;
}
finish:
free
(
action_data
.
pathname
);
free
(
action_data
.
subpath
);
}
static
void
move_dir
(
struct
dir_ent
*
dir_ent
)
{
struct
dir_info
*
dir
=
dir_ent
->
dir
;
struct
dir_ent
*
comp_ent
;
/* update our directory's subpath name */
free
(
dir
->
subpath
);
dir
->
subpath
=
strdup
(
subpathname
(
dir_ent
));
/* recursively update the subpaths of any sub-directories */
for
(
comp_ent
=
dir
->
list
;
comp_ent
;
comp_ent
=
comp_ent
->
next
)
if
(
comp_ent
->
dir
)
move_dir
(
comp_ent
);
}
static
void
move_file
(
struct
move_ent
*
move_ent
)
{
struct
dir_ent
*
dir_ent
=
move_ent
->
dir_ent
;
if
(
move_ent
->
ops
&
ACTION_MOVE_MOVE
)
{
struct
dir_ent
*
comp_ent
,
*
prev
=
NULL
;
struct
dir_info
*
source
=
dir_ent
->
our_dir
,
*
dest
=
move_ent
->
dest
;
char
*
filename
=
pathname
(
dir_ent
);
/*
* If we're moving a directory, check we're not moving it to a
* subdirectory of itself
*/
if
(
subdirectory
(
dir_ent
->
dir
,
dest
))
{
char
*
conf_path
=
move_pathname
(
move_ent
);
ERROR
(
"Move action: Cannot move %s to %s, this is a "
"subdirectory of itself
\n
"
,
subpathname
(
dir_ent
),
conf_path
);
free
(
conf_path
);
return
;
}
/* Remove the file from source directory */
for
(
comp_ent
=
source
->
list
;
comp_ent
!=
dir_ent
;
prev
=
comp_ent
,
comp_ent
=
comp_ent
->
next
);
if
(
prev
)
prev
->
next
=
comp_ent
->
next
;
else
source
->
list
=
comp_ent
->
next
;
source
->
count
--
;
if
((
comp_ent
->
inode
->
buf
.
st_mode
&
S_IFMT
)
==
S_IFDIR
)
source
->
directory_count
--
;
/* Add the file to dest directory */
comp_ent
->
next
=
dest
->
list
;
dest
->
list
=
comp_ent
;
comp_ent
->
our_dir
=
dest
;
dest
->
count
++
;
if
((
comp_ent
->
inode
->
buf
.
st_mode
&
S_IFMT
)
==
S_IFDIR
)
dest
->
directory_count
++
;
/*
* We've moved the file, and so we can't now use the
* parent directory's pathname to calculate the pathname
*/
if
(
dir_ent
->
nonstandard_pathname
==
NULL
)
{
dir_ent
->
nonstandard_pathname
=
strdup
(
filename
);
if
(
dir_ent
->
source_name
)
{
free
(
dir_ent
->
source_name
);
dir_ent
->
source_name
=
NULL
;
}
}
}
if
(
move_ent
->
ops
&
ACTION_MOVE_RENAME
)
{
/*
* If we're using name in conjunction with the parent
* directory's pathname to calculate the pathname, we need
* to use source_name to override. Otherwise it's already being
* over-ridden
*/
if
(
dir_ent
->
nonstandard_pathname
==
NULL
&&
dir_ent
->
source_name
==
NULL
)
dir_ent
->
source_name
=
dir_ent
->
name
;
else
free
(
dir_ent
->
name
);
dir_ent
->
name
=
move_ent
->
name
;
}
if
(
dir_ent
->
dir
)
/*
* dir_ent is a directory, and we have to recursively fix-up
* its subpath, and the subpaths of all of its sub-directories
*/
move_dir
(
dir_ent
);
}
void
do_move_actions
()
{
while
(
move_list
)
{
struct
move_ent
*
temp
=
move_list
;
struct
dir_info
*
dest
=
(
move_list
->
ops
&
ACTION_MOVE_MOVE
)
?
move_list
->
dest
:
move_list
->
dir_ent
->
our_dir
;
char
*
name
=
(
move_list
->
ops
&
ACTION_MOVE_RENAME
)
?
move_list
->
name
:
move_list
->
dir_ent
->
name
;
struct
dir_ent
*
comp_ent
=
lookup_comp
(
name
,
dest
);
if
(
comp_ent
)
{
char
*
conf_path
=
move_pathname
(
move_list
);
ERROR
(
"Move action: Cannot move %s to %s, "
"destination already exists
\n
"
,
subpathname
(
move_list
->
dir_ent
),
conf_path
);
free
(
conf_path
);
}
else
move_file
(
move_list
);
move_list
=
move_list
->
next
;
free
(
temp
);
}
}
/*
* Prune specific action code
*/
int
prune_actions
()
{
return
prune_count
;
}
int
eval_prune_actions
(
struct
dir_info
*
root
,
struct
dir_ent
*
dir_ent
)
{
int
i
,
match
=
0
;
struct
action_data
action_data
;
action_data
.
name
=
dir_ent
->
name
;
action_data
.
pathname
=
strdup
(
pathname
(
dir_ent
));
action_data
.
subpath
=
strdup
(
subpathname
(
dir_ent
));
action_data
.
buf
=
&
dir_ent
->
inode
->
buf
;
action_data
.
depth
=
dir_ent
->
our_dir
->
depth
;
action_data
.
dir_ent
=
dir_ent
;
action_data
.
root
=
root
;
for
(
i
=
0
;
i
<
prune_count
&&
!
match
;
i
++
)
match
=
eval_expr_top
(
&
prune_spec
[
i
],
&
action_data
);
free
(
action_data
.
pathname
);
free
(
action_data
.
subpath
);
return
match
;
}
/*
* Noop specific action code
*/
static
void
noop_action
(
struct
action
*
action
,
struct
dir_ent
*
dir_ent
)
{
}
/*
* General test evaluation code
*/
/*
* A number can be of the form [range]number[size]
* [range] is either:
* '<' or '-', match on less than number
* '>' or '+', match on greater than number
* '' (nothing), match on exactly number
* [size] is either:
* '' (nothing), number
* 'k' or 'K', number * 2^10
* 'm' or 'M', number * 2^20
* 'g' or 'G', number * 2^30
*/
static
int
parse_number
(
char
*
start
,
long
long
*
size
,
int
*
range
,
char
**
error
)
{
char
*
end
;
long
long
number
;
if
(
*
start
==
'>'
||
*
start
==
'+'
)
{
*
range
=
NUM_GREATER
;
start
++
;
}
else
if
(
*
start
==
'<'
||
*
start
==
'-'
)
{
*
range
=
NUM_LESS
;
start
++
;
}
else
*
range
=
NUM_EQ
;
errno
=
0
;
/* To enable failure after call to be determined */
number
=
strtoll
(
start
,
&
end
,
10
);
if
((
errno
==
ERANGE
&&
(
number
==
LLONG_MAX
||
number
==
LLONG_MIN
))
||
(
errno
!=
0
&&
number
==
0
))
{
/* long long underflow or overflow in conversion, or other
* conversion error.
* Note: we don't check for LLONG_MIN and LLONG_MAX only
* because strtoll can validly return that if the
* user used these values
*/
*
error
=
"Long long underflow, overflow or other conversion "
"error"
;
return
0
;
}
if
(
end
==
start
)
{
/* Couldn't read any number */
*
error
=
"Number expected"
;
return
0
;
}
switch
(
end
[
0
])
{
case
'g'
:
case
'G'
:
number
*=
1024
;
case
'm'
:
case
'M'
:
number
*=
1024
;
case
'k'
:
case
'K'
:
number
*=
1024
;
if
(
end
[
1
]
!=
'\0'
)
{
*
error
=
"Trailing junk after size specifier"
;
return
0
;
}
break
;
case
'\0'
:
break
;
default:
*
error
=
"Trailing junk after number"
;
return
0
;
}
*
size
=
number
;
return
1
;
}
static
int
parse_number_arg
(
struct
test_entry
*
test
,
struct
atom
*
atom
)
{
struct
test_number_arg
*
number
;
long
long
size
;
int
range
;
char
*
error
;
int
res
=
parse_number
(
atom
->
argv
[
0
],
&
size
,
&
range
,
&
error
);
if
(
res
==
0
)
{
TEST_SYNTAX_ERROR
(
test
,
0
,
"%s
\n
"
,
error
);
return
0
;
}
number
=
malloc
(
sizeof
(
*
number
));
if
(
number
==
NULL
)
MEM_ERROR
();
number
->
range
=
range
;
number
->
size
=
size
;
atom
->
data
=
number
;
return
1
;
}
static
int
parse_range_args
(
struct
test_entry
*
test
,
struct
atom
*
atom
)
{
struct
test_range_args
*
range
;
long
long
start
,
end
;
int
type
;
int
res
;
char
*
error
;
res
=
parse_number
(
atom
->
argv
[
0
],
&
start
,
&
type
,
&
error
);
if
(
res
==
0
)
{
TEST_SYNTAX_ERROR
(
test
,
0
,
"%s
\n
"
,
error
);
return
0
;
}
if
(
type
!=
NUM_EQ
)
{
TEST_SYNTAX_ERROR
(
test
,
0
,
"Range specifier (<, >, -, +) not "
"expected
\n
"
);
return
0
;
}
res
=
parse_number
(
atom
->
argv
[
1
],
&
end
,
&
type
,
&
error
);
if
(
res
==
0
)
{
TEST_SYNTAX_ERROR
(
test
,
1
,
"%s
\n
"
,
error
);
return
0
;
}
if
(
type
!=
NUM_EQ
)
{
TEST_SYNTAX_ERROR
(
test
,
1
,
"Range specifier (<, >, -, +) not "
"expected
\n
"
);
return
0
;
}
range
=
malloc
(
sizeof
(
*
range
));
if
(
range
==
NULL
)
MEM_ERROR
();
range
->
start
=
start
;
range
->
end
=
end
;
atom
->
data
=
range
;
return
1
;
}
/*
* Generic test code macro
*/
#define TEST_FN(NAME, MATCH, CODE) \
static int NAME##_fn(struct atom *atom, struct action_data *action_data) \
{ \
/* test operates on MATCH file types only */
\
if (!file_type_match(action_data->buf->st_mode, MATCH)) \
return 0; \
\
CODE \
}
/*
* Generic test code macro testing VAR for size (eq, less than, greater than)
*/
#define TEST_VAR_FN(NAME, MATCH, VAR) TEST_FN(NAME, MATCH, \
{ \
int match = 0; \
struct test_number_arg *number = atom->data; \
\
switch (number->range) { \
case NUM_EQ: \
match = VAR == number->size; \
break; \
case NUM_LESS: \
match = VAR < number->size; \
break; \
case NUM_GREATER: \
match = VAR > number->size; \
break; \
} \
\
return match; \
})
/*
* Generic test code macro testing VAR for range [x, y] (value between x and y
* inclusive).
*/
#define TEST_VAR_RANGE_FN(NAME, MATCH, VAR) TEST_FN(NAME##_range, MATCH, \
{ \
struct test_range_args *range = atom->data; \
\
return range->start <= VAR && VAR <= range->end; \
})
/*
* Name, Pathname and Subpathname test specific code
*/
/*
* Add a leading "/" if subpathname and pathname lacks it
*/
static
int
check_pathname
(
struct
test_entry
*
test
,
struct
atom
*
atom
)
{
int
res
;
char
*
name
;
if
(
atom
->
argv
[
0
][
0
]
!=
'/'
)
{
res
=
asprintf
(
&
name
,
"/%s"
,
atom
->
argv
[
0
]);
if
(
res
==
-
1
)
BAD_ERROR
(
"asprintf failed in check_pathname
\n
"
);
free
(
atom
->
argv
[
0
]);
atom
->
argv
[
0
]
=
name
;
}
return
1
;
}
TEST_FN
(
name
,
ACTION_ALL_LNK
,
\
return
fnmatch
(
atom
->
argv
[
0
],
action_data
->
name
,
FNM_PATHNAME
|
FNM_PERIOD
|
FNM_EXTMATCH
)
==
0
;)
TEST_FN
(
pathname
,
ACTION_ALL_LNK
,
\
return
fnmatch
(
atom
->
argv
[
0
],
action_data
->
subpath
,
FNM_PATHNAME
|
FNM_PERIOD
|
FNM_EXTMATCH
)
==
0
;)
static
int
count_components
(
char
*
path
)
{
int
count
;
for
(
count
=
0
;
*
path
!=
'\0'
;
count
++
)
{
while
(
*
path
==
'/'
)
path
++
;
while
(
*
path
!=
'\0'
&&
*
path
!=
'/'
)
path
++
;
}
return
count
;
}
static
char
*
get_start
(
char
*
s
,
int
n
)
{
int
count
;
char
*
path
=
s
;
for
(
count
=
0
;
*
path
!=
'\0'
&&
count
<
n
;
count
++
)
{
while
(
*
path
==
'/'
)
path
++
;
while
(
*
path
!=
'\0'
&&
*
path
!=
'/'
)
path
++
;
}
if
(
count
==
n
)
*
path
=
'\0'
;
return
s
;
}
static
int
subpathname_fn
(
struct
atom
*
atom
,
struct
action_data
*
action_data
)
{
return
fnmatch
(
atom
->
argv
[
0
],
get_start
(
strdupa
(
action_data
->
subpath
),
count_components
(
atom
->
argv
[
0
])),
FNM_PATHNAME
|
FNM_PERIOD
|
FNM_EXTMATCH
)
==
0
;
}
/*
* Inode attribute test operations using generic
* TEST_VAR_FN(test name, file scope, attribute name) macro.
* This is for tests that do not need to be specially handled in any way.
* They just take a variable and compare it against a number.
*/
TEST_VAR_FN
(
filesize
,
ACTION_REG
,
action_data
->
buf
->
st_size
)
TEST_VAR_FN
(
dirsize
,
ACTION_DIR
,
action_data
->
buf
->
st_size
)
TEST_VAR_FN
(
size
,
ACTION_ALL_LNK
,
action_data
->
buf
->
st_size
)
TEST_VAR_FN
(
inode
,
ACTION_ALL_LNK
,
action_data
->
buf
->
st_ino
)
TEST_VAR_FN
(
nlink
,
ACTION_ALL_LNK
,
action_data
->
buf
->
st_nlink
)
TEST_VAR_FN
(
fileblocks
,
ACTION_REG
,
action_data
->
buf
->
st_blocks
)
TEST_VAR_FN
(
dirblocks
,
ACTION_DIR
,
action_data
->
buf
->
st_blocks
)
TEST_VAR_FN
(
blocks
,
ACTION_ALL_LNK
,
action_data
->
buf
->
st_blocks
)
TEST_VAR_FN
(
dircount
,
ACTION_DIR
,
action_data
->
dir_ent
->
dir
->
count
)
TEST_VAR_FN
(
depth
,
ACTION_ALL_LNK
,
action_data
->
depth
)
TEST_VAR_RANGE_FN
(
filesize
,
ACTION_REG
,
action_data
->
buf
->
st_size
)
TEST_VAR_RANGE_FN
(
dirsize
,
ACTION_DIR
,
action_data
->
buf
->
st_size
)
TEST_VAR_RANGE_FN
(
size
,
ACTION_ALL_LNK
,
action_data
->
buf
->
st_size
)
TEST_VAR_RANGE_FN
(
inode
,
ACTION_ALL_LNK
,
action_data
->
buf
->
st_ino
)
TEST_VAR_RANGE_FN
(
nlink
,
ACTION_ALL_LNK
,
action_data
->
buf
->
st_nlink
)
TEST_VAR_RANGE_FN
(
fileblocks
,
ACTION_REG
,
action_data
->
buf
->
st_blocks
)
TEST_VAR_RANGE_FN
(
dirblocks
,
ACTION_DIR
,
action_data
->
buf
->
st_blocks
)
TEST_VAR_RANGE_FN
(
blocks
,
ACTION_ALL_LNK
,
action_data
->
buf
->
st_blocks
)
TEST_VAR_RANGE_FN
(
gid
,
ACTION_ALL_LNK
,
action_data
->
buf
->
st_gid
)
TEST_VAR_RANGE_FN
(
uid
,
ACTION_ALL_LNK
,
action_data
->
buf
->
st_uid
)
TEST_VAR_RANGE_FN
(
depth
,
ACTION_ALL_LNK
,
action_data
->
depth
)
TEST_VAR_RANGE_FN
(
dircount
,
ACTION_DIR
,
action_data
->
dir_ent
->
dir
->
count
)
/*
* uid specific test code
*/
TEST_VAR_FN
(
uid
,
ACTION_ALL_LNK
,
action_data
->
buf
->
st_uid
)
static
int
parse_uid_arg
(
struct
test_entry
*
test
,
struct
atom
*
atom
)
{
struct
test_number_arg
*
number
;
long
long
size
;
int
range
;
char
*
error
;
if
(
parse_number
(
atom
->
argv
[
0
],
&
size
,
&
range
,
&
error
))
{
/* managed to fully parse argument as a number */
if
(
size
<
0
||
size
>
(((
long
long
)
1
<<
32
)
-
1
))
{
TEST_SYNTAX_ERROR
(
test
,
1
,
"Numeric uid out of "
"range
\n
"
);
return
0
;
}
}
else
{
/* couldn't parse (fully) as a number, is it a user name? */
struct
passwd
*
uid
=
getpwnam
(
atom
->
argv
[
0
]);
if
(
uid
)
{
size
=
uid
->
pw_uid
;
range
=
NUM_EQ
;
}
else
{
TEST_SYNTAX_ERROR
(
test
,
1
,
"Invalid uid or unknown "
"user
\n
"
);
return
0
;
}
}
number
=
malloc
(
sizeof
(
*
number
));
if
(
number
==
NULL
)
MEM_ERROR
();
number
->
range
=
range
;
number
->
size
=
size
;
atom
->
data
=
number
;
return
1
;
}
/*
* gid specific test code
*/
TEST_VAR_FN
(
gid
,
ACTION_ALL_LNK
,
action_data
->
buf
->
st_gid
)
static
int
parse_gid_arg
(
struct
test_entry
*
test
,
struct
atom
*
atom
)
{
struct
test_number_arg
*
number
;
long
long
size
;
int
range
;
char
*
error
;
if
(
parse_number
(
atom
->
argv
[
0
],
&
size
,
&
range
,
&
error
))
{
/* managed to fully parse argument as a number */
if
(
size
<
0
||
size
>
(((
long
long
)
1
<<
32
)
-
1
))
{
TEST_SYNTAX_ERROR
(
test
,
1
,
"Numeric gid out of "
"range
\n
"
);
return
0
;
}
}
else
{
/* couldn't parse (fully) as a number, is it a group name? */
struct
group
*
gid
=
getgrnam
(
atom
->
argv
[
0
]);
if
(
gid
)
{
size
=
gid
->
gr_gid
;
range
=
NUM_EQ
;
}
else
{
TEST_SYNTAX_ERROR
(
test
,
1
,
"Invalid gid or unknown "
"group
\n
"
);
return
0
;
}
}
number
=
malloc
(
sizeof
(
*
number
));
if
(
number
==
NULL
)
MEM_ERROR
();
number
->
range
=
range
;
number
->
size
=
size
;
atom
->
data
=
number
;
return
1
;
}
/*
* Type test specific code
*/
struct
type_entry
type_table
[]
=
{
{
S_IFSOCK
,
's'
},
{
S_IFLNK
,
'l'
},
{
S_IFREG
,
'f'
},
{
S_IFBLK
,
'b'
},
{
S_IFDIR
,
'd'
},
{
S_IFCHR
,
'c'
},
{
S_IFIFO
,
'p'
},
{
0
,
0
},
};
static
int
parse_type_arg
(
struct
test_entry
*
test
,
struct
atom
*
atom
)
{
int
i
;
if
(
strlen
(
atom
->
argv
[
0
])
!=
1
)
goto
failed
;
for
(
i
=
0
;
type_table
[
i
].
type
!=
0
;
i
++
)
if
(
type_table
[
i
].
type
==
atom
->
argv
[
0
][
0
])
break
;
atom
->
data
=
&
type_table
[
i
];
if
(
type_table
[
i
].
type
!=
0
)
return
1
;
failed:
TEST_SYNTAX_ERROR
(
test
,
0
,
"Unexpected file type, expected 'f', 'd', "
"'c', 'b', 'l', 's' or 'p'
\n
"
);
return
0
;
}
static
int
type_fn
(
struct
atom
*
atom
,
struct
action_data
*
action_data
)
{
struct
type_entry
*
type
=
atom
->
data
;
return
(
action_data
->
buf
->
st_mode
&
S_IFMT
)
==
type
->
value
;
}
/*
* True test specific code
*/
static
int
true_fn
(
struct
atom
*
atom
,
struct
action_data
*
action_data
)
{
return
1
;
}
/*
* False test specific code
*/
static
int
false_fn
(
struct
atom
*
atom
,
struct
action_data
*
action_data
)
{
return
0
;
}
/*
* File test specific code
*/
static
int
parse_file_arg
(
struct
test_entry
*
test
,
struct
atom
*
atom
)
{
int
res
;
regex_t
*
preg
=
malloc
(
sizeof
(
regex_t
));
if
(
preg
==
NULL
)
MEM_ERROR
();
res
=
regcomp
(
preg
,
atom
->
argv
[
0
],
REG_EXTENDED
);
if
(
res
)
{
char
str
[
1024
];
/* overflow safe */
regerror
(
res
,
preg
,
str
,
1024
);
free
(
preg
);
TEST_SYNTAX_ERROR
(
test
,
0
,
"invalid regex
\"
%s
\"
because "
"
\"
%s
\"\n
"
,
atom
->
argv
[
0
],
str
);
return
0
;
}
atom
->
data
=
preg
;
return
1
;
}
static
int
file_fn
(
struct
atom
*
atom
,
struct
action_data
*
action_data
)
{
int
child
,
res
,
size
=
0
,
status
;
int
pipefd
[
2
];
char
*
buffer
=
NULL
;
regex_t
*
preg
=
atom
->
data
;
res
=
pipe
(
pipefd
);
if
(
res
==
-
1
)
BAD_ERROR
(
"file_fn pipe failed
\n
"
);
child
=
fork
();
if
(
child
==
-
1
)
BAD_ERROR
(
"file_fn fork_failed
\n
"
);
if
(
child
==
0
)
{
/*
* Child process
* Connect stdout to pipefd[1] and execute file command
*/
close
(
STDOUT_FILENO
);
res
=
dup
(
pipefd
[
1
]);
if
(
res
==
-
1
)
exit
(
EXIT_FAILURE
);
execlp
(
"file"
,
"file"
,
"-b"
,
action_data
->
pathname
,
(
char
*
)
NULL
);
exit
(
EXIT_FAILURE
);
}
/*
* Parent process. Read stdout from file command
*/
close
(
pipefd
[
1
]);
do
{
buffer
=
realloc
(
buffer
,
size
+
512
);
if
(
buffer
==
NULL
)
MEM_ERROR
();
res
=
read_bytes
(
pipefd
[
0
],
buffer
+
size
,
512
);
if
(
res
==
-
1
)
BAD_ERROR
(
"file_fn pipe read error
\n
"
);
size
+=
512
;
}
while
(
res
==
512
);
size
=
size
+
res
-
512
;
buffer
[
size
]
=
'\0'
;
res
=
waitpid
(
child
,
&
status
,
0
);
if
(
res
==
-
1
)
BAD_ERROR
(
"file_fn waitpid failed
\n
"
);
if
(
!
WIFEXITED
(
status
)
||
WEXITSTATUS
(
status
)
!=
0
)
BAD_ERROR
(
"file_fn file returned error
\n
"
);
close
(
pipefd
[
0
]);
res
=
regexec
(
preg
,
buffer
,
(
size_t
)
0
,
NULL
,
0
);
free
(
buffer
);
return
res
==
0
;
}
/*
* Exec test specific code
*/
static
int
exec_fn
(
struct
atom
*
atom
,
struct
action_data
*
action_data
)
{
int
child
,
i
,
res
,
status
;
child
=
fork
();
if
(
child
==
-
1
)
BAD_ERROR
(
"exec_fn fork_failed
\n
"
);
if
(
child
==
0
)
{
/*
* Child process
* redirect stdin, stdout & stderr to /dev/null and
* execute atom->argv[0]
*/
int
fd
=
open
(
"/dev/null"
,
O_RDWR
);
if
(
fd
==
-
1
)
exit
(
EXIT_FAILURE
);
close
(
STDIN_FILENO
);
close
(
STDOUT_FILENO
);
close
(
STDERR_FILENO
);
for
(
i
=
0
;
i
<
3
;
i
++
)
{
res
=
dup
(
fd
);
if
(
res
==
-
1
)
exit
(
EXIT_FAILURE
);
}
close
(
fd
);
/*
* Create environment variables
* NAME: name of file
* PATHNAME: pathname of file relative to squashfs root
* SOURCE_PATHNAME: the pathname of the file in the source
* directory
*/
res
=
setenv
(
"NAME"
,
action_data
->
name
,
1
);
if
(
res
==
-
1
)
exit
(
EXIT_FAILURE
);
res
=
setenv
(
"PATHNAME"
,
action_data
->
subpath
,
1
);
if
(
res
==
-
1
)
exit
(
EXIT_FAILURE
);
res
=
setenv
(
"SOURCE_PATHNAME"
,
action_data
->
pathname
,
1
);
if
(
res
==
-
1
)
exit
(
EXIT_FAILURE
);
execl
(
"/bin/sh"
,
"sh"
,
"-c"
,
atom
->
argv
[
0
],
(
char
*
)
NULL
);
exit
(
EXIT_FAILURE
);
}
/*
* Parent process.
*/
res
=
waitpid
(
child
,
&
status
,
0
);
if
(
res
==
-
1
)
BAD_ERROR
(
"exec_fn waitpid failed
\n
"
);
return
WIFEXITED
(
status
)
?
WEXITSTATUS
(
status
)
==
0
:
0
;
}
/*
* Symbolic link specific test code
*/
/*
* Walk the supplied pathname and return the directory entry corresponding
* to the pathname. If any symlinks are encountered whilst walking the
* pathname, then recursively walk these, to obtain the fully
* dereferenced canonicalised directory entry.
*
* If follow_path fails to walk a pathname either because a component
* doesn't exist, it is a non directory component when a directory
* component is expected, a symlink with an absolute path is encountered,
* or a symlink is encountered which cannot be recursively walked due to
* the above failures, then return NULL.
*/
static
struct
dir_ent
*
follow_path
(
struct
dir_info
*
dir
,
char
*
pathname
)
{
char
*
comp
,
*
path
=
pathname
;
struct
dir_ent
*
dir_ent
=
NULL
;
/* We cannot follow absolute paths */
if
(
pathname
[
0
]
==
'/'
)
return
NULL
;
for
(
comp
=
get_comp
(
&
path
);
comp
;
free
(
comp
),
comp
=
get_comp
(
&
path
))
{
if
(
strcmp
(
comp
,
"."
)
==
0
)
continue
;
if
(
strcmp
(
comp
,
".."
)
==
0
)
{
/* Move to parent if we're not in the root directory */
if
(
dir
->
depth
>
1
)
{
dir
=
dir
->
dir_ent
->
our_dir
;
dir_ent
=
NULL
;
/* lazily eval at loop exit */
continue
;
}
else
/* Failed to walk pathname */
return
NULL
;
}
/* Lookup comp in current directory */
dir_ent
=
lookup_comp
(
comp
,
dir
);
if
(
dir_ent
==
NULL
)
/* Doesn't exist, failed to walk pathname */
return
NULL
;
if
((
dir_ent
->
inode
->
buf
.
st_mode
&
S_IFMT
)
==
S_IFLNK
)
{
/* Symbolic link, try to walk it */
dir_ent
=
follow_path
(
dir
,
dir_ent
->
inode
->
symlink
);
if
(
dir_ent
==
NULL
)
/* Failed to follow symlink */
return
NULL
;
}
if
((
dir_ent
->
inode
->
buf
.
st_mode
&
S_IFMT
)
!=
S_IFDIR
)
/* Cannot walk further */
break
;
dir
=
dir_ent
->
dir
;
}
/* We will have exited the loop either because we've processed
* all the components, which means we've successfully walked the
* pathname, or because we've hit a non-directory, in which case
* it's success if this is the leaf component */
if
(
comp
)
{
free
(
comp
);
comp
=
get_comp
(
&
path
);
free
(
comp
);
if
(
comp
!=
NULL
)
/* Not a leaf component */
return
NULL
;
}
else
{
/* Fully walked pathname, dir_ent contains correct value unless
* we've walked to the parent ("..") in which case we need
* to resolve it here */
if
(
!
dir_ent
)
dir_ent
=
dir
->
dir_ent
;
}
return
dir_ent
;
}
static
int
exists_fn
(
struct
atom
*
atom
,
struct
action_data
*
action_data
)
{
/*
* Test if a symlink exists within the output filesystem, that is,
* the symlink has a relative path, and the relative path refers
* to an entry within the output filesystem.
*
* This test function evaluates the path for symlinks - that is it
* follows any symlinks in the path (and any symlinks that it contains
* etc.), to discover the fully dereferenced canonicalised relative
* path.
*
* If any symlinks within the path do not exist or are absolute
* then the symlink is considered to not exist, as it cannot be
* fully dereferenced.
*
* exists operates on symlinks only, other files by definition
* exist
*/
if
(
!
file_type_match
(
action_data
->
buf
->
st_mode
,
ACTION_LNK
))
return
1
;
/* dereference the symlink, and return TRUE if it exists */
return
follow_path
(
action_data
->
dir_ent
->
our_dir
,
action_data
->
dir_ent
->
inode
->
symlink
)
?
1
:
0
;
}
static
int
absolute_fn
(
struct
atom
*
atom
,
struct
action_data
*
action_data
)
{
/*
* Test if a symlink has an absolute path, which by definition
* means the symbolic link may be broken (even if the absolute path
* does point into the filesystem being squashed, because the resultant
* filesystem can be mounted/unsquashed anywhere, it is unlikely the
* absolute path will still point to the right place). If you know that
* an absolute symlink will point to the right place then you don't need
* to use this function, and/or these symlinks can be excluded by
* use of other test operators.
*
* absolute operates on symlinks only, other files by definition
* don't have problems
*/
if
(
!
file_type_match
(
action_data
->
buf
->
st_mode
,
ACTION_LNK
))
return
0
;
return
action_data
->
dir_ent
->
inode
->
symlink
[
0
]
==
'/'
;
}
static
int
parse_expr_argX
(
struct
test_entry
*
test
,
struct
atom
*
atom
,
int
argno
)
{
/* Call parse_expr to parse argument, which should be an expression */
/* save the current parser state */
char
*
save_cur_ptr
=
cur_ptr
;
char
*
save_source
=
source
;
cur_ptr
=
source
=
atom
->
argv
[
argno
];
atom
->
data
=
parse_expr
(
0
);
cur_ptr
=
save_cur_ptr
;
source
=
save_source
;
if
(
atom
->
data
==
NULL
)
{
/* parse_expr(0) will have reported the exact syntax error,
* but, because we recursively evaluated the expression, it
* will have been reported without the context of the stat
* test(). So here additionally report our failure to parse
* the expression in the stat() test to give context */
TEST_SYNTAX_ERROR
(
test
,
0
,
"Failed to parse expression
\n
"
);
return
0
;
}
return
1
;
}
static
int
parse_expr_arg0
(
struct
test_entry
*
test
,
struct
atom
*
atom
)
{
return
parse_expr_argX
(
test
,
atom
,
0
);
}
static
int
parse_expr_arg1
(
struct
test_entry
*
test
,
struct
atom
*
atom
)
{
return
parse_expr_argX
(
test
,
atom
,
1
);
}
static
int
stat_fn
(
struct
atom
*
atom
,
struct
action_data
*
action_data
)
{
struct
stat
buf
;
struct
action_data
eval_action
;
int
match
,
res
;
/* evaluate the expression using the context of the inode
* pointed to by the symlink. This allows the inode attributes
* of the file pointed to by the symlink to be evaluated, rather
* than the symlink itself.
*
* Note, stat() deliberately does not evaluate the pathname, name or
* depth of the symlink, these are left with the symlink values.
* This allows stat() to be used on any symlink, rather than
* just symlinks which are contained (if the symlink is *not*
* contained then pathname, name and depth are meaningless as they
* are relative to the filesystem being squashed). */
/* if this isn't a symlink then stat will just return the current
* information, i.e. stat(expr) == expr. This is harmless and
* is better than returning TRUE or FALSE in a non symlink case */
res
=
stat
(
action_data
->
pathname
,
&
buf
);
if
(
res
==
-
1
)
{
if
(
expr_log_cmnd
(
LOG_ENABLED
))
{
expr_log
(
atom
->
test
->
name
);
expr_log
(
"("
);
expr_log_match
(
0
);
expr_log
(
")"
);
}
return
0
;
}
/* fill in the inode values of the file pointed to by the
* symlink, but, leave everything else the same */
memcpy
(
&
eval_action
,
action_data
,
sizeof
(
struct
action_data
));
eval_action
.
buf
=
&
buf
;
if
(
expr_log_cmnd
(
LOG_ENABLED
))
{
expr_log
(
atom
->
test
->
name
);
expr_log
(
"("
);
match
=
eval_expr_log
(
atom
->
data
,
&
eval_action
);
expr_log
(
")"
);
}
else
match
=
eval_expr
(
atom
->
data
,
&
eval_action
);
return
match
;
}
static
int
readlink_fn
(
struct
atom
*
atom
,
struct
action_data
*
action_data
)
{
int
match
=
0
;
struct
dir_ent
*
dir_ent
;
struct
action_data
eval_action
;
/* Dereference the symlink and evaluate the expression in the
* context of the file pointed to by the symlink.
* All attributes are updated to refer to the file that is pointed to.
* Thus the inode attributes, pathname, name and depth all refer to
* the dereferenced file, and not the symlink.
*
* If the symlink cannot be dereferenced because it doesn't exist in
* the output filesystem, or due to some other failure to
* walk the pathname (see follow_path above), then FALSE is returned.
*
* If you wish to evaluate the inode attributes of symlinks which
* exist in the source filestem (but not in the output filesystem then
* use stat instead (see above).
*
* readlink operates on symlinks only */
if
(
!
file_type_match
(
action_data
->
buf
->
st_mode
,
ACTION_LNK
))
goto
finish
;
/* dereference the symlink, and get the directory entry it points to */
dir_ent
=
follow_path
(
action_data
->
dir_ent
->
our_dir
,
action_data
->
dir_ent
->
inode
->
symlink
);
if
(
dir_ent
==
NULL
)
goto
finish
;
eval_action
.
name
=
dir_ent
->
name
;
eval_action
.
pathname
=
strdup
(
pathname
(
dir_ent
));
eval_action
.
subpath
=
strdup
(
subpathname
(
dir_ent
));
eval_action
.
buf
=
&
dir_ent
->
inode
->
buf
;
eval_action
.
depth
=
dir_ent
->
our_dir
->
depth
;
eval_action
.
dir_ent
=
dir_ent
;
eval_action
.
root
=
action_data
->
root
;
if
(
expr_log_cmnd
(
LOG_ENABLED
))
{
expr_log
(
atom
->
test
->
name
);
expr_log
(
"("
);
match
=
eval_expr_log
(
atom
->
data
,
&
eval_action
);
expr_log
(
")"
);
}
else
match
=
eval_expr
(
atom
->
data
,
&
eval_action
);
free
(
eval_action
.
pathname
);
free
(
eval_action
.
subpath
);
return
match
;
finish:
if
(
expr_log_cmnd
(
LOG_ENABLED
))
{
expr_log
(
atom
->
test
->
name
);
expr_log
(
"("
);
expr_log_match
(
0
);
expr_log
(
")"
);
}
return
0
;
}
static
int
eval_fn
(
struct
atom
*
atom
,
struct
action_data
*
action_data
)
{
int
match
;
char
*
path
=
atom
->
argv
[
0
];
struct
dir_ent
*
dir_ent
=
action_data
->
dir_ent
;
struct
stat
*
buf
=
action_data
->
buf
;
struct
action_data
eval_action
;
/* Follow path (arg1) and evaluate the expression (arg2)
* in the context of the file discovered. All attributes are updated
* to refer to the file that is pointed to.
*
* This test operation allows you to add additional context to the
* evaluation of the file being scanned, such as "if current file is
* XXX and the parent is YYY, then ..." Often times you need or
* want to test a combination of file status
*
* If the file referenced by the path does not exist in
* the output filesystem, or some other failure is experienced in
* walking the path (see follow_path above), then FALSE is returned.
*
* If you wish to evaluate the inode attributes of files which
* exist in the source filestem (but not in the output filesystem then
* use stat instead (see above). */
/* try to follow path, and get the directory entry it points to */
if
(
path
[
0
]
==
'/'
)
{
/* absolute, walk from root - first skip the leading / */
while
(
path
[
0
]
==
'/'
)
path
++
;
if
(
path
[
0
]
==
'\0'
)
dir_ent
=
action_data
->
root
->
dir_ent
;
else
dir_ent
=
follow_path
(
action_data
->
root
,
path
);
}
else
{
/* relative, if first component is ".." walk from parent,
* otherwise walk from dir_ent.
* Note: this has to be handled here because follow_path
* will quite correctly refuse to execute ".." on anything
* which isn't a directory */
if
(
strncmp
(
path
,
".."
,
2
)
==
0
&&
(
path
[
2
]
==
'\0'
||
path
[
2
]
==
'/'
))
{
/* walk from parent */
path
+=
2
;
while
(
path
[
0
]
==
'/'
)
path
++
;
if
(
path
[
0
]
==
'\0'
)
dir_ent
=
dir_ent
->
our_dir
->
dir_ent
;
else
dir_ent
=
follow_path
(
dir_ent
->
our_dir
,
path
);
}
else
if
(
!
file_type_match
(
buf
->
st_mode
,
ACTION_DIR
))
dir_ent
=
NULL
;
else
dir_ent
=
follow_path
(
dir_ent
->
dir
,
path
);
}
if
(
dir_ent
==
NULL
)
{
if
(
expr_log_cmnd
(
LOG_ENABLED
))
{
expr_log
(
atom
->
test
->
name
);
expr_log
(
"("
);
expr_log
(
atom
->
argv
[
0
]);
expr_log
(
","
);
expr_log_match
(
0
);
expr_log
(
")"
);
}
return
0
;
}
eval_action
.
name
=
dir_ent
->
name
;
eval_action
.
pathname
=
strdup
(
pathname
(
dir_ent
));
eval_action
.
subpath
=
strdup
(
subpathname
(
dir_ent
));
eval_action
.
buf
=
&
dir_ent
->
inode
->
buf
;
eval_action
.
depth
=
dir_ent
->
our_dir
->
depth
;
eval_action
.
dir_ent
=
dir_ent
;
eval_action
.
root
=
action_data
->
root
;
if
(
expr_log_cmnd
(
LOG_ENABLED
))
{
expr_log
(
atom
->
test
->
name
);
expr_log
(
"("
);
expr_log
(
eval_action
.
subpath
);
expr_log
(
","
);
match
=
eval_expr_log
(
atom
->
data
,
&
eval_action
);
expr_log
(
")"
);
}
else
match
=
eval_expr
(
atom
->
data
,
&
eval_action
);
free
(
eval_action
.
pathname
);
free
(
eval_action
.
subpath
);
return
match
;
}
/*
* Perm specific test code
*/
static
int
parse_perm_args
(
struct
test_entry
*
test
,
struct
atom
*
atom
)
{
int
res
=
1
,
mode
,
op
,
i
;
char
*
arg
;
struct
mode_data
*
head
=
NULL
,
*
cur
=
NULL
;
struct
perm_data
*
perm_data
;
if
(
atom
->
args
==
0
)
{
TEST_SYNTAX_ERROR
(
test
,
0
,
"One or more arguments expected
\n
"
);
return
0
;
}
switch
(
atom
->
argv
[
0
][
0
])
{
case
'-'
:
op
=
PERM_ALL
;
arg
=
atom
->
argv
[
0
]
+
1
;
break
;
case
'/'
:
op
=
PERM_ANY
;
arg
=
atom
->
argv
[
0
]
+
1
;
break
;
default:
op
=
PERM_EXACT
;
arg
=
atom
->
argv
[
0
];
break
;
}
/* try to parse as an octal number */
res
=
parse_octal_mode_args
(
atom
->
args
,
atom
->
argv
,
(
void
**
)
&
head
);
if
(
res
==
-
1
)
{
/* parse as sym mode argument */
for
(
i
=
0
;
i
<
atom
->
args
&&
res
;
i
++
,
arg
=
atom
->
argv
[
i
])
res
=
parse_sym_mode_arg
(
arg
,
&
head
,
&
cur
);
}
if
(
res
==
0
)
goto
finish
;
/*
* Evaluate the symbolic mode against a permission of 0000 octal
*/
mode
=
mode_execute
(
head
,
0
);
perm_data
=
malloc
(
sizeof
(
struct
perm_data
));
if
(
perm_data
==
NULL
)
MEM_ERROR
();
perm_data
->
op
=
op
;
perm_data
->
mode
=
mode
;
atom
->
data
=
perm_data
;
finish:
while
(
head
)
{
struct
mode_data
*
tmp
=
head
;
head
=
head
->
next
;
free
(
tmp
);
}
return
res
;
}
static
int
perm_fn
(
struct
atom
*
atom
,
struct
action_data
*
action_data
)
{
struct
perm_data
*
perm_data
=
atom
->
data
;
struct
stat
*
buf
=
action_data
->
buf
;
switch
(
perm_data
->
op
)
{
case
PERM_EXACT
:
return
(
buf
->
st_mode
&
~
S_IFMT
)
==
perm_data
->
mode
;
case
PERM_ALL
:
return
(
buf
->
st_mode
&
perm_data
->
mode
)
==
perm_data
->
mode
;
case
PERM_ANY
:
default:
/*
* if no permission bits are set in perm_data->mode match
* on any file, this is to be consistent with find, which
* does this to be consistent with the behaviour of
* -perm -000
*/
return
perm_data
->
mode
==
0
||
(
buf
->
st_mode
&
perm_data
->
mode
);
}
}
#ifdef SQUASHFS_TRACE
static
void
dump_parse_tree
(
struct
expr
*
expr
)
{
int
i
;
if
(
expr
->
type
==
ATOM_TYPE
)
{
printf
(
"%s"
,
expr
->
atom
.
test
->
name
);
if
(
expr
->
atom
.
args
)
{
printf
(
"("
);
for
(
i
=
0
;
i
<
expr
->
atom
.
args
;
i
++
)
{
printf
(
"%s"
,
expr
->
atom
.
argv
[
i
]);
if
(
i
+
1
<
expr
->
atom
.
args
)
printf
(
","
);
}
printf
(
")"
);
}
}
else
if
(
expr
->
type
==
UNARY_TYPE
)
{
printf
(
"%s"
,
token_table
[
expr
->
unary_op
.
op
].
string
);
dump_parse_tree
(
expr
->
unary_op
.
expr
);
}
else
{
printf
(
"("
);
dump_parse_tree
(
expr
->
expr_op
.
lhs
);
printf
(
"%s"
,
token_table
[
expr
->
expr_op
.
op
].
string
);
dump_parse_tree
(
expr
->
expr_op
.
rhs
);
printf
(
")"
);
}
}
void
dump_action_list
(
struct
action
*
spec_list
,
int
spec_count
)
{
int
i
;
for
(
i
=
0
;
i
<
spec_count
;
i
++
)
{
printf
(
"%s"
,
spec_list
[
i
].
action
->
name
);
if
(
spec_list
[
i
].
args
)
{
int
n
;
printf
(
"("
);
for
(
n
=
0
;
n
<
spec_list
[
i
].
args
;
n
++
)
{
printf
(
"%s"
,
spec_list
[
i
].
argv
[
n
]);
if
(
n
+
1
<
spec_list
[
i
].
args
)
printf
(
","
);
}
printf
(
")"
);
}
printf
(
"="
);
dump_parse_tree
(
spec_list
[
i
].
expr
);
printf
(
"
\n
"
);
}
}
void
dump_actions
()
{
dump_action_list
(
exclude_spec
,
exclude_count
);
dump_action_list
(
fragment_spec
,
fragment_count
);
dump_action_list
(
other_spec
,
other_count
);
dump_action_list
(
move_spec
,
move_count
);
dump_action_list
(
empty_spec
,
empty_count
);
}
#else
void
dump_actions
()
{
}
#endif
static
struct
test_entry
test_table
[]
=
{
{
"name"
,
1
,
name_fn
,
NULL
,
1
},
{
"pathname"
,
1
,
pathname_fn
,
check_pathname
,
1
,
0
},
{
"subpathname"
,
1
,
subpathname_fn
,
check_pathname
,
1
,
0
},
{
"filesize"
,
1
,
filesize_fn
,
parse_number_arg
,
1
,
0
},
{
"dirsize"
,
1
,
dirsize_fn
,
parse_number_arg
,
1
,
0
},
{
"size"
,
1
,
size_fn
,
parse_number_arg
,
1
,
0
},
{
"inode"
,
1
,
inode_fn
,
parse_number_arg
,
1
,
0
},
{
"nlink"
,
1
,
nlink_fn
,
parse_number_arg
,
1
,
0
},
{
"fileblocks"
,
1
,
fileblocks_fn
,
parse_number_arg
,
1
,
0
},
{
"dirblocks"
,
1
,
dirblocks_fn
,
parse_number_arg
,
1
,
0
},
{
"blocks"
,
1
,
blocks_fn
,
parse_number_arg
,
1
,
0
},
{
"gid"
,
1
,
gid_fn
,
parse_gid_arg
,
1
,
0
},
{
"uid"
,
1
,
uid_fn
,
parse_uid_arg
,
1
,
0
},
{
"depth"
,
1
,
depth_fn
,
parse_number_arg
,
1
,
0
},
{
"dircount"
,
1
,
dircount_fn
,
parse_number_arg
,
0
,
0
},
{
"filesize_range"
,
2
,
filesize_range_fn
,
parse_range_args
,
1
,
0
},
{
"dirsize_range"
,
2
,
dirsize_range_fn
,
parse_range_args
,
1
,
0
},
{
"size_range"
,
2
,
size_range_fn
,
parse_range_args
,
1
,
0
},
{
"inode_range"
,
2
,
inode_range_fn
,
parse_range_args
,
1
,
0
},
{
"nlink_range"
,
2
,
nlink_range_fn
,
parse_range_args
,
1
,
0
},
{
"fileblocks_range"
,
2
,
fileblocks_range_fn
,
parse_range_args
,
1
,
0
},
{
"dirblocks_range"
,
2
,
dirblocks_range_fn
,
parse_range_args
,
1
,
0
},
{
"blocks_range"
,
2
,
blocks_range_fn
,
parse_range_args
,
1
,
0
},
{
"gid_range"
,
2
,
gid_range_fn
,
parse_range_args
,
1
,
0
},
{
"uid_range"
,
2
,
uid_range_fn
,
parse_range_args
,
1
,
0
},
{
"depth_range"
,
2
,
depth_range_fn
,
parse_range_args
,
1
,
0
},
{
"dircount_range"
,
2
,
dircount_range_fn
,
parse_range_args
,
0
,
0
},
{
"type"
,
1
,
type_fn
,
parse_type_arg
,
1
,
0
},
{
"true"
,
0
,
true_fn
,
NULL
,
1
,
0
},
{
"false"
,
0
,
false_fn
,
NULL
,
1
,
0
},
{
"file"
,
1
,
file_fn
,
parse_file_arg
,
1
,
0
},
{
"exec"
,
1
,
exec_fn
,
NULL
,
1
,
0
},
{
"exists"
,
0
,
exists_fn
,
NULL
,
0
,
0
},
{
"absolute"
,
0
,
absolute_fn
,
NULL
,
0
,
0
},
{
"stat"
,
1
,
stat_fn
,
parse_expr_arg0
,
1
,
1
},
{
"readlink"
,
1
,
readlink_fn
,
parse_expr_arg0
,
0
,
1
},
{
"eval"
,
2
,
eval_fn
,
parse_expr_arg1
,
0
,
1
},
{
"perm"
,
-
2
,
perm_fn
,
parse_perm_args
,
1
,
0
},
{
""
,
-
1
}
};
static
struct
action_entry
action_table
[]
=
{
{
"fragment"
,
FRAGMENT_ACTION
,
1
,
ACTION_REG
,
NULL
,
NULL
},
{
"exclude"
,
EXCLUDE_ACTION
,
0
,
ACTION_ALL_LNK
,
NULL
,
NULL
},
{
"fragments"
,
FRAGMENTS_ACTION
,
0
,
ACTION_REG
,
NULL
,
frag_action
},
{
"no-fragments"
,
NO_FRAGMENTS_ACTION
,
0
,
ACTION_REG
,
NULL
,
no_frag_action
},
{
"always-use-fragments"
,
ALWAYS_FRAGS_ACTION
,
0
,
ACTION_REG
,
NULL
,
always_frag_action
},
{
"dont-always-use-fragments"
,
NO_ALWAYS_FRAGS_ACTION
,
0
,
ACTION_REG
,
NULL
,
no_always_frag_action
},
{
"compressed"
,
COMPRESSED_ACTION
,
0
,
ACTION_REG
,
NULL
,
comp_action
},
{
"uncompressed"
,
UNCOMPRESSED_ACTION
,
0
,
ACTION_REG
,
NULL
,
uncomp_action
},
{
"uid"
,
UID_ACTION
,
1
,
ACTION_ALL_LNK
,
parse_uid_args
,
uid_action
},
{
"gid"
,
GID_ACTION
,
1
,
ACTION_ALL_LNK
,
parse_gid_args
,
gid_action
},
{
"guid"
,
GUID_ACTION
,
2
,
ACTION_ALL_LNK
,
parse_guid_args
,
guid_action
},
{
"mode"
,
MODE_ACTION
,
-
2
,
ACTION_ALL
,
parse_mode_args
,
mode_action
},
{
"empty"
,
EMPTY_ACTION
,
-
2
,
ACTION_DIR
,
parse_empty_args
,
NULL
},
{
"move"
,
MOVE_ACTION
,
1
,
ACTION_ALL_LNK
,
NULL
,
NULL
},
{
"prune"
,
PRUNE_ACTION
,
0
,
ACTION_ALL_LNK
,
NULL
,
NULL
},
{
"chmod"
,
MODE_ACTION
,
-
2
,
ACTION_ALL
,
parse_mode_args
,
mode_action
},
{
"noop"
,
NOOP_ACTION
,
0
,
ACTION_ALL
,
NULL
,
noop_action
},
{
""
,
0
,
-
1
,
0
,
NULL
,
NULL
}
};
SQUASHFS/squashfs-tools-4.4/squashfs-tools/action.h
0 → 100644
View file @
05a1b863
#ifndef ACTION_H
#define ACTION_H
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2011, 2012, 2013, 2014
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* action.h
*/
/*
* Lexical analyser definitions
*/
#define TOK_OPEN_BRACKET 0
#define TOK_CLOSE_BRACKET 1
#define TOK_AND 2
#define TOK_OR 3
#define TOK_NOT 4
#define TOK_COMMA 5
#define TOK_AT 6
#define TOK_WHITE_SPACE 7
#define TOK_STRING 8
#define TOK_EOF 9
#define TOK_TO_STR(OP, S) ({ \
char *s; \
switch(OP) { \
case TOK_EOF: \
s = "EOF"; \
break; \
case TOK_STRING: \
s = S; \
break; \
default: \
s = token_table[OP].string; \
break; \
} \
s; \
})
struct
token_entry
{
char
*
string
;
int
token
;
int
size
;
};
/*
* Expression parser definitions
*/
#define OP_TYPE 0
#define ATOM_TYPE 1
#define UNARY_TYPE 2
#define SYNTAX_ERROR(S, ARGS...) { \
char *src = strdup(source); \
src[cur_ptr - source] = '\0'; \
fprintf(stderr, "Failed to parse action \"%s\"\n", source); \
fprintf(stderr, "Syntax error: "S, ##ARGS); \
fprintf(stderr, "Got here \"%s\"\n", src); \
free(src); \
}
#define TEST_SYNTAX_ERROR(TEST, ARG, S, ARGS...) { \
char *src = strdup(source); \
src[cur_ptr - source] = '\0'; \
fprintf(stderr, "Failed to parse action \"%s\"\n", source); \
fprintf(stderr, "Syntax error in \"%s()\", arg %d: "S, TEST->name, \
ARG, ##ARGS); \
fprintf(stderr, "Got here \"%s\"\n", src); \
free(src); \
}
struct
expr
;
struct
expr_op
{
struct
expr
*
lhs
;
struct
expr
*
rhs
;
int
op
;
};
struct
atom
{
struct
test_entry
*
test
;
int
args
;
char
**
argv
;
void
*
data
;
};
struct
unary_op
{
struct
expr
*
expr
;
int
op
;
};
struct
expr
{
int
type
;
union
{
struct
atom
atom
;
struct
expr_op
expr_op
;
struct
unary_op
unary_op
;
};
};
/*
* Test operation definitions
*/
#define NUM_EQ 1
#define NUM_LESS 2
#define NUM_GREATER 3
struct
test_number_arg
{
long
long
size
;
int
range
;
};
struct
test_range_args
{
long
long
start
;
long
long
end
;
};
struct
action
;
struct
action_data
;
struct
test_entry
{
char
*
name
;
int
args
;
int
(
*
fn
)(
struct
atom
*
,
struct
action_data
*
);
int
(
*
parse_args
)(
struct
test_entry
*
,
struct
atom
*
);
int
exclude_ok
;
int
handle_logging
;
};
/*
* Type test specific definitions
*/
struct
type_entry
{
int
value
;
char
type
;
};
/*
* Action definitions
*/
#define FRAGMENT_ACTION 0
#define EXCLUDE_ACTION 1
#define FRAGMENTS_ACTION 2
#define NO_FRAGMENTS_ACTION 3
#define ALWAYS_FRAGS_ACTION 4
#define NO_ALWAYS_FRAGS_ACTION 5
#define COMPRESSED_ACTION 6
#define UNCOMPRESSED_ACTION 7
#define UID_ACTION 8
#define GID_ACTION 9
#define GUID_ACTION 10
#define MODE_ACTION 11
#define EMPTY_ACTION 12
#define MOVE_ACTION 13
#define PRUNE_ACTION 14
#define NOOP_ACTION 15
/*
* Define what file types each action operates over
*/
#define ACTION_DIR 1
#define ACTION_REG 2
#define ACTION_ALL_LNK 3
#define ACTION_ALL 4
#define ACTION_LNK 5
/*
* Action logging requested, specified by the various
* -action, -true-action, -false-action and -verbose-action
* options
*/
#define ACTION_LOG_NONE 0
#define ACTION_LOG_TRUE 1
#define ACTION_LOG_FALSE 2
#define ACTION_LOG_VERBOSE ACTION_LOG_TRUE | ACTION_LOG_FALSE
struct
action_entry
{
char
*
name
;
int
type
;
int
args
;
int
file_types
;
int
(
*
parse_args
)(
struct
action_entry
*
,
int
,
char
**
,
void
**
);
void
(
*
run_action
)(
struct
action
*
,
struct
dir_ent
*
);
};
struct
action_data
{
int
depth
;
char
*
name
;
char
*
pathname
;
char
*
subpath
;
struct
stat
*
buf
;
struct
dir_ent
*
dir_ent
;
struct
dir_info
*
root
;
};
struct
action
{
int
type
;
struct
action_entry
*
action
;
int
args
;
char
**
argv
;
struct
expr
*
expr
;
void
*
data
;
int
verbose
;
};
/*
* Uid/gid action specific definitions
*/
struct
uid_info
{
uid_t
uid
;
};
struct
gid_info
{
gid_t
gid
;
};
struct
guid_info
{
uid_t
uid
;
gid_t
gid
;
};
/*
* Mode action specific definitions
*/
#define ACTION_MODE_SET 0
#define ACTION_MODE_ADD 1
#define ACTION_MODE_REM 2
#define ACTION_MODE_OCT 3
struct
mode_data
{
struct
mode_data
*
next
;
int
operation
;
int
mode
;
unsigned
int
mask
;
char
X
;
};
/*
* Empty action specific definitions
*/
#define EMPTY_ALL 0
#define EMPTY_SOURCE 1
#define EMPTY_EXCLUDED 2
struct
empty_data
{
int
val
;
};
/*
* Move action specific definitions
*/
#define ACTION_MOVE_RENAME 1
#define ACTION_MOVE_MOVE 2
struct
move_ent
{
int
ops
;
struct
dir_ent
*
dir_ent
;
char
*
name
;
struct
dir_info
*
dest
;
struct
move_ent
*
next
;
};
/*
* Perm test function specific definitions
*/
#define PERM_ALL 1
#define PERM_ANY 2
#define PERM_EXACT 3
struct
perm_data
{
int
op
;
int
mode
;
};
/*
* External function definitions
*/
extern
int
parse_action
(
char
*
,
int
verbose
);
extern
void
dump_actions
();
extern
void
*
eval_frag_actions
(
struct
dir_info
*
,
struct
dir_ent
*
);
extern
void
*
get_frag_action
(
void
*
);
extern
int
eval_exclude_actions
(
char
*
,
char
*
,
char
*
,
struct
stat
*
,
int
,
struct
dir_ent
*
);
extern
void
eval_actions
(
struct
dir_info
*
,
struct
dir_ent
*
);
extern
int
eval_empty_actions
(
struct
dir_info
*
,
struct
dir_ent
*
dir_ent
);
extern
void
eval_move_actions
(
struct
dir_info
*
,
struct
dir_ent
*
);
extern
int
eval_prune_actions
(
struct
dir_info
*
,
struct
dir_ent
*
);
extern
void
do_move_actions
();
extern
int
read_bytes
(
int
,
void
*
,
int
);
extern
int
actions
();
extern
int
move_actions
();
extern
int
empty_actions
();
extern
int
read_action_file
(
char
*
,
int
);
extern
int
exclude_actions
();
extern
int
prune_actions
();
#endif
SQUASHFS/squashfs-tools-4.4/squashfs-tools/build.sh
0 → 100644
View file @
05a1b863
#!/bin/bash
export
LZMA_LIBDIR
=
$PWD
/../../LIB/LZMA
export
LZ4_LIBDIR
=
$PWD
/../../LIB/LZ4
export
ZSTD_LIBDIR
=
$PWD
/../../LIB/ZSTD
export
LZO_LIBDIR
=
$PWD
/../../LIB/LZO
if
[
-e
/lib64/libz.a
]
;
then
export
VTZLIB
=
/lib64/libz.a
elif
[
-e
/lib/libz.a
]
;
then
export
VTZLIB
=
/lib/libz.a
elif
[
-e
/usr/lib/libz.a
]
;
then
export
VTZLIB
=
/usr/lib/libz.a
fi
rm
-f
unsquashfs
make clean
make
-e
unsquashfs
if
[
-e
unsquashfs
]
;
then
strip
--strip-all
unsquashfs
echo
-e
"
\n
========== SUCCESS ============
\n
"
else
echo
-e
"
\n
========== FAILED ============
\n
"
fi
if
uname
-a
| egrep
-q
'x86_64|amd64'
;
then
name
=
unsquashfs_64
else
name
=
unsquashfs_32
fi
rm
-f
../../
$name
cp
-a
unsquashfs ../../
$name
SQUASHFS/squashfs-tools-4.4/squashfs-tools/caches-queues-lists.c
0 → 100644
View file @
05a1b863
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2013, 2014, 2019
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* caches-queues-lists.c
*/
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "error.h"
#include "caches-queues-lists.h"
extern
int
add_overflow
(
int
,
int
);
extern
int
multiply_overflow
(
int
,
int
);
#define TRUE 1
#define FALSE 0
struct
queue
*
queue_init
(
int
size
)
{
struct
queue
*
queue
=
malloc
(
sizeof
(
struct
queue
));
if
(
queue
==
NULL
)
MEM_ERROR
();
if
(
add_overflow
(
size
,
1
)
||
multiply_overflow
(
size
+
1
,
sizeof
(
void
*
)))
BAD_ERROR
(
"Size too large in queue_init
\n
"
);
queue
->
data
=
malloc
(
sizeof
(
void
*
)
*
(
size
+
1
));
if
(
queue
->
data
==
NULL
)
MEM_ERROR
();
queue
->
size
=
size
+
1
;
queue
->
readp
=
queue
->
writep
=
0
;
pthread_mutex_init
(
&
queue
->
mutex
,
NULL
);
pthread_cond_init
(
&
queue
->
empty
,
NULL
);
pthread_cond_init
(
&
queue
->
full
,
NULL
);
return
queue
;
}
void
queue_put
(
struct
queue
*
queue
,
void
*
data
)
{
int
nextp
;
pthread_cleanup_push
((
void
*
)
pthread_mutex_unlock
,
&
queue
->
mutex
);
pthread_mutex_lock
(
&
queue
->
mutex
);
while
((
nextp
=
(
queue
->
writep
+
1
)
%
queue
->
size
)
==
queue
->
readp
)
pthread_cond_wait
(
&
queue
->
full
,
&
queue
->
mutex
);
queue
->
data
[
queue
->
writep
]
=
data
;
queue
->
writep
=
nextp
;
pthread_cond_signal
(
&
queue
->
empty
);
pthread_cleanup_pop
(
1
);
}
void
*
queue_get
(
struct
queue
*
queue
)
{
void
*
data
;
pthread_cleanup_push
((
void
*
)
pthread_mutex_unlock
,
&
queue
->
mutex
);
pthread_mutex_lock
(
&
queue
->
mutex
);
while
(
queue
->
readp
==
queue
->
writep
)
pthread_cond_wait
(
&
queue
->
empty
,
&
queue
->
mutex
);
data
=
queue
->
data
[
queue
->
readp
];
queue
->
readp
=
(
queue
->
readp
+
1
)
%
queue
->
size
;
pthread_cond_signal
(
&
queue
->
full
);
pthread_cleanup_pop
(
1
);
return
data
;
}
int
queue_empty
(
struct
queue
*
queue
)
{
int
empty
;
pthread_cleanup_push
((
void
*
)
pthread_mutex_unlock
,
&
queue
->
mutex
);
pthread_mutex_lock
(
&
queue
->
mutex
);
empty
=
queue
->
readp
==
queue
->
writep
;
pthread_cleanup_pop
(
1
);
return
empty
;
}
void
queue_flush
(
struct
queue
*
queue
)
{
pthread_cleanup_push
((
void
*
)
pthread_mutex_unlock
,
&
queue
->
mutex
);
pthread_mutex_lock
(
&
queue
->
mutex
);
queue
->
readp
=
queue
->
writep
;
pthread_cleanup_pop
(
1
);
}
void
dump_queue
(
struct
queue
*
queue
)
{
pthread_cleanup_push
((
void
*
)
pthread_mutex_unlock
,
&
queue
->
mutex
);
pthread_mutex_lock
(
&
queue
->
mutex
);
printf
(
"
\t
Max size %d, size %d%s
\n
"
,
queue
->
size
-
1
,
queue
->
readp
<=
queue
->
writep
?
queue
->
writep
-
queue
->
readp
:
queue
->
size
-
queue
->
readp
+
queue
->
writep
,
queue
->
readp
==
queue
->
writep
?
" (EMPTY)"
:
((
queue
->
writep
+
1
)
%
queue
->
size
)
==
queue
->
readp
?
" (FULL)"
:
""
);
pthread_cleanup_pop
(
1
);
}
/* define seq queue hash tables */
#define CALCULATE_SEQ_HASH(N) CALCULATE_HASH(N)
/* Called with the seq queue mutex held */
INSERT_HASH_TABLE
(
seq
,
struct
seq_queue
,
CALCULATE_SEQ_HASH
,
sequence
,
seq
)
/* Called with the cache mutex held */
REMOVE_HASH_TABLE
(
seq
,
struct
seq_queue
,
CALCULATE_SEQ_HASH
,
sequence
,
seq
);
struct
seq_queue
*
seq_queue_init
()
{
struct
seq_queue
*
queue
=
malloc
(
sizeof
(
struct
seq_queue
));
if
(
queue
==
NULL
)
MEM_ERROR
();
memset
(
queue
,
0
,
sizeof
(
struct
seq_queue
));
pthread_mutex_init
(
&
queue
->
mutex
,
NULL
);
pthread_cond_init
(
&
queue
->
wait
,
NULL
);
return
queue
;
}
void
seq_queue_put
(
struct
seq_queue
*
queue
,
struct
file_buffer
*
entry
)
{
pthread_cleanup_push
((
void
*
)
pthread_mutex_unlock
,
&
queue
->
mutex
);
pthread_mutex_lock
(
&
queue
->
mutex
);
insert_seq_hash_table
(
queue
,
entry
);
if
(
entry
->
fragment
)
queue
->
fragment_count
++
;
else
queue
->
block_count
++
;
if
(
entry
->
sequence
==
queue
->
sequence
)
pthread_cond_signal
(
&
queue
->
wait
);
pthread_cleanup_pop
(
1
);
}
struct
file_buffer
*
seq_queue_get
(
struct
seq_queue
*
queue
)
{
/*
* Return next buffer from queue in sequence order (queue->sequence). If
* found return it, otherwise wait for it to arrive.
*/
int
hash
=
CALCULATE_SEQ_HASH
(
queue
->
sequence
);
struct
file_buffer
*
entry
;
pthread_cleanup_push
((
void
*
)
pthread_mutex_unlock
,
&
queue
->
mutex
);
pthread_mutex_lock
(
&
queue
->
mutex
);
while
(
1
)
{
for
(
entry
=
queue
->
hash_table
[
hash
];
entry
;
entry
=
entry
->
seq_next
)
if
(
entry
->
sequence
==
queue
->
sequence
)
break
;
if
(
entry
)
{
/*
* found the buffer in the queue, decrement the
* appropriate count, and remove from hash list
*/
if
(
entry
->
fragment
)
queue
->
fragment_count
--
;
else
queue
->
block_count
--
;
remove_seq_hash_table
(
queue
,
entry
);
queue
->
sequence
++
;
break
;
}
/* entry not found, wait for it to arrive */
pthread_cond_wait
(
&
queue
->
wait
,
&
queue
->
mutex
);
}
pthread_cleanup_pop
(
1
);
return
entry
;
}
void
seq_queue_flush
(
struct
seq_queue
*
queue
)
{
int
i
;
pthread_cleanup_push
((
void
*
)
pthread_mutex_unlock
,
&
queue
->
mutex
);
pthread_mutex_lock
(
&
queue
->
mutex
);
for
(
i
=
0
;
i
<
HASH_SIZE
;
i
++
)
queue
->
hash_table
[
i
]
=
NULL
;
queue
->
fragment_count
=
queue
->
block_count
=
0
;
pthread_cleanup_pop
(
1
);
}
void
dump_seq_queue
(
struct
seq_queue
*
queue
,
int
fragment_queue
)
{
int
size
;
pthread_cleanup_push
((
void
*
)
pthread_mutex_unlock
,
&
queue
->
mutex
);
pthread_mutex_lock
(
&
queue
->
mutex
);
size
=
fragment_queue
?
queue
->
fragment_count
:
queue
->
block_count
;
printf
(
"
\t
Max size unlimited, size %d%s
\n
"
,
size
,
size
==
0
?
" (EMPTY)"
:
""
);
pthread_cleanup_pop
(
1
);
}
/* define cache hash tables */
#define CALCULATE_CACHE_HASH(N) CALCULATE_HASH(llabs(N))
/* Called with the cache mutex held */
INSERT_HASH_TABLE
(
cache
,
struct
cache
,
CALCULATE_CACHE_HASH
,
index
,
hash
)
/* Called with the cache mutex held */
REMOVE_HASH_TABLE
(
cache
,
struct
cache
,
CALCULATE_CACHE_HASH
,
index
,
hash
);
/* define cache free list */
/* Called with the cache mutex held */
INSERT_LIST
(
free
,
struct
file_buffer
)
/* Called with the cache mutex held */
REMOVE_LIST
(
free
,
struct
file_buffer
)
struct
cache
*
cache_init
(
int
buffer_size
,
int
max_buffers
,
int
noshrink_lookup
,
int
first_freelist
)
{
struct
cache
*
cache
=
malloc
(
sizeof
(
struct
cache
));
if
(
cache
==
NULL
)
MEM_ERROR
();
cache
->
max_buffers
=
max_buffers
;
cache
->
buffer_size
=
buffer_size
;
cache
->
count
=
0
;
cache
->
used
=
0
;
cache
->
free_list
=
NULL
;
/*
* The cache will grow up to max_buffers in size in response to
* an increase in readhead/number of buffers in flight. But
* once the outstanding buffers gets returned, we can either elect
* to shrink the cache, or to put the freed blocks onto a free list.
*
* For the caches where we want to do lookup (fragment/writer),
* a don't shrink policy is best, for the reader cache it
* makes no sense to keep buffers around longer than necessary as
* we don't do any lookup on those blocks.
*/
cache
->
noshrink_lookup
=
noshrink_lookup
;
/*
* The default use freelist before growing cache policy behaves
* poorly with appending - with many duplicates the caches
* do not grow due to the fact that large queues of outstanding
* fragments/writer blocks do not occur, leading to small caches
* and un-uncessary performance loss to frequent cache
* replacement in the small caches. Therefore with appending
* change the policy to grow the caches before reusing blocks
* from the freelist
*/
cache
->
first_freelist
=
first_freelist
;
memset
(
cache
->
hash_table
,
0
,
sizeof
(
struct
file_buffer
*
)
*
65536
);
pthread_mutex_init
(
&
cache
->
mutex
,
NULL
);
pthread_cond_init
(
&
cache
->
wait_for_free
,
NULL
);
pthread_cond_init
(
&
cache
->
wait_for_unlock
,
NULL
);
return
cache
;
}
struct
file_buffer
*
cache_lookup
(
struct
cache
*
cache
,
long
long
index
)
{
/* Lookup block in the cache, if found return with usage count
* incremented, if not found return NULL */
int
hash
=
CALCULATE_CACHE_HASH
(
index
);
struct
file_buffer
*
entry
;
pthread_cleanup_push
((
void
*
)
pthread_mutex_unlock
,
&
cache
->
mutex
);
pthread_mutex_lock
(
&
cache
->
mutex
);
for
(
entry
=
cache
->
hash_table
[
hash
];
entry
;
entry
=
entry
->
hash_next
)
if
(
entry
->
index
==
index
)
break
;
if
(
entry
)
{
/* found the block in the cache, increment used count and
* if necessary remove from free list so it won't disappear
*/
if
(
entry
->
used
==
0
)
{
remove_free_list
(
&
cache
->
free_list
,
entry
);
cache
->
used
++
;
}
entry
->
used
++
;
}
pthread_cleanup_pop
(
1
);
return
entry
;
}
static
struct
file_buffer
*
cache_freelist
(
struct
cache
*
cache
)
{
struct
file_buffer
*
entry
=
cache
->
free_list
;
remove_free_list
(
&
cache
->
free_list
,
entry
);
/* a block on the free_list is hashed */
remove_cache_hash_table
(
cache
,
entry
);
cache
->
used
++
;
return
entry
;
}
static
struct
file_buffer
*
cache_alloc
(
struct
cache
*
cache
)
{
struct
file_buffer
*
entry
=
malloc
(
sizeof
(
struct
file_buffer
)
+
cache
->
buffer_size
);
if
(
entry
==
NULL
)
MEM_ERROR
();
entry
->
cache
=
cache
;
entry
->
free_prev
=
entry
->
free_next
=
NULL
;
cache
->
count
++
;
return
entry
;
}
static
struct
file_buffer
*
_cache_get
(
struct
cache
*
cache
,
long
long
index
,
int
hash
)
{
/* Get a free block out of the cache indexed on index. */
struct
file_buffer
*
entry
=
NULL
;
pthread_cleanup_push
((
void
*
)
pthread_mutex_unlock
,
&
cache
->
mutex
);
pthread_mutex_lock
(
&
cache
->
mutex
);
while
(
1
)
{
if
(
cache
->
noshrink_lookup
)
{
/* first try to get a block from the free list */
if
(
cache
->
first_freelist
&&
cache
->
free_list
)
entry
=
cache_freelist
(
cache
);
else
if
(
cache
->
count
<
cache
->
max_buffers
)
{
entry
=
cache_alloc
(
cache
);
cache
->
used
++
;
}
else
if
(
!
cache
->
first_freelist
&&
cache
->
free_list
)
entry
=
cache_freelist
(
cache
);
}
else
{
/* shrinking non-lookup cache */
if
(
cache
->
count
<
cache
->
max_buffers
)
{
entry
=
cache_alloc
(
cache
);
if
(
cache
->
count
>
cache
->
max_count
)
cache
->
max_count
=
cache
->
count
;
}
}
if
(
entry
)
break
;
/* wait for a block */
pthread_cond_wait
(
&
cache
->
wait_for_free
,
&
cache
->
mutex
);
}
/* initialise block and if hash is set insert into the hash table */
entry
->
used
=
1
;
entry
->
locked
=
FALSE
;
entry
->
wait_on_unlock
=
FALSE
;
entry
->
error
=
FALSE
;
if
(
hash
)
{
entry
->
index
=
index
;
insert_cache_hash_table
(
cache
,
entry
);
}
pthread_cleanup_pop
(
1
);
return
entry
;
}
struct
file_buffer
*
cache_get
(
struct
cache
*
cache
,
long
long
index
)
{
return
_cache_get
(
cache
,
index
,
1
);
}
struct
file_buffer
*
cache_get_nohash
(
struct
cache
*
cache
)
{
return
_cache_get
(
cache
,
0
,
0
);
}
void
cache_hash
(
struct
file_buffer
*
entry
,
long
long
index
)
{
struct
cache
*
cache
=
entry
->
cache
;
pthread_cleanup_push
((
void
*
)
pthread_mutex_unlock
,
&
cache
->
mutex
);
pthread_mutex_lock
(
&
cache
->
mutex
);
entry
->
index
=
index
;
insert_cache_hash_table
(
cache
,
entry
);
pthread_cleanup_pop
(
1
);
}
void
cache_block_put
(
struct
file_buffer
*
entry
)
{
struct
cache
*
cache
;
/*
* Finished with this cache entry, once the usage count reaches zero it
* can be reused.
*
* If noshrink_lookup is set, put the block onto the free list.
* As blocks remain accessible via the hash table they can be found
* getting a new lease of life before they are reused.
*
* if noshrink_lookup is not set then shrink the cache.
*/
if
(
entry
==
NULL
)
return
;
cache
=
entry
->
cache
;
pthread_cleanup_push
((
void
*
)
pthread_mutex_unlock
,
&
cache
->
mutex
);
pthread_mutex_lock
(
&
cache
->
mutex
);
entry
->
used
--
;
if
(
entry
->
used
==
0
)
{
if
(
cache
->
noshrink_lookup
)
{
insert_free_list
(
&
cache
->
free_list
,
entry
);
cache
->
used
--
;
}
else
{
free
(
entry
);
cache
->
count
--
;
}
/* One or more threads may be waiting on this block */
pthread_cond_signal
(
&
cache
->
wait_for_free
);
}
pthread_cleanup_pop
(
1
);
}
void
dump_cache
(
struct
cache
*
cache
)
{
pthread_cleanup_push
((
void
*
)
pthread_mutex_unlock
,
&
cache
->
mutex
);
pthread_mutex_lock
(
&
cache
->
mutex
);
if
(
cache
->
noshrink_lookup
)
printf
(
"
\t
Max buffers %d, Current size %d, Used %d, %s
\n
"
,
cache
->
max_buffers
,
cache
->
count
,
cache
->
used
,
cache
->
free_list
?
"Free buffers"
:
"No free buffers"
);
else
printf
(
"
\t
Max buffers %d, Current size %d, Maximum historical "
"size %d
\n
"
,
cache
->
max_buffers
,
cache
->
count
,
cache
->
max_count
);
pthread_cleanup_pop
(
1
);
}
struct
file_buffer
*
cache_get_nowait
(
struct
cache
*
cache
,
long
long
index
)
{
struct
file_buffer
*
entry
=
NULL
;
/*
* block doesn't exist, create it, but return it with the
* locked flag set, so nothing tries to use it while it doesn't
* contain data.
*
* If there's no space in the cache then return NULL.
*/
pthread_cleanup_push
((
void
*
)
pthread_mutex_unlock
,
&
cache
->
mutex
);
pthread_mutex_lock
(
&
cache
->
mutex
);
/* first try to get a block from the free list */
if
(
cache
->
first_freelist
&&
cache
->
free_list
)
entry
=
cache_freelist
(
cache
);
else
if
(
cache
->
count
<
cache
->
max_buffers
)
{
entry
=
cache_alloc
(
cache
);
cache
->
used
++
;
}
else
if
(
!
cache
->
first_freelist
&&
cache
->
free_list
)
entry
=
cache_freelist
(
cache
);
if
(
entry
)
{
/* initialise block and insert into the hash table */
entry
->
used
=
1
;
entry
->
locked
=
TRUE
;
entry
->
wait_on_unlock
=
FALSE
;
entry
->
error
=
FALSE
;
entry
->
index
=
index
;
insert_cache_hash_table
(
cache
,
entry
);
}
pthread_cleanup_pop
(
1
);
return
entry
;
}
struct
file_buffer
*
cache_lookup_nowait
(
struct
cache
*
cache
,
long
long
index
,
char
*
locked
)
{
/*
* Lookup block in the cache, if found return it with the locked flag
* indicating whether it is currently locked. In both cases increment
* the used count.
*
* If it doesn't exist in the cache return NULL;
*/
int
hash
=
CALCULATE_CACHE_HASH
(
index
);
struct
file_buffer
*
entry
;
pthread_cleanup_push
((
void
*
)
pthread_mutex_unlock
,
&
cache
->
mutex
);
pthread_mutex_lock
(
&
cache
->
mutex
);
/* first check if the entry already exists */
for
(
entry
=
cache
->
hash_table
[
hash
];
entry
;
entry
=
entry
->
hash_next
)
if
(
entry
->
index
==
index
)
break
;
if
(
entry
)
{
if
(
entry
->
used
==
0
)
{
remove_free_list
(
&
cache
->
free_list
,
entry
);
cache
->
used
++
;
}
entry
->
used
++
;
*
locked
=
entry
->
locked
;
}
pthread_cleanup_pop
(
1
);
return
entry
;
}
void
cache_wait_unlock
(
struct
file_buffer
*
buffer
)
{
struct
cache
*
cache
=
buffer
->
cache
;
pthread_cleanup_push
((
void
*
)
pthread_mutex_unlock
,
&
cache
->
mutex
);
pthread_mutex_lock
(
&
cache
->
mutex
);
while
(
buffer
->
locked
)
{
/*
* another thread is filling this in, wait until it
* becomes unlocked. Used has been incremented to ensure it
* doesn't get reused. By definition a block can't be
* locked and unused, and so we don't need to worry
* about it being on the freelist now, but, it may
* become unused when unlocked unless used is
* incremented
*/
buffer
->
wait_on_unlock
=
TRUE
;
pthread_cond_wait
(
&
cache
->
wait_for_unlock
,
&
cache
->
mutex
);
}
pthread_cleanup_pop
(
1
);
}
void
cache_unlock
(
struct
file_buffer
*
entry
)
{
struct
cache
*
cache
=
entry
->
cache
;
/*
* Unlock this locked cache entry. If anything is waiting for this
* to become unlocked, wake it up.
*/
pthread_cleanup_push
((
void
*
)
pthread_mutex_unlock
,
&
cache
->
mutex
);
pthread_mutex_lock
(
&
cache
->
mutex
);
entry
->
locked
=
FALSE
;
if
(
entry
->
wait_on_unlock
)
{
entry
->
wait_on_unlock
=
FALSE
;
pthread_cond_broadcast
(
&
cache
->
wait_for_unlock
);
}
pthread_cleanup_pop
(
1
);
}
SQUASHFS/squashfs-tools-4.4/squashfs-tools/caches-queues-lists.h
0 → 100644
View file @
05a1b863
#ifndef CACHES_QUEUES_LISTS_H
#define CACHES_QUEUES_LISTS_H
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2013, 2014, 2019
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* caches-queues-lists.h
*/
#define INSERT_LIST(NAME, TYPE) \
void insert_##NAME##_list(TYPE **list, TYPE *entry) { \
if(*list) { \
entry->NAME##_next = *list; \
entry->NAME##_prev = (*list)->NAME##_prev; \
(*list)->NAME##_prev->NAME##_next = entry; \
(*list)->NAME##_prev = entry; \
} else { \
*list = entry; \
entry->NAME##_prev = entry->NAME##_next = entry; \
} \
}
#define REMOVE_LIST(NAME, TYPE) \
void remove_##NAME##_list(TYPE **list, TYPE *entry) { \
if(entry->NAME##_prev == entry && entry->NAME##_next == entry) { \
/* only this entry in the list */
\
*list = NULL; \
} else if(entry->NAME##_prev != NULL && entry->NAME##_next != NULL) { \
/* more than one entry in the list */
\
entry->NAME##_next->NAME##_prev = entry->NAME##_prev; \
entry->NAME##_prev->NAME##_next = entry->NAME##_next; \
if(*list == entry) \
*list = entry->NAME##_next; \
} \
entry->NAME##_prev = entry->NAME##_next = NULL; \
}
#define INSERT_HASH_TABLE(NAME, TYPE, HASH_FUNCTION, FIELD, LINK) \
void insert_##NAME##_hash_table(TYPE *container, struct file_buffer *entry) \
{ \
int hash = HASH_FUNCTION(entry->FIELD); \
\
entry->LINK##_next = container->hash_table[hash]; \
container->hash_table[hash] = entry; \
entry->LINK##_prev = NULL; \
if(entry->LINK##_next) \
entry->LINK##_next->LINK##_prev = entry; \
}
#define REMOVE_HASH_TABLE(NAME, TYPE, HASH_FUNCTION, FIELD, LINK) \
void remove_##NAME##_hash_table(TYPE *container, struct file_buffer *entry) \
{ \
if(entry->LINK##_prev) \
entry->LINK##_prev->LINK##_next = entry->LINK##_next; \
else \
container->hash_table[HASH_FUNCTION(entry->FIELD)] = \
entry->LINK##_next; \
if(entry->LINK##_next) \
entry->LINK##_next->LINK##_prev = entry->LINK##_prev; \
\
entry->LINK##_prev = entry->LINK##_next = NULL; \
}
#define HASH_SIZE 65536
#define CALCULATE_HASH(n) ((n) & 0xffff)
/* struct describing a cache entry passed between threads */
struct
file_buffer
{
long
long
index
;
long
long
sequence
;
long
long
file_size
;
union
{
long
long
block
;
unsigned
short
checksum
;
};
struct
cache
*
cache
;
union
{
struct
file_info
*
dupl_start
;
struct
file_buffer
*
hash_next
;
};
union
{
int
duplicate
;
struct
file_buffer
*
hash_prev
;
};
union
{
struct
{
struct
file_buffer
*
free_next
;
struct
file_buffer
*
free_prev
;
};
struct
{
struct
file_buffer
*
seq_next
;
struct
file_buffer
*
seq_prev
;
};
};
int
size
;
int
c_byte
;
char
used
;
char
fragment
;
char
error
;
char
locked
;
char
wait_on_unlock
;
char
noD
;
char
data
[
0
]
__attribute__
((
aligned
));
};
/* struct describing queues used to pass data between threads */
struct
queue
{
int
size
;
int
readp
;
int
writep
;
pthread_mutex_t
mutex
;
pthread_cond_t
empty
;
pthread_cond_t
full
;
void
**
data
;
};
/*
* struct describing seq_queues used to pass data between the read
* thread and the deflate and main threads
*/
struct
seq_queue
{
int
fragment_count
;
int
block_count
;
long
long
sequence
;
struct
file_buffer
*
hash_table
[
HASH_SIZE
];
pthread_mutex_t
mutex
;
pthread_cond_t
wait
;
};
/* Cache status struct. Caches are used to keep
track of memory buffers passed between different threads */
struct
cache
{
int
max_buffers
;
int
count
;
int
buffer_size
;
int
noshrink_lookup
;
int
first_freelist
;
union
{
int
used
;
int
max_count
;
};
pthread_mutex_t
mutex
;
pthread_cond_t
wait_for_free
;
pthread_cond_t
wait_for_unlock
;
struct
file_buffer
*
free_list
;
struct
file_buffer
*
hash_table
[
HASH_SIZE
];
};
extern
struct
queue
*
queue_init
(
int
);
extern
void
queue_put
(
struct
queue
*
,
void
*
);
extern
void
*
queue_get
(
struct
queue
*
);
extern
int
queue_empty
(
struct
queue
*
);
extern
void
queue_flush
(
struct
queue
*
);
extern
void
dump_queue
(
struct
queue
*
);
extern
struct
seq_queue
*
seq_queue_init
();
extern
void
seq_queue_put
(
struct
seq_queue
*
,
struct
file_buffer
*
);
extern
void
dump_seq_queue
(
struct
seq_queue
*
,
int
);
extern
struct
file_buffer
*
seq_queue_get
(
struct
seq_queue
*
);
extern
void
seq_queue_flush
(
struct
seq_queue
*
);
extern
struct
cache
*
cache_init
(
int
,
int
,
int
,
int
);
extern
struct
file_buffer
*
cache_lookup
(
struct
cache
*
,
long
long
);
extern
struct
file_buffer
*
cache_get
(
struct
cache
*
,
long
long
);
extern
struct
file_buffer
*
cache_get_nohash
(
struct
cache
*
);
extern
void
cache_hash
(
struct
file_buffer
*
,
long
long
);
extern
void
cache_block_put
(
struct
file_buffer
*
);
extern
void
dump_cache
(
struct
cache
*
);
extern
struct
file_buffer
*
cache_get_nowait
(
struct
cache
*
,
long
long
);
extern
struct
file_buffer
*
cache_lookup_nowait
(
struct
cache
*
,
long
long
,
char
*
);
extern
void
cache_wait_unlock
(
struct
file_buffer
*
);
extern
void
cache_unlock
(
struct
file_buffer
*
);
extern
int
first_freelist
;
#endif
SQUASHFS/squashfs-tools-4.4/squashfs-tools/compressor.c
0 → 100644
View file @
05a1b863
/*
*
* Copyright (c) 2009, 2010, 2011
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* compressor.c
*/
#include <stdio.h>
#include <string.h>
#include "compressor.h"
#include "squashfs_fs.h"
#ifndef GZIP_SUPPORT
static
struct
compressor
gzip_comp_ops
=
{
ZLIB_COMPRESSION
,
"gzip"
};
#else
extern
struct
compressor
gzip_comp_ops
;
#endif
#ifndef LZMA_SUPPORT
static
struct
compressor
lzma_comp_ops
=
{
LZMA_COMPRESSION
,
"lzma"
};
#else
extern
struct
compressor
lzma_comp_ops
;
#endif
#ifndef LZO_SUPPORT
static
struct
compressor
lzo_comp_ops
=
{
LZO_COMPRESSION
,
"lzo"
};
#else
extern
struct
compressor
lzo_comp_ops
;
#endif
#ifndef LZ4_SUPPORT
static
struct
compressor
lz4_comp_ops
=
{
LZ4_COMPRESSION
,
"lz4"
};
#else
extern
struct
compressor
lz4_comp_ops
;
#endif
#ifndef XZ_SUPPORT
static
struct
compressor
xz_comp_ops
=
{
XZ_COMPRESSION
,
"xz"
};
#else
extern
struct
compressor
xz_comp_ops
;
#endif
#ifndef ZSTD_SUPPORT
static
struct
compressor
zstd_comp_ops
=
{
ZSTD_COMPRESSION
,
"zstd"
};
#else
extern
struct
compressor
zstd_comp_ops
;
#endif
static
struct
compressor
unknown_comp_ops
=
{
0
,
"unknown"
};
struct
compressor
*
compressor
[]
=
{
&
gzip_comp_ops
,
&
lzma_comp_ops
,
&
lzo_comp_ops
,
&
lz4_comp_ops
,
&
xz_comp_ops
,
&
zstd_comp_ops
,
&
unknown_comp_ops
};
struct
compressor
*
lookup_compressor
(
char
*
name
)
{
int
i
;
for
(
i
=
0
;
compressor
[
i
]
->
id
;
i
++
)
if
(
strcmp
(
compressor
[
i
]
->
name
,
name
)
==
0
)
break
;
return
compressor
[
i
];
}
struct
compressor
*
lookup_compressor_id
(
int
id
)
{
int
i
;
for
(
i
=
0
;
compressor
[
i
]
->
id
;
i
++
)
if
(
id
==
compressor
[
i
]
->
id
)
break
;
return
compressor
[
i
];
}
void
display_compressors
(
char
*
indent
,
char
*
def_comp
)
{
int
i
;
for
(
i
=
0
;
compressor
[
i
]
->
id
;
i
++
)
if
(
compressor
[
i
]
->
supported
)
fprintf
(
stderr
,
"%s
\t
%s%s
\n
"
,
indent
,
compressor
[
i
]
->
name
,
strcmp
(
compressor
[
i
]
->
name
,
def_comp
)
==
0
?
" (default)"
:
""
);
}
void
display_compressor_usage
(
char
*
def_comp
)
{
int
i
;
for
(
i
=
0
;
compressor
[
i
]
->
id
;
i
++
)
if
(
compressor
[
i
]
->
supported
)
{
char
*
str
=
strcmp
(
compressor
[
i
]
->
name
,
def_comp
)
==
0
?
" (default)"
:
""
;
if
(
compressor
[
i
]
->
usage
)
{
fprintf
(
stderr
,
"
\t
%s%s
\n
"
,
compressor
[
i
]
->
name
,
str
);
compressor
[
i
]
->
usage
();
}
else
fprintf
(
stderr
,
"
\t
%s (no options)%s
\n
"
,
compressor
[
i
]
->
name
,
str
);
}
}
SQUASHFS/squashfs-tools-4.4/squashfs-tools/compressor.h
0 → 100644
View file @
05a1b863
#ifndef COMPRESSOR_H
#define COMPRESSOR_H
/*
*
* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* compressor.h
*/
struct
compressor
{
int
id
;
char
*
name
;
int
supported
;
int
(
*
init
)(
void
**
,
int
,
int
);
int
(
*
compress
)(
void
*
,
void
*
,
void
*
,
int
,
int
,
int
*
);
int
(
*
uncompress
)(
void
*
,
void
*
,
int
,
int
,
int
*
);
int
(
*
options
)(
char
**
,
int
);
int
(
*
options_post
)(
int
);
void
*
(
*
dump_options
)(
int
,
int
*
);
int
(
*
extract_options
)(
int
,
void
*
,
int
);
int
(
*
check_options
)(
int
,
void
*
,
int
);
void
(
*
display_options
)(
void
*
,
int
);
void
(
*
usage
)();
};
extern
struct
compressor
*
lookup_compressor
(
char
*
);
extern
struct
compressor
*
lookup_compressor_id
(
int
);
extern
void
display_compressors
(
char
*
,
char
*
);
extern
void
display_compressor_usage
(
char
*
);
static
inline
int
compressor_init
(
struct
compressor
*
comp
,
void
**
stream
,
int
block_size
,
int
datablock
)
{
if
(
comp
->
init
==
NULL
)
return
0
;
return
comp
->
init
(
stream
,
block_size
,
datablock
);
}
static
inline
int
compressor_compress
(
struct
compressor
*
comp
,
void
*
strm
,
void
*
dest
,
void
*
src
,
int
size
,
int
block_size
,
int
*
error
)
{
return
comp
->
compress
(
strm
,
dest
,
src
,
size
,
block_size
,
error
);
}
static
inline
int
compressor_uncompress
(
struct
compressor
*
comp
,
void
*
dest
,
void
*
src
,
int
size
,
int
block_size
,
int
*
error
)
{
return
comp
->
uncompress
(
dest
,
src
,
size
,
block_size
,
error
);
}
/*
* For the following functions please see the lzo, lz4 or xz
* compressors for commented examples of how they are used.
*/
static
inline
int
compressor_options
(
struct
compressor
*
comp
,
char
*
argv
[],
int
argc
)
{
if
(
comp
->
options
==
NULL
)
return
-
1
;
return
comp
->
options
(
argv
,
argc
);
}
static
inline
int
compressor_options_post
(
struct
compressor
*
comp
,
int
block_size
)
{
if
(
comp
->
options_post
==
NULL
)
return
0
;
return
comp
->
options_post
(
block_size
);
}
static
inline
void
*
compressor_dump_options
(
struct
compressor
*
comp
,
int
block_size
,
int
*
size
)
{
if
(
comp
->
dump_options
==
NULL
)
return
NULL
;
return
comp
->
dump_options
(
block_size
,
size
);
}
static
inline
int
compressor_extract_options
(
struct
compressor
*
comp
,
int
block_size
,
void
*
buffer
,
int
size
)
{
if
(
comp
->
extract_options
==
NULL
)
return
size
?
-
1
:
0
;
return
comp
->
extract_options
(
block_size
,
buffer
,
size
);
}
static
inline
int
compressor_check_options
(
struct
compressor
*
comp
,
int
block_size
,
void
*
buffer
,
int
size
)
{
if
(
comp
->
check_options
==
NULL
)
return
0
;
return
comp
->
check_options
(
block_size
,
buffer
,
size
);
}
static
inline
void
compressor_display_options
(
struct
compressor
*
comp
,
void
*
buffer
,
int
size
)
{
if
(
comp
->
display_options
!=
NULL
)
comp
->
display_options
(
buffer
,
size
);
}
#endif
SQUASHFS/squashfs-tools-4.4/squashfs-tools/error.h
0 → 100644
View file @
05a1b863
#ifndef ERROR_H
#define ERROR_H
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2012, 2013, 2014, 2019
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* error.h
*/
extern
int
exit_on_error
;
extern
void
prep_exit
();
extern
void
progressbar_error
(
char
*
fmt
,
...);
extern
void
progressbar_info
(
char
*
fmt
,
...);
#ifdef SQUASHFS_TRACE
#define TRACE(s, args...) \
do { \
progressbar_info("squashfs: "s, ## args);\
} while(0)
#else
#define TRACE(s, args...)
#endif
#define INFO(s, args...) \
do {\
if(!silent)\
progressbar_info(s, ## args);\
} while(0)
#define ERROR(s, args...) \
do {\
progressbar_error(s, ## args); \
} while(0)
#define ERROR_START(s, args...) \
do { \
disable_progress_bar(); \
fprintf(stderr, s, ## args); \
} while(0)
#define ERROR_EXIT(s, args...) \
do {\
if (exit_on_error) { \
fprintf(stderr, "\n"); \
EXIT_MKSQUASHFS(); \
} else { \
fprintf(stderr, s, ## args); \
enable_progress_bar(); \
} \
} while(0)
#define EXIT_MKSQUASHFS() \
do {\
prep_exit();\
exit(1);\
} while(0)
#define BAD_ERROR(s, args...) \
do {\
progressbar_error("FATAL ERROR:" s, ##args); \
EXIT_MKSQUASHFS();\
} while(0)
#define EXIT_UNSQUASH(s, args...) BAD_ERROR(s, ##args)
#define EXIT_UNSQUASH_IGNORE(s, args...) \
do {\
if(ignore_errors) \
ERROR(s, ##args); \
else \
BAD_ERROR(s, ##args); \
} while(0)
#define EXIT_UNSQUASH_STRICT(s, args...) \
do {\
if(!strict_errors) \
ERROR(s, ##args); \
else \
BAD_ERROR(s, ##args); \
} while(0)
#define MEM_ERROR() \
do {\
progressbar_error("FATAL ERROR: Out of memory (%s)\n", \
__func__); \
EXIT_MKSQUASHFS();\
} while(0)
#endif
Prev
1
…
9
10
11
12
13
14
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment