From: Nye Liu <nyet@nyet.org>
When unpacking the cpio into the initramfs, mtimes are not preserved by
default. This patch adds an INITRAMFS_PRESERVE_MTIME option that allows
mtimes stored in the cpio image to be used when constructing the
initramfs. For embedded applications that run exclusively out of the
initramfs, this is invaluable.
Signed-off-by: Nye Liu <nyet@nyet.org>
---
diff --git a/init/initramfs.c b/init/initramfs.c
index 644fc01..0dd0a73 100644
--- a/init/initramfs.c
+++ b/init/initramfs.c
@@ -7,6 +7,12 @@
#include <linux/string.h>
#include <linux/syscalls.h>
+#ifdef CONFIG_INITRAMFS_PRESERVE_MTIME
+#include <linux/utime.h>
+#include <linux/namei.h>
+#include <linux/list.h>
+#endif
+
static __initdata char *message;
static void __init error(char *x)
{
@@ -72,6 +78,38 @@ static void __init free_hash(void)
}
}
+#ifdef CONFIG_INITRAMFS_PRESERVE_MTIME
+static __initdata LIST_HEAD(dir_list);
+struct dir_entry {
+ struct list_head list;
+ char *name;
+ struct utimbuf mtime;
+};
+
+static void __init dir_add(const char *name, struct utimbuf mtime)
+{
+ struct dir_entry *de = kmalloc(sizeof(struct dir_entry), GFP_KERNEL);
+ INIT_LIST_HEAD(&de->list);
+ de->name = kstrdup(name, GFP_KERNEL);
+ de->mtime = mtime;
+ list_add(&de->list, &dir_list);
+}
+
+static void __init dir_utime(void)
+{
+ struct list_head *e, *tmp;
+ list_for_each_safe(e, tmp, &dir_list) {
+ struct dir_entry *de = list_entry(e, struct dir_entry, list);
+ list_del(e);
+ sys_utime(de->name, &de->mtime);
+ kfree(de->name);
+ kfree(de);
+ }
+}
+
+static __initdata struct utimbuf mtime;
+#endif
+
/* cpio header parsing */
static __initdata unsigned long ino, major, minor, nlink;
@@ -97,6 +135,9 @@ static void __init parse_header(char *s)
uid = parsed[2];
gid = parsed[3];
nlink = parsed[4];
+#ifdef CONFIG_INITRAMFS_PRESERVE_MTIME
+ mtime.actime = mtime.modtime = parsed[5];
+#endif
body_len = parsed[6];
major = parsed[7];
minor = ...It would be better to do it unconditionally if possible - remove the Please avoid using the sys_ namespace. That is for system calls. How does this differ from sys_utime()? --
Resubmission of initramfs preserve mtime patch following Andrew Morton's
suggestions.
From: Nye Liu <nyet@nyet.org>
When unpacking the cpio into the initramfs, mtimes are not preserved by
default. This patch adds an INITRAMFS_PRESERVE_MTIME option that allows mtimes
stored in the cpio image to be used when constructing the initramfs. For
embedded applications that run exclusively out of the initramfs, this is
invaluable.
Signed-off-by: Nye Liu <nyet@nyet.org>
diff --git a/init/initramfs.c b/init/initramfs.c
index 644fc01..ebfc049 100644
--- a/init/initramfs.c
+++ b/init/initramfs.c
@@ -6,6 +6,7 @@
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/syscalls.h>
+#include <linux/utime.h>
static __initdata char *message;
static void __init error(char *x)
@@ -72,6 +73,38 @@ static void __init free_hash(void)
}
}
+static __initdata LIST_HEAD(dir_list);
+struct dir_entry {
+ struct list_head list;
+ char *name;
+ struct utimbuf mtime;
+};
+
+static void __init dir_add(const char *name, struct utimbuf mtime)
+{
+ struct dir_entry *de = kmalloc(sizeof(struct dir_entry), GFP_KERNEL);
+ if (!de)
+ panic("can't allocate dir_entry buffer");
+ INIT_LIST_HEAD(&de->list);
+ de->name = kstrdup(name, GFP_KERNEL);
+ de->mtime = mtime;
+ list_add(&de->list, &dir_list);
+}
+
+static void __init dir_utime(void)
+{
+ struct list_head *e, *tmp;
+ list_for_each_safe(e, tmp, &dir_list) {
+ struct dir_entry *de = list_entry(e, struct dir_entry, list);
+ list_del(e);
+ sys_utime(de->name, &de->mtime);
+ kfree(de->name);
+ kfree(de);
+ }
+}
+
+static __initdata struct utimbuf mtime;
+
/* cpio header parsing */
static __initdata unsigned long ino, major, minor, nlink;
@@ -97,6 +130,7 @@ static void __init parse_header(char *s)
uid = parsed[2];
gid = parsed[3];
nlink = parsed[4];
+ mtime.actime = mtime.modtime = parsed[5];
body_len = parsed[6];
major = parsed[7];
minor = parsed[8];
@@ -130,6 +164,7 @@ static inline ...On Wed, 3 Sep 2008 13:29:38 -0700 It's nice to provide some sort of accounting for the reviewer's individual comments. But in this case I only had three or fopur and I Why is it "invlauable". Please explain this value in full detail - gargh. Why does this work? It's normally a big fail to pass a kernel address into a system call. I guess we're running under KERNEL_DS here and getname() and strncpy_from_user() did the right thing. On what CPU architecture was this tested? Wouldn't it be simpler to put a timespec into struct dir_entry then go Perhaps this is the simplest implementation - I didn't check the fine details. What's your thinking here? --
No, if i understand your question correctly. I have to do the mtime The main problem is that i need to save off the entire list for later processing of the directory mtimes... if i process the directory mtimes in the same pass as the file/link mtimes, touching the directory inode when creating/modifying the file/links updates the directory mtime, and overwrites whatever mtime i set the directory to when i created it. The only solution is to do a two pass, which is why the list is necessary. If there is a better way, i did not find it :( It could be that i misunderstood your question though :) --
On Wed, 3 Sep 2008 15:41:31 -0700 I'm wondering whether this code need to use `struct utimbuf' at all. Or at least, as much as it does. utimbuf is more a userspace-facing thing whereas in-kernel timespecs and timevals are more common. The code as you have it does a fair few conversions into utimbuf format (both directly and via the existing functions which it calls). Would it be simpler if it dealt in terms of timespecs? --
It could be. Heck, it would be even simpler to just use a single time_t (since
cpio doesn't have timespecs OR seperate atime/mtimes):
static __initdata LIST_HEAD(dir_list);
struct dir_entry {
struct list_head list;
char *name;
time_t mtime;
};
then make a simple wrapper for sys_utime() to convert the time_t into a the
utimbuf:
static void __init do_utime(char *name, time_t mtime)
{
struct utimbuf time;
time.actime=time.modtime=mtime;
sys_utime(name, &time);
}
I can try it that way and resubmit. Gimme a bit to compose a patch
ALSO it disturbs me about the alloc problem you mentioned, especially since
i did NOT TEST on anything but our embedded (ppc/powerpc) target.
--
Nye Liu
nliu@mrv.com
(818) 772-6235x248
(818) 772-0576 fax
"Who would be stupid enough to quote a fictitious character?"
-- Don Quixote
--
maybe something like this:
diff --git a/init/initramfs.c b/init/initramfs.c
index 644fc01..89d3e53 100644
--- a/init/initramfs.c
+++ b/init/initramfs.c
@@ -6,6 +6,7 @@
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/syscalls.h>
+#include <linux/utime.h>
static __initdata char *message;
static void __init error(char *x)
@@ -72,6 +73,45 @@ static void __init free_hash(void)
}
}
+static __initdata LIST_HEAD(dir_list);
+struct dir_entry {
+ struct list_head list;
+ char *name;
+ time_t mtime;
+};
+
+static void __init dir_add(const char *name, time_t mtime)
+{
+ struct dir_entry *de = kmalloc(sizeof(struct dir_entry), GFP_KERNEL);
+ if (!de)
+ panic("can't allocate dir_entry buffer");
+ INIT_LIST_HEAD(&de->list);
+ de->name = kstrdup(name, GFP_KERNEL);
+ de->mtime = mtime;
+ list_add(&de->list, &dir_list);
+}
+
+static void __init do_utime(char *name, time_t mtime)
+{
+ struct utimbuf time;
+ time.actime=time.modtime=mtime;
+ sys_utime(name, &time);
+}
+
+static void __init dir_utime(void)
+{
+ struct list_head *e, *tmp;
+ list_for_each_safe(e, tmp, &dir_list) {
+ struct dir_entry *de = list_entry(e, struct dir_entry, list);
+ list_del(e);
+ do_utime(de->name, de->mtime);
+ kfree(de->name);
+ kfree(de);
+ }
+}
+
+static __initdata time_t mtime;
+
/* cpio header parsing */
static __initdata unsigned long ino, major, minor, nlink;
@@ -97,6 +137,7 @@ static void __init parse_header(char *s)
uid = parsed[2];
gid = parsed[3];
nlink = parsed[4];
+ mtime = parsed[5];
body_len = parsed[6];
major = parsed[7];
minor = parsed[8];
@@ -130,6 +171,7 @@ static inline void __init eat(unsigned n)
count -= n;
}
+static __initdata char *vcollected;
static __initdata char *collected;
static __initdata int remains;
static __initdata char *collect;
@@ -271,6 +313,7 @@ static int __init do_name(void)
if (wfd >= 0) {
sys_fchown(wfd, uid, gid);
sys_fchmod(wfd, ...or maybe this, this one ONLY using do_utimes() and a single wrapper
to convert the time_t
diff --git a/init/initramfs.c b/init/initramfs.c
index 644fc01..1360a67 100644
--- a/init/initramfs.c
+++ b/init/initramfs.c
@@ -6,6 +6,7 @@
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/syscalls.h>
+#include <linux/utime.h>
static __initdata char *message;
static void __init error(char *x)
@@ -72,6 +73,51 @@ static void __init free_hash(void)
}
}
+static long __init do_utime(char __user *filename,
+ time_t mtime)
+{
+ struct timespec t[2];
+
+ t[0].tv_sec = mtime;
+ t[0].tv_nsec = 0;
+ t[1].tv_sec = mtime;
+ t[1].tv_nsec = 0;
+
+ return do_utimes(AT_FDCWD, filename, t, AT_SYMLINK_NOFOLLOW);
+}
+
+static __initdata LIST_HEAD(dir_list);
+struct dir_entry {
+ struct list_head list;
+ char *name;
+ time_t mtime;
+};
+
+static void __init dir_add(const char *name, time_t mtime)
+{
+ struct dir_entry *de = kmalloc(sizeof(struct dir_entry), GFP_KERNEL);
+ if (!de)
+ panic("can't allocate dir_entry buffer");
+ INIT_LIST_HEAD(&de->list);
+ de->name = kstrdup(name, GFP_KERNEL);
+ de->mtime = mtime;
+ list_add(&de->list, &dir_list);
+}
+
+static void __init dir_utime(void)
+{
+ struct list_head *e, *tmp;
+ list_for_each_safe(e, tmp, &dir_list) {
+ struct dir_entry *de = list_entry(e, struct dir_entry, list);
+ list_del(e);
+ do_utime(de->name, de->mtime);
+ kfree(de->name);
+ kfree(de);
+ }
+}
+
+static __initdata time_t mtime;
+
/* cpio header parsing */
static __initdata unsigned long ino, major, minor, nlink;
@@ -97,6 +143,7 @@ static void __init parse_header(char *s)
uid = parsed[2];
gid = parsed[3];
nlink = parsed[4];
+ mtime = parsed[5];
body_len = parsed[6];
major = parsed[7];
minor = parsed[8];
@@ -130,6 +177,7 @@ static inline void __init eat(unsigned n)
count -= n;
}
+static __initdata char *vcollected;
static __initdata char *collected;
static __initdata int remains;
static ...On Wed, 3 Sep 2008 16:04:30 -0700 Sub-second information is lost. It'd be nice to preserve it if we're Alas, kstrdup() can fail too. It's all a bit silly checking for kmalloc() failures at this stage in the boot process. Particularly if all we can do is panic - we might as well blunder ahead and dereference the NULL pointer, which gives us just as much info as the panic. And there's not much point in handling the allocation and continuing the boot, because the system obviously won't be booting. So it'd be understandable to just omit the error-checking here, despite --
Hard to avoid, unless you want me composing the following every place
static void __init dir_utime(void)
{
struct dir_entry *de, *tmp;
list_for_each_entry_safe(de, tmp, &dir_list, list) {
list_del(de);
do_utime(de->name, de->mtime);
kfree(de->name);
kfree(de);
}
}
--
Nye Liu
nliu@mrv.com
(818) 772-6235x248
(818) 772-0576 fax
"Who would be stupid enough to quote a fictitious character?"
-- Don Quixote
--
When building embedded application initramfs images, its nice to know when the files were actually created during the build process - that makes it easier to see what files were modified when so we can compare the files that are being used on the image with the files used during the build process. This might help (for example) to determine if the target system has all the updated files you expect to see w/o having to check MD5s etc. In our environment, the whole system runs off the initramfs partition, and seeing the modified times of the shared libraries (for example) helps us find bugs that may have been introduced by the build system incorrectly propogating outdated shared libraries into the image. Similarly, many of the initializion/configuration files in /etc might be dynamically built by the build system, and knowing when they were modified helps us sanity check whether the target system has the "latest" files etc. Finally, we might use last modified times to determine whether a hot fix should be applied or not to the running ramfs. -- Nye Liu nliu@mrv.com (818) 772-6235x248 (818) 772-0576 fax "Who would be stupid enough to quote a fictitious character?" -- Don Quixote --
On Wed, 3 Sep 2008 15:31:14 -0700 Did you see this stuff? --
Hm. "Invaluable" != "nice to know". What worries me is that this code is executed at boot time (when populating the ramfs). For embedded systems a fast boot time is often important. I admit that the net effect of this on boot time is marginal (but some might consider having mtime a marginal benefit), and 100 cents also make a dollar, so my suggestion would be to either reject this patch or make it optional (e.g. depending on some debug config flag). Best regards, Frans. --
An earlier rev patch made it optional. I can resubmit of course. --
Well, I guess it's as "invaluable" for embedded systems running on initramfs as mtime on regular filesystems in case of normal systems. It depends on how marginal is it. If it makes unpacking 1% slower I can see no need for being optional. 10% - maybe it should? -- Krzysztof Halasa --
