Messages in this thread Patch in this message | | | Date | Sat, 9 Jan 1999 23:43:42 -0700 (MST) | From | Brad Midgley <> | Subject | sysctl real-root-dev patch |
| |
hello...
I've shot this past Alan and the linux-ppc group already. Hoping it be incorporated. Comments?
---
Reading or writing /proc/sys/kernel/real-root-dev does not work on big-endian architectures. I propose a multiplatform solution.
The reason it doesn't work is that sysctl is told to treat the corresponding variable, real_root_dev, as if it's an int. It's actually of type kdev_t which is ultimately an unsigned short int. Treating it like an int only works on x86 because of (unfortunate?) alignment coincidence. Even on intel where this code works, two adjacent bytes that don't really belong to real_root_dev are being read and modified as if they do.
This code needs to work on all platforms because it is extremely important for makers of linux distributions to offer convenient startup and installation options.
As I see it there are three ways to fix the problem:
1. Change real_root_dev to type int (ugly, it really should be a kdev_t)
2. Change the definition of kdev_t from an unsigned short to an int (ugly, affects large amounts of code)
3. make sysctl treat real_root_dev properly as it is.
I show an implementation for (3). I think it's the best solution and it's the most localized solution.
All my changes are in kernel/sysctl.c. The version is 2.2.0p4.
First I made the change to have the real size of real_root_dev reflected in its entry in the table:
-{KERN_REALROOTDEV, "real-root-dev", &real_root_dev, sizeof(int), +{KERN_REALROOTDEV, "real-root-dev", &real_root_dev, sizeof(real_root_dev),
If the size of kdev_t ever does change to a whole int or multiple ints, the sysctl code won't need changes. (there's an assumption that userland programs understand how real_root_dev is represented but that's an entirely different problem.)
Next I make the code that parses and outputs values able to deal with a data chunk smaller than an int. I have it assume that a chunk of data smaller than an int is an unsigned short. All the other entries in the sysctl table report their size as multiples of sizeof(int) so this doesn't affect any other entries.
vleft now represents the number of bytes, not ints, that need to be filled:
- vleft = table->maxlen / sizeof(int); + vleft = table->maxlen; left = *lenp; - for (; left && vleft--; i++, first=0) { + for (; left && vleft > 0; i++, first=0) { + vleft -= sizeof(int); if (write) { ... left -= len; - *i = val; + if(vleft >= 0) + *i = val; + else + *((unsigned short *)i) = val; } else { p = buf; if (!first) *p++ = '\t'; - sprintf(p, "%d", (*i) / conv); + if(vleft >= 0) + sprintf(p, "%d", (*i) / conv); + else + sprintf(p, "%hd", (*((unsigned short *)i)) / (unsigned short)conv); len = strlen(buf); if (len > left) len = left;
a proper patch is attached.
brad --- kernel/sysctl.c.orig Mon Jan 4 20:02:02 1999 +++ kernel/sysctl.c Tue Jan 5 07:42:28 1999 @@ -162,7 +162,7 @@ {KERN_PANIC, "panic", &panic_timeout, sizeof(int), 0644, NULL, &proc_dointvec}, #ifdef CONFIG_BLK_DEV_INITRD - {KERN_REALROOTDEV, "real-root-dev", &real_root_dev, sizeof(int), + {KERN_REALROOTDEV, "real-root-dev", &real_root_dev, sizeof(real_root_dev), 0644, NULL, &proc_dointvec}, #endif #ifdef CONFIG_BINFMT_JAVA @@ -688,10 +688,11 @@ } i = (int *) table->data; - vleft = table->maxlen / sizeof(int); + vleft = table->maxlen; left = *lenp; - for (; left && vleft--; i++, first=0) { + for (; left && vleft > 0; i++, first=0) { + vleft -= sizeof(int); if (write) { while (left) { char c; @@ -726,12 +727,18 @@ val = -val; buffer += len; left -= len; - *i = val; + if(vleft >= 0) + *i = val; + else + *((unsigned short *)i) = val; } else { p = buf; if (!first) *p++ = '\t'; - sprintf(p, "%d", (*i) / conv); + if(vleft >= 0) + sprintf(p, "%d", (*i) / conv); + else + sprintf(p, "%hd", (*((unsigned short *)i)) / (unsigned short)conv); len = strlen(buf); if (len > left) len = left; | |