# HG changeset patch # User Pascal Bellard # Date 1417705790 -3600 # Node ID 597fc2eb6392b17f2e290e5d573cdc1db097bdc8 # Parent e2056bc81ae50e1366fbb6c7599fd9ced573091c linux-uml: hardlink on fifos, sockets, char & block devices and symlinks in initramfs diff -r e2056bc81ae5 -r 597fc2eb6392 linux-uml/receipt --- a/linux-uml/receipt Thu Dec 04 14:08:30 2014 +0200 +++ b/linux-uml/receipt Thu Dec 04 16:09:50 2014 +0100 @@ -27,6 +27,7 @@ xzcat $SRC/$(basename $PATCH) | patch -Np1 touch done.patch-$VERSION fi + patch -p1 < $stuff/linux-hardlinks.u sed -i 's/uname -m/echo i386/;s|/bin/bash|/bin/ash|g' Makefile make ARCH=um mrproper cat > mini.config << EOF && diff -r e2056bc81ae5 -r 597fc2eb6392 linux-uml/stuff/linux-hardlinks.u --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/linux-uml/stuff/linux-hardlinks.u Thu Dec 04 16:09:50 2014 +0100 @@ -0,0 +1,244 @@ +Fix hardlink on fifos, sockets, char & block devices and symlinks +--- linux-3.2.53/init/initramfs.c ++++ linux-3.2.53/init/initramfs.c +@@ -20,10 +20,24 @@ + + #define N_ALIGN(len) ((((len) + 1) & ~3) + 2) + ++static __initdata unsigned long ino, major, minor, nlink; ++static __initdata time_t mtime; ++static __initdata mode_t mode; ++static __initdata unsigned long body_len, name_len; ++static __initdata uid_t uid; ++static __initdata gid_t gid; ++static __initdata unsigned rdev; ++ ++struct names { ++ struct names *next; ++ char name[1]; ++}; ++ + static __initdata struct hash { +- int ino, minor, major; ++ int ino, nlink, minor, major; + mode_t mode; + struct hash *next; ++ struct names *next_name; + char name[N_ALIGN(PATH_MAX)]; + } *head[32]; + +@@ -34,8 +48,7 @@ + return tmp & 31; + } + +-static char __init *find_link(int major, int minor, int ino, +- mode_t mode, char *name) ++static struct hash __init *find_link(char *name) + { + struct hash **p, *q; + for (p = head + hash(major, minor, ino); *p; p = &(*p)->next) { +@@ -47,7 +60,16 @@ + continue; + if (((*p)->mode ^ mode) & S_IFMT) + continue; +- return (*p)->name; ++ if (--(*p)->nlink) { ++ struct names **n; ++ for (n = &(*p)->next_name; *n; n = &(*n)->next); ++ *n = kmalloc(sizeof(struct names) + strlen(name), GFP_KERNEL); ++ if (!*n) ++ panic("can't allocate link name entry"); ++ strcpy((*n)->name, name); ++ (*n)->next = NULL; ++ } ++ return *p; + } + q = kmalloc(sizeof(struct hash), GFP_KERNEL); + if (!q) +@@ -55,11 +77,13 @@ + q->major = major; + q->minor = minor; + q->ino = ino; ++ q->nlink = nlink; + q->mode = mode; + strcpy(q->name, name); ++ q->next_name = NULL; + q->next = NULL; + *p = q; +- return NULL; ++ return q; + } + + static void __init free_hash(void) +@@ -115,17 +139,8 @@ + } + } + +-static __initdata time_t mtime; +- + /* cpio header parsing */ + +-static __initdata unsigned long ino, major, minor, nlink; +-static __initdata mode_t mode; +-static __initdata unsigned long body_len, name_len; +-static __initdata uid_t uid; +-static __initdata gid_t gid; +-static __initdata unsigned rdev; +- + static void __init parse_header(char *s) + { + unsigned long parsed[12]; +@@ -193,7 +208,7 @@ + } + } + +-static __initdata char *header_buf, *symlink_buf, *name_buf; ++static __initdata char *header_buf, *name_buf; + + static int __init do_start(void) + { +@@ -231,17 +246,9 @@ + state = SkipIt; + if (name_len <= 0 || name_len > PATH_MAX) + return 0; +- if (S_ISLNK(mode)) { +- if (body_len > PATH_MAX) +- return 0; +- collect = collected = symlink_buf; +- remains = N_ALIGN(name_len) + body_len; +- next_state = GotSymlink; +- state = Collect; ++ if (S_ISLNK(mode) && (body_len > PATH_MAX)) + return 0; +- } +- if (S_ISREG(mode) || !body_len) +- read_into(name_buf, N_ALIGN(name_len), GotName); ++ read_into(name_buf, N_ALIGN(name_len), GotName); + return 0; + } + +@@ -269,13 +276,34 @@ + static int __init maybe_link(void) + { + if (nlink >= 2) { +- char *old = find_link(major, minor, ino, mode, collected); +- if (old) +- return (sys_link(old, collected) < 0) ? -1 : 1; ++ struct hash *p = find_link(collected); ++ if (p->nlink >= 2) ++ return 1; + } + return 0; + } + ++static void __init walk_names(struct names *p, char *name) ++{ ++ if (!p) ++ return; ++ walk_names(p->next, name); ++ sys_link(name, p->name); ++ kfree(p); ++} ++ ++static void __init make_links(char *name) ++{ ++ if (nlink >= 2) { ++ struct hash *p = find_link(name); ++ if (!p->nlink) { ++ walk_names(p->next_name, name); ++ sys_link(name, p->name); ++ p->next_name = NULL; ++ } ++ } ++} ++ + static void __init clean_path(char *path, mode_t mode) + { + struct stat st; +@@ -300,11 +328,8 @@ + } + clean_path(collected, mode); + if (S_ISREG(mode)) { +- int ml = maybe_link(); +- if (ml >= 0) { +- int openflags = O_WRONLY|O_CREAT; +- if (ml != 1) +- openflags |= O_TRUNC; ++ if (maybe_link() == 0) { ++ int openflags = O_WRONLY|O_CREAT|O_TRUNC; + wfd = sys_open(collected, openflags, mode); + + if (wfd >= 0) { +@@ -316,6 +341,11 @@ + state = CopyFile; + } + } ++ } else if (S_ISLNK(mode)) { ++ if (maybe_link() == 0) { ++ vcollected = kstrdup(collected, GFP_KERNEL); ++ state = GotSymlink; ++ } + } else if (S_ISDIR(mode)) { + sys_mkdir(collected, mode); + sys_chown(collected, uid, gid); +@@ -328,6 +358,7 @@ + sys_chown(collected, uid, gid); + sys_chmod(collected, mode); + do_utime(collected, mtime); ++ make_links(collected); + } + } + return 0; +@@ -339,6 +370,7 @@ + sys_write(wfd, victim, body_len); + sys_close(wfd); + do_utime(vcollected, mtime); ++ make_links(vcollected); + kfree(vcollected); + eat(body_len); + state = SkipIt; +@@ -353,13 +385,18 @@ + + static int __init do_symlink(void) + { +- collected[N_ALIGN(name_len) + body_len] = '\0'; +- clean_path(collected, 0); +- sys_symlink(collected + N_ALIGN(name_len), collected); +- sys_lchown(collected, uid, gid); +- do_utime(collected, mtime); ++ char c = victim[body_len]; ++ ++ victim[body_len] = '\0'; ++ clean_path(vcollected, 0); ++ sys_symlink(victim, vcollected); ++ victim[body_len] = c; ++ sys_lchown(vcollected, uid, gid); ++ do_utime(vcollected, mtime); ++ make_links(vcollected); ++ kfree(vcollected); ++ eat(body_len); + state = SkipIt; +- next_state = Reset; + return 0; + } + +@@ -419,10 +456,9 @@ + static __initdata char msg_buf[64]; + + header_buf = kmalloc(110, GFP_KERNEL); +- symlink_buf = kmalloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1, GFP_KERNEL); + name_buf = kmalloc(N_ALIGN(PATH_MAX), GFP_KERNEL); + +- if (!header_buf || !symlink_buf || !name_buf) ++ if (!header_buf || !name_buf) + panic("can't allocate buffers"); + + state = Start; +@@ -467,7 +503,6 @@ + } + dir_utime(); + kfree(name_buf); +- kfree(symlink_buf); + kfree(header_buf); + return message; + }