This column covers utilities that set and list ext2/ext3 file attributes.
You probably already know about permission bits and chmod(1);
they're available on all Linux filesystems.
The ext2 and ext3 filesystems also support file attributes.
If you haven't used attributes before, they might surprise you.
For instance, chattr +a myfile
makes myfile append-only:
you can add to the end of it, but you can't change its contents.
Many file attributes have been planned but aren't implemented yet.
Still, the ones that work now are worth knowing about!
The user-level programs chattr(1) and lsattr(1) change and list attributes. (They're part of e2fsprogs package, from http://e2fsprogs.sourceforge.net/. Version 1.40.8 was released in March, 2008.) The chattr manpage describes the attributes pretty well, so we'll see just a couple of examples. However, the lsattr manpage gives almost no information -- so we'll check the source code to figure out how it works.
Attributes are supported on ext2 and ext3 filesystems (and the ext4 filesystem, under development). Posix and BSD machines with those filesystems also support file attributes.
(On BSD systems, also see the chflags(1) utility and the command
ls -l -o
.
These set and list file flags.)
If you get an error like
lsattr: Inappropriate ioctl for device While reading flags on filesystem
,
then check /etc/mtab to find a locally-mounted ext[234] filesystem.
(You can't check or set attributes on an NFS-mounted filesystem, for example.)
The attributes include:
A
: no atime updatesa
: append onlyc
: compressedD
: synchronous directory updatesd
: no dumpE
: compression errori
: immutablej
: data journallingS
: synchronous updatess
: secure deletionT
: top of directory hierarchyt
: no tail-mergingu
: undeletableX
: raw compressed dataZ
: dirty compressed dataNot all of the planned attributes are supported yet, and some attributes can be set only by the superuser (root) or by processes with certain capabilities. The current chattr(1) manpage lists those limitations, including:
c
, s
, and u
attributes aren't implemented in mainline Linux kernels,E
, X
, and Z
are part of experimental compression patches, and they can only be read with lsattr (not set or cleared with chattr).j
only works on the journaling ext3 filesystem.As we'll see later (the section "Checking lsattr output"), a few other attributes are defined but not documented in chattr(1).
Listing One has an example of file attributes:
setting a file's append-only (a
) attribute, then trying to overwrite
or remove the file.
First, ls -l
shows that the log file has 132 characters and
lsattr shows that it has no attributes set.
After setting the a
attribute with chattr +a
, lsattr shows
the attribute set.
Then we can't replace the file with the shell's >
operator or
the cp command -- or remove it with rm.
An append operation (with >>
) succeeds, though;
ls -l
shows that the file now has 143 characters.
Removing the attribute with chattr -a
lets us overvrite the file;
now it has just 15 characters.
Listing One: Append-only file
# ls -l log -rw-r--r-- 1 root root 132 2008-04-22 11:41 log # lsattr log ----------------- log # chattr +a log # lsattr log -----a----------- log # echo the first line > log bash: log: Operation not permitted # echo a new line >> log # ls -l log -rw-r--r-- 1 root root 143 2008-04-22 11:44 log # cp /var/log/messages log cp: cannot create regular file `log': Operation not permitted # rm log rm: cannot remove `log': Operation not permitted # chattr -a log # echo the first line > log # ls -l log -rw-r--r-- 1 root root 15 2008-04-22 11:45 log
The append-only flag can help to prevent accidental changes in log files. Like many attributes, it can only be set or cleared by the superuser or by a process with that privilege.
The A
attribute prevents changes to a file's last-access time.
As the chattr manpage says, this can prevent some disk I/O on laptop
computers and other filesystems -- which can be handy for files that are
accessed often.
It's also useful whenever you want to preserve a file's atime:
use chattr +A
to prevent changes, then use chattr -A
once the
atime can change again.
Although most attributes must be set or removed by the superuser
or a specially-privileged process, any user can set the A
attribute.
Listing Two has examples with two files, busyfile and otherfile.
After setting the A
attribute on busyfile,
we show both files' atimes.
Next we read each file (and throw the text away into /dev/null)
and check both files again:
otherfile's atime has changed, but busyfile's has not.
After removing the A
attribute, checking the atimes again shows
no change.
Listing Two: Preventing atime changes
$ ls -l *file -rw------- 1 jpeek users 776 Apr 23 11:37 busyfile -rw------- 1 jpeek users 776 Apr 23 11:39 otherfile $ chattr +A busyfile $ lsattr *file -------A---------- busyfile ------------------ otherfile $ ls -lu *file -rw------- 1 jpeek users 776 Apr 23 11:37 busyfile -rw------- 1 jpeek users 776 Apr 23 11:39 otherfile $ cat *file >/dev/null $ ls -lu *file -rw------- 1 jpeek users 776 Apr 23 11:37 busyfile -rw------- 1 jpeek users 776 Apr 23 11:40 otherfile $ chattr -A busyfile $ ls -lu *file -rw------- 1 jpeek users 776 Apr 23 11:37 busyfile -rw------- 1 jpeek users 776 Apr 23 11:40 otherfile
Documentation on lsattr(1) is much less detailed than for chattr.
There's also an undocumented but useful -l
option.
Let's dig in.
lsattr gives one line of information for each file. It lists files and directories named on its command line; by default, it lists all files in the current directory:
$ lsattr /root/tmp -----a----------- /root/tmp/log ----i------------ /root/tmp/uneditable ------dA--------- /root/tmp/activefile
The undocumented lsattr -l
option (it's a lowercase "L")
lists the long name of each attribute.
This can be handy when you don't remember what each letter stands for:
$ cd /root/tmp $ lsattr -l ./log Append_Only ./uneditable Immutable ./activefile No_Dump, No_Atime
Each file has a version/generation number, which you can list with
lsattr -v
.
This comes from the ext2 filesystem inode, the unsigned long variable
i_generation
(also called i_version
in some versions).
It's especially useful with NFS, for systems that have an open
filehandle for a file on another machine that's deleted and later the
inode is re-used while the NFS filehandle is still open.
Changing the inode's version/generation number can show that the file
has changed.
As Listing Three shows, lsattr displays the version number for the
target of a symbolic link and for any hard links, too.
You can set this number with chattr -v
, but that's probably not
advisable without a good reason...
Listing Three: File version/generation numbers
$ ls -l total 8 -rw-r--r-- 2 jpeek users 119 2008-04-22 13:09 afile -rw-r--r-- 2 jpeek users 119 2008-04-22 13:09 hardlink lrwxrwxrwx 1 jpeek users 5 2008-04-22 13:07 symlink -> afile $ lsattr -v afile 1260086426 ----------------- afile $ chattr -v 1234 afile $ lsattr -v 1234 ----------------- ./hardlink 1234 ----------------- ./afile 1234 ----------------- ./symlink
Most Linux utilties don't recognize attributes. For instance, find(1) can't search for files by their attributes. But you can probably get what you want in the old-fashioned way: by hacking the output of lsattr with a shell alias, function, or script. Because the order of attributes from lsattr isn't documented, we'll have to check the source code.
Listing Four has the relevant part of e2fs_lib.c. The function print_flags(), which isn't shown here, iterates through flags_array:
-l
option, it prints the long_name
of any attribute that's setshort_name
of each set attribute, and a dash
(-
) otherwise.Listing Four: How lsattr lists attributes
/* Print file attributes on an ext2 file system */ struct flags_name { unsigned long flag; const char *short_name; const char *long_name; }; static const struct flags_name flags_array[] = { { EXT2_SECRM_FL, "s", "Secure_Deletion" }, { EXT2_UNRM_FL, "u" , "Undelete" }, { EXT2_SYNC_FL, "S", "Synchronous_Updates" }, { EXT2_DIRSYNC_FL, "D", "Synchronous_Directory_Updates" }, { EXT2_IMMUTABLE_FL, "i", "Immutable" }, { EXT2_APPEND_FL, "a", "Append_Only" }, { EXT2_NODUMP_FL, "d", "No_Dump" }, { EXT2_NOATIME_FL, "A", "No_Atime" }, { EXT2_COMPR_FL, "c", "Compression_Requested" }, #ifdef ENABLE_COMPRESSION { EXT2_COMPRBLK_FL, "B", "Compressed_File" }, { EXT2_DIRTY_FL, "Z", "Compressed_Dirty_File" }, { EXT2_NOCOMPR_FL, "X", "Compression_Raw_Access" }, { EXT2_ECOMPR_FL, "E", "Compression_Error" }, #endif { EXT3_JOURNAL_DATA_FL, "j", "Journaled_Data" }, { EXT2_INDEX_FL, "I", "Indexed_direcctory" }, { EXT2_NOTAIL_FL, "t", "No_Tailmerging" }, { EXT2_TOPDIR_FL, "T", "Top_of_Directory_Hierarchies" }, { 0, NULL, NULL } };
You can extend lsattr by filtering its output through other utilities.
For instance, to find all files in the current directory with the i
("immutable") attribute set:
lsattr | grep '^-*i-* '
As we saw earlier, that will actually include symbolic links -- which
don't actually have the attribute set.
To exclude symlinks (and the current directory itself),
use find with its -maxdepth 1
option and xargs.
(The find -print0
and xargs -0
options handle "special"
filenames reliably; see the article “Filename Trouble”.)
find . -maxdepth 1 ! -name . ! -type l -print0 | xargs -0 lsattr | grep '^-*i-* '
To find all files in the current directory and below that
have an attribute set, try piping lsattr output through
egrep -v
to omit lines that are empty,
the names of directories (lines ending with a colon character),
error messages, and lines for files with no attributes (lines that start
with all dash characters followed by a space).
The egrep operator |
(a vertical bar) is the pattern alternation
("OR") operator.
The lsattr option -R
does a recursive search, and the shell
operator 2>&1
merges standard error onto standard output
(so egrep will filter everything):
lsattr -R 2>&1 | egrep -v '^$|:$|^lsattr: |^-+ '
To find files with any arbitrary attribute set, a script like the one
in Listing Five could do the job.
The script uses grep to search for any line of lsattr output
that starts with zero or more of the valid attribute letters or a dash,
followed by the attribute letter you're looking for,
followed by zero or more other attributes or dashes,
followed by a space character.
The attribute letters were copied from the
flags_array
in e2fs_lib.c; since some of these attributes are
unsupported or not normally used, you may want to shorten the list.
Listing Five: findattr script
#!/bin/bash # findattr - find files in current directory # with a certain attribute # Usage: findattr attribute attributes='suSDiadAcBZXEjItT' case "$1" in [$attributes]) lsattr | grep "^[-$attributes]*$1[-$attributes]* " ;; *) echo "${0##*/}: unknown attribute '$1'" 1>&2 exit 1 ;; esac
Those are just some examples. Cook up your own to get the most out of file attributes.
There's more information at http://www.securityfocus.com/infocus/1407, http://e2fsprogs.sourceforge.net/, the section "Advanced" Ext2fs features of the paper at http://e2fsprogs.sourceforge.net/ext2intro.html#section:ext2fs, the section ``History'' of http://www.win.tue.nl/~aeb/linux/lk/lk-7.html#ss7.2, and in the article "Programming Linux 2.6" at http://www.linux-mag.com/id/1684.
Jerry Peek is a freelance writer and instructor who has used Unix and Linux for more than 25 years. He's happy to hear from readers; see https://www.jpeek.com/contact.html.
[Read previous article]
[Read next article]
[Read Jerry’s other Linux Magazine articles]