mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
New APIs for reading and writing ext2 file attributes and file generation.
The new APIs are: get-e2attrs: List ext2 file attributes of a file. set-e2attrs: Set or clear ext2 file attributes of a file. get-e2generation: Get ext2 file generation of a file. set-e2generation: Set ext2 file generation of a file. These are implemented using the lsattr and chattr programs from e2fsprogs.
This commit is contained in:
2
TODO
2
TODO
@@ -51,8 +51,6 @@ Ideas for extra commands
|
||||
more mk*temp calls
|
||||
|
||||
ext2 properties:
|
||||
chattr
|
||||
lsattr
|
||||
badblocks
|
||||
debugfs
|
||||
dumpe2fs
|
||||
|
||||
194
daemon/ext2.c
194
daemon/ext2.c
@@ -1,5 +1,5 @@
|
||||
/* libguestfs - the guestfsd daemon
|
||||
* Copyright (C) 2009 Red Hat Inc.
|
||||
* Copyright (C) 2009-2012 Red Hat Inc.
|
||||
*
|
||||
* 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
|
||||
@@ -694,3 +694,195 @@ do_tune2fs (const char *device, /* only required parameter */
|
||||
free (err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
compare_chars (const void *vc1, const void *vc2)
|
||||
{
|
||||
char c1 = * (char *) vc1;
|
||||
char c2 = * (char *) vc2;
|
||||
return c1 - c2;
|
||||
}
|
||||
|
||||
char *
|
||||
do_get_e2attrs (const char *filename)
|
||||
{
|
||||
int r;
|
||||
char *buf;
|
||||
char *out, *err;
|
||||
size_t i, j;
|
||||
|
||||
buf = sysroot_path (filename);
|
||||
if (!buf) {
|
||||
reply_with_perror ("malloc");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r = command (&out, &err, "lsattr", "-d", "--", buf, NULL);
|
||||
free (buf);
|
||||
if (r == -1) {
|
||||
reply_with_error ("%s: %s: %s", "lsattr", filename, err);
|
||||
free (err);
|
||||
free (out);
|
||||
return NULL;
|
||||
}
|
||||
free (err);
|
||||
|
||||
/* Output looks like:
|
||||
* -------------e- filename
|
||||
* Remove the dashes and return everything up to the space.
|
||||
*/
|
||||
for (i = j = 0; out[j] != ' '; j++) {
|
||||
if (out[j] != '-')
|
||||
out[i++] = out[j];
|
||||
}
|
||||
|
||||
out[i] = '\0';
|
||||
|
||||
/* Sort the output, mainly to make testing simpler. */
|
||||
qsort (out, i, sizeof (char), compare_chars);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/* Takes optional arguments, consult optargs_bitmask. */
|
||||
int
|
||||
do_set_e2attrs (const char *filename, const char *attrs, int clear)
|
||||
{
|
||||
int r;
|
||||
char *buf;
|
||||
char *err;
|
||||
size_t i, j;
|
||||
int lowers[26], uppers[26];
|
||||
char attr_arg[26*2+1+1]; /* '+'/'-' + attrs + trailing '\0' */
|
||||
|
||||
if (!(optargs_bitmask & GUESTFS_SET_E2ATTRS_CLEAR_BITMASK))
|
||||
attr_arg[0] = '+';
|
||||
else if (!clear)
|
||||
attr_arg[0] = '+';
|
||||
else
|
||||
attr_arg[0] = '-';
|
||||
j = 1;
|
||||
|
||||
/* You can't write "chattr - file", so we have to just return if
|
||||
* the string is empty.
|
||||
*/
|
||||
if (STREQ (attrs, ""))
|
||||
return 0;
|
||||
|
||||
/* Valid attrs are all lower or upper case ASCII letters. Check
|
||||
* this and that there are no duplicates.
|
||||
*/
|
||||
memset (lowers, 0, sizeof lowers);
|
||||
memset (uppers, 0, sizeof uppers);
|
||||
for (; *attrs; attrs++) {
|
||||
/* These are reserved by the chattr program for command line flags. */
|
||||
if (*attrs == 'R' || *attrs == 'V' || *attrs == 'f' || *attrs == 'v') {
|
||||
reply_with_error ("bad file attribute '%c'", *attrs);
|
||||
return -1;
|
||||
}
|
||||
else if (*attrs >= 'a' && *attrs <= 'z') {
|
||||
i = *attrs - 'a';
|
||||
if (lowers[i] > 0)
|
||||
goto error_duplicate;
|
||||
lowers[i]++;
|
||||
attr_arg[j++] = *attrs;
|
||||
}
|
||||
else if (*attrs >= 'A' && *attrs <= 'Z') {
|
||||
i = *attrs - 'A';
|
||||
if (uppers[i] > 0) {
|
||||
error_duplicate:
|
||||
reply_with_error ("duplicate file attribute '%c'", *attrs);
|
||||
return -1;
|
||||
}
|
||||
uppers[i]++;
|
||||
attr_arg[j++] = *attrs;
|
||||
}
|
||||
else {
|
||||
reply_with_error ("unknown file attribute '%c'", *attrs);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
attr_arg[j] = '\0';
|
||||
|
||||
buf = sysroot_path (filename);
|
||||
if (!buf) {
|
||||
reply_with_perror ("malloc");
|
||||
return -1;
|
||||
}
|
||||
|
||||
r = command (NULL, &err, "chattr", attr_arg, "--", buf, NULL);
|
||||
free (buf);
|
||||
if (r == -1) {
|
||||
reply_with_error ("%s: %s: %s", "chattr", filename, err);
|
||||
free (err);
|
||||
return -1;
|
||||
}
|
||||
free (err);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t
|
||||
do_get_e2generation (const char *filename)
|
||||
{
|
||||
int r;
|
||||
char *buf;
|
||||
char *out, *err;
|
||||
int64_t ret;
|
||||
|
||||
buf = sysroot_path (filename);
|
||||
if (!buf) {
|
||||
reply_with_perror ("malloc");
|
||||
return -1;
|
||||
}
|
||||
|
||||
r = command (&out, &err, "lsattr", "-dv", "--", buf, NULL);
|
||||
free (buf);
|
||||
if (r == -1) {
|
||||
reply_with_error ("%s: %s: %s", "lsattr", filename, err);
|
||||
free (err);
|
||||
free (out);
|
||||
return -1;
|
||||
}
|
||||
free (err);
|
||||
|
||||
if (sscanf (out, "%" SCNu64, &ret) != 1) {
|
||||
reply_with_error ("cannot parse output from '%s' command: %s",
|
||||
"lsattr", out);
|
||||
free (out);
|
||||
return -1;
|
||||
}
|
||||
free (out);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
do_set_e2generation (const char *filename, int64_t generation)
|
||||
{
|
||||
int r;
|
||||
char *buf;
|
||||
char *err;
|
||||
char generation_str[64];
|
||||
|
||||
buf = sysroot_path (filename);
|
||||
if (!buf) {
|
||||
reply_with_perror ("malloc");
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf (generation_str, sizeof generation_str,
|
||||
"%" PRIu64, (uint64_t) generation);
|
||||
|
||||
r = command (NULL, &err, "chattr", "-v", generation_str, "--", buf, NULL);
|
||||
free (buf);
|
||||
if (r == -1) {
|
||||
reply_with_error ("%s: %s: %s", "chattr", filename, err);
|
||||
free (err);
|
||||
return -1;
|
||||
}
|
||||
free (err);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -6940,6 +6940,199 @@ For more information on the optional arguments, see L<mkfs.btrfs(8)>.
|
||||
|
||||
To create general filesystems, use C<guestfs_mkfs_opts>.");
|
||||
|
||||
("get_e2attrs", (RString "attrs", [Pathname "file"], []), 318, [],
|
||||
[InitScratchFS, Always, TestOutput (
|
||||
[["touch"; "/e2attrs1"];
|
||||
["get_e2attrs"; "/e2attrs1"]], "");
|
||||
InitScratchFS, Always, TestOutput (
|
||||
[["touch"; "/e2attrs2"];
|
||||
["set_e2attrs"; "/e2attrs2"; "is"; "false"];
|
||||
["get_e2attrs"; "/e2attrs2"]], "is");
|
||||
InitScratchFS, Always, TestOutput (
|
||||
[["touch"; "/e2attrs3"];
|
||||
["set_e2attrs"; "/e2attrs3"; "is"; "false"];
|
||||
["set_e2attrs"; "/e2attrs3"; "i"; "true"];
|
||||
["get_e2attrs"; "/e2attrs3"]], "s");
|
||||
InitScratchFS, Always, TestOutput (
|
||||
[["touch"; "/e2attrs4"];
|
||||
["set_e2attrs"; "/e2attrs4"; "adst"; "false"];
|
||||
["set_e2attrs"; "/e2attrs4"; "iS"; "false"];
|
||||
["set_e2attrs"; "/e2attrs4"; "i"; "true"];
|
||||
["set_e2attrs"; "/e2attrs4"; "ad"; "true"];
|
||||
["set_e2attrs"; "/e2attrs4"; ""; "false"];
|
||||
["set_e2attrs"; "/e2attrs4"; ""; "true"];
|
||||
["get_e2attrs"; "/e2attrs4"]], "Sst");
|
||||
InitScratchFS, Always, TestLastFail (
|
||||
[["touch"; "/e2attrs5"];
|
||||
["set_e2attrs"; "/e2attrs5"; "R"; "false"]]);
|
||||
InitScratchFS, Always, TestLastFail (
|
||||
[["touch"; "/e2attrs6"];
|
||||
["set_e2attrs"; "/e2attrs6"; "v"; "false"]]);
|
||||
InitScratchFS, Always, TestLastFail (
|
||||
[["touch"; "/e2attrs7"];
|
||||
["set_e2attrs"; "/e2attrs7"; "aa"; "false"]]);
|
||||
InitScratchFS, Always, TestLastFail (
|
||||
[["touch"; "/e2attrs8"];
|
||||
["set_e2attrs"; "/e2attrs8"; "BabcdB"; "false"]])],
|
||||
"get ext2 file attributes of a file",
|
||||
"\
|
||||
This returns the file attributes associated with C<file>.
|
||||
|
||||
The attributes are a set of bits associated with each
|
||||
inode which affect the behaviour of the file. The attributes
|
||||
are returned as a string of letters (described below). The
|
||||
string may be empty, indicating that no file attributes are
|
||||
set for this file.
|
||||
|
||||
These attributes are only present when the file is located on
|
||||
an ext2/3/4 filesystem. Using this call on other filesystem
|
||||
types will result in an error.
|
||||
|
||||
The characters (file attributes) in the returned string are
|
||||
currently:
|
||||
|
||||
=over 4
|
||||
|
||||
=item 'A'
|
||||
|
||||
When the file is accessed, its atime is not modified.
|
||||
|
||||
=item 'a'
|
||||
|
||||
The file is append-only.
|
||||
|
||||
=item 'c'
|
||||
|
||||
The file is compressed on-disk.
|
||||
|
||||
=item 'D'
|
||||
|
||||
(Directories only.) Changes to this directory are written
|
||||
synchronously to disk.
|
||||
|
||||
=item 'd'
|
||||
|
||||
The file is not a candidate for backup (see L<dump(8)>).
|
||||
|
||||
=item 'E'
|
||||
|
||||
The file has compression errors.
|
||||
|
||||
=item 'e'
|
||||
|
||||
The file is using extents.
|
||||
|
||||
=item 'h'
|
||||
|
||||
The file is storing its blocks in units of the filesystem blocksize
|
||||
instead of sectors.
|
||||
|
||||
=item 'I'
|
||||
|
||||
(Directories only.) The directory is using hashed trees.
|
||||
|
||||
=item 'i'
|
||||
|
||||
The file is immutable. It cannot be modified, deleted or renamed.
|
||||
No link can be created to this file.
|
||||
|
||||
=item 'j'
|
||||
|
||||
The file is data-journaled.
|
||||
|
||||
=item 's'
|
||||
|
||||
When the file is deleted, all its blocks will be zeroed.
|
||||
|
||||
=item 'S'
|
||||
|
||||
Changes to this file are written synchronously to disk.
|
||||
|
||||
=item 'T'
|
||||
|
||||
(Directories only.) This is a hint to the block allocator
|
||||
that subdirectories contained in this directory should be
|
||||
spread across blocks. If not present, the block allocator
|
||||
will try to group subdirectories together.
|
||||
|
||||
=item 't'
|
||||
|
||||
For a file, this disables tail-merging.
|
||||
(Not used by upstream implementations of ext2.)
|
||||
|
||||
=item 'u'
|
||||
|
||||
When the file is deleted, its blocks will be saved, allowing
|
||||
the file to be undeleted.
|
||||
|
||||
=item 'X'
|
||||
|
||||
The raw contents of the compressed file may be accessed.
|
||||
|
||||
=item 'Z'
|
||||
|
||||
The compressed file is dirty.
|
||||
|
||||
=back
|
||||
|
||||
More file attributes may be added to this list later. Not all
|
||||
file attributes may be set for all kinds of files. For
|
||||
detailed information, consult the L<chattr(1)> man page.
|
||||
|
||||
See also C<guestfs_set_e2attrs>.
|
||||
|
||||
Don't confuse these attributes with extended attributes
|
||||
(see C<guestfs_getxattr>).");
|
||||
|
||||
("set_e2attrs", (RErr, [Pathname "file"; String "attrs"], [OBool "clear"]), 319, [],
|
||||
[] (* tested by get_e2attrs *),
|
||||
"set ext2 file attributes of a file",
|
||||
"\
|
||||
This sets or clears the file attributes C<attrs>
|
||||
associated with the inode C<file>.
|
||||
|
||||
C<attrs> is a string of characters representing
|
||||
file attributes. See C<guestfs_get_e2attrs> for a list of
|
||||
possible attributes. Not all attributes can be changed.
|
||||
|
||||
If optional boolean C<clear> is not present or false, then
|
||||
the C<attrs> listed are set in the inode.
|
||||
|
||||
If C<clear> is true, then the C<attrs> listed are cleared
|
||||
in the inode.
|
||||
|
||||
In both cases, other attributes not present in the C<attrs>
|
||||
string are left unchanged.
|
||||
|
||||
These attributes are only present when the file is located on
|
||||
an ext2/3/4 filesystem. Using this call on other filesystem
|
||||
types will result in an error.");
|
||||
|
||||
("get_e2generation", (RInt64 "generation", [Pathname "file"], []), 320, [],
|
||||
[InitScratchFS, Always, TestOutputInt (
|
||||
[["touch"; "/e2generation"];
|
||||
["set_e2generation"; "/e2generation"; "123456"];
|
||||
["get_e2generation"; "/e2generation"]], 123456)],
|
||||
"get ext2 file generation of a file",
|
||||
"\
|
||||
This returns the ext2 file generation of a file. The generation
|
||||
(which used to be called the \"version\") is a number associated
|
||||
with an inode. This is most commonly used by NFS servers.
|
||||
|
||||
The generation is only present when the file is located on
|
||||
an ext2/3/4 filesystem. Using this call on other filesystem
|
||||
types will result in an error.
|
||||
|
||||
See C<guestfs_set_e2generation>.");
|
||||
|
||||
("set_e2generation", (RErr, [Pathname "file"; Int64 "generation"], []), 321, [],
|
||||
[], (* tested by get_e2generation *)
|
||||
"set ext2 file generation of a file",
|
||||
"\
|
||||
This sets the ext2 file generation of a file.
|
||||
|
||||
See C<guestfs_get_e2generation>.");
|
||||
|
||||
]
|
||||
|
||||
let all_functions = non_daemon_functions @ daemon_functions
|
||||
|
||||
@@ -58,7 +58,8 @@ guestfs_gobject_headers=\
|
||||
guestfs-gobject-optargs-e2fsck.h \
|
||||
guestfs-gobject-optargs-ntfsfix.h \
|
||||
guestfs-gobject-optargs-ntfsclone_out.h \
|
||||
guestfs-gobject-optargs-mkfs_btrfs.h
|
||||
guestfs-gobject-optargs-mkfs_btrfs.h \
|
||||
guestfs-gobject-optargs-set_e2attrs.h
|
||||
|
||||
guestfs_gobject_sources=\
|
||||
guestfs-gobject-session.c \
|
||||
@@ -98,4 +99,5 @@ guestfs_gobject_sources=\
|
||||
guestfs-gobject-optargs-e2fsck.c \
|
||||
guestfs-gobject-optargs-ntfsfix.c \
|
||||
guestfs-gobject-optargs-ntfsclone_out.c \
|
||||
guestfs-gobject-optargs-mkfs_btrfs.c
|
||||
guestfs-gobject-optargs-mkfs_btrfs.c \
|
||||
guestfs-gobject-optargs-set_e2attrs.c
|
||||
|
||||
@@ -149,6 +149,7 @@ gobject/guestfs-gobject-optargs-mount_local.c
|
||||
gobject/guestfs-gobject-optargs-ntfsclone_out.c
|
||||
gobject/guestfs-gobject-optargs-ntfsfix.c
|
||||
gobject/guestfs-gobject-optargs-ntfsresize_opts.c
|
||||
gobject/guestfs-gobject-optargs-set_e2attrs.c
|
||||
gobject/guestfs-gobject-optargs-test0.c
|
||||
gobject/guestfs-gobject-optargs-tune2fs.c
|
||||
gobject/guestfs-gobject-optargs-umount_local.c
|
||||
|
||||
@@ -1 +1 @@
|
||||
317
|
||||
321
|
||||
|
||||
Reference in New Issue
Block a user