diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2006-12-27 04:35:04 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2006-12-27 04:35:04 +0000 |
commit | 8d42f86b146871ae4c4cafd3801a85f381249a14 (patch) | |
tree | b963999fc54eddb65f1929b894f868e24851fc9c /util-linux | |
download | busybox-w32-8d42f86b146871ae4c4cafd3801a85f381249a14.tar.gz busybox-w32-8d42f86b146871ae4c4cafd3801a85f381249a14.tar.bz2 busybox-w32-8d42f86b146871ae4c4cafd3801a85f381249a14.zip |
Correcting branch name to be like previous ones
Diffstat (limited to 'util-linux')
30 files changed, 13695 insertions, 0 deletions
diff --git a/util-linux/Config.in b/util-linux/Config.in new file mode 100644 index 000000000..848914d6f --- /dev/null +++ b/util-linux/Config.in | |||
@@ -0,0 +1,526 @@ | |||
1 | # | ||
2 | # For a description of the syntax of this configuration file, | ||
3 | # see scripts/kbuild/config-language.txt. | ||
4 | # | ||
5 | |||
6 | menu "Linux System Utilities" | ||
7 | |||
8 | config DMESG | ||
9 | bool "dmesg" | ||
10 | default n | ||
11 | help | ||
12 | dmesg is used to examine or control the kernel ring buffer. When the | ||
13 | Linux kernel prints messages to the system log, they are stored in | ||
14 | the kernel ring buffer. You can use dmesg to print the kernel's ring | ||
15 | buffer, clear the kernel ring buffer, change the size of the kernel | ||
16 | ring buffer, and change the priority level at which kernel messages | ||
17 | are also logged to the system console. Enable this option if you | ||
18 | wish to enable the 'dmesg' utility. | ||
19 | |||
20 | config FEATURE_DMESG_PRETTY | ||
21 | bool "pretty dmesg output" | ||
22 | default y | ||
23 | depends on DMESG | ||
24 | help | ||
25 | If you wish to scrub the syslog level from the output, say 'Y' here. | ||
26 | The syslog level is a string prefixed to every line with the form "<#>". | ||
27 | |||
28 | With this option you will see: | ||
29 | # dmesg | ||
30 | Linux version 2.6.17.4 ..... | ||
31 | BIOS-provided physical RAM map: | ||
32 | BIOS-e820: 0000000000000000 - 000000000009f000 (usable) | ||
33 | |||
34 | Without this option you will see: | ||
35 | # dmesg | ||
36 | <5>Linux version 2.6.17.4 ..... | ||
37 | <6>BIOS-provided physical RAM map: | ||
38 | <6> BIOS-e820: 0000000000000000 - 000000000009f000 (usable) | ||
39 | |||
40 | config FBSET | ||
41 | bool "fbset" | ||
42 | default n | ||
43 | help | ||
44 | fbset is used to show or change the settings of a Linux frame buffer | ||
45 | device. The frame buffer device provides a simple and unique | ||
46 | interface to access a graphics display. Enable this option | ||
47 | if you wish to enable the 'fbset' utility. | ||
48 | |||
49 | config FEATURE_FBSET_FANCY | ||
50 | bool "Turn on extra fbset options" | ||
51 | default n | ||
52 | depends on FBSET | ||
53 | help | ||
54 | This option enables extended fbset options, allowing one to set the | ||
55 | framebuffer size, color depth, etc. interface to access a graphics | ||
56 | display. Enable this option if you wish to enable extended fbset | ||
57 | options. | ||
58 | |||
59 | config FEATURE_FBSET_READMODE | ||
60 | bool "Turn on fbset readmode support" | ||
61 | default n | ||
62 | depends on FBSET | ||
63 | help | ||
64 | This option allows fbset to read the video mode database stored by | ||
65 | default as /etc/fb.modes, which can be used to set frame buffer | ||
66 | device to pre-defined video modes. | ||
67 | |||
68 | config FDFLUSH | ||
69 | bool "fdflush" | ||
70 | default n | ||
71 | help | ||
72 | fdflush is only needed when changing media on slightly-broken | ||
73 | removable media drives. It is used to make Linux believe that a | ||
74 | hardware disk-change switch has been actuated, which causes Linux to | ||
75 | forget anything it has cached from the previous media. If you have | ||
76 | such a slightly-broken drive, you will need to run fdflush every time | ||
77 | you change a disk. Most people have working hardware and can safely | ||
78 | leave this disabled. | ||
79 | |||
80 | config FDFORMAT | ||
81 | bool "fdformat" | ||
82 | default n | ||
83 | help | ||
84 | fdformat is used to low-level format a floppy disk. | ||
85 | |||
86 | config FDISK | ||
87 | bool "fdisk" | ||
88 | default n | ||
89 | help | ||
90 | The fdisk utility is used to divide hard disks into one or more | ||
91 | logical disks, which are generally called partitions. This utility | ||
92 | can be used to list and edit the set of partitions or BSD style | ||
93 | 'disk slices' that are defined on a hard drive. | ||
94 | |||
95 | config FDISK_SUPPORT_LARGE_DISKS | ||
96 | bool "support over 4GB disks" | ||
97 | default y | ||
98 | depends on FDISK | ||
99 | help | ||
100 | Enable this option to support large disks > 4GB. | ||
101 | |||
102 | config FEATURE_FDISK_WRITABLE | ||
103 | bool "Write support" | ||
104 | default y | ||
105 | depends on FDISK | ||
106 | help | ||
107 | Enabling this option allows you to create or change a partition table | ||
108 | and write those changes out to disk. If you leave this option | ||
109 | disabled, you will only be able to view the partition table. | ||
110 | |||
111 | config FEATURE_AIX_LABEL | ||
112 | bool "Support AIX disklabels" | ||
113 | default n | ||
114 | depends on FDISK && FEATURE_FDISK_WRITABLE | ||
115 | help | ||
116 | Enabling this option allows you to create or change AIX disklabels. | ||
117 | Most people can safely leave this option disabled. | ||
118 | |||
119 | config FEATURE_SGI_LABEL | ||
120 | bool "Support SGI disklabels" | ||
121 | default n | ||
122 | depends on FDISK && FEATURE_FDISK_WRITABLE | ||
123 | help | ||
124 | Enabling this option allows you to create or change SGI disklabels. | ||
125 | Most people can safely leave this option disabled. | ||
126 | |||
127 | config FEATURE_SUN_LABEL | ||
128 | bool "Support SUN disklabels" | ||
129 | default n | ||
130 | depends on FDISK && FEATURE_FDISK_WRITABLE | ||
131 | help | ||
132 | Enabling this option allows you to create or change SUN disklabels. | ||
133 | Most people can safely leave this option disabled. | ||
134 | |||
135 | config FEATURE_OSF_LABEL | ||
136 | bool "Support BSD disklabels" | ||
137 | default n | ||
138 | depends on FDISK && FEATURE_FDISK_WRITABLE | ||
139 | help | ||
140 | Enabling this option allows you to create or change BSD disklabels | ||
141 | and define and edit BSD disk slices. | ||
142 | |||
143 | config FEATURE_FDISK_ADVANCED | ||
144 | bool "Support expert mode" | ||
145 | default n | ||
146 | depends on FDISK && FEATURE_FDISK_WRITABLE | ||
147 | help | ||
148 | Enabling this option allows you to do terribly unsafe things like | ||
149 | define arbitrary drive geometry, move the beginning of data in a | ||
150 | partition, and similarly evil things. Unless you have a very good | ||
151 | reason you would be wise to leave this disabled. | ||
152 | |||
153 | config FREERAMDISK | ||
154 | bool "freeramdisk" | ||
155 | default n | ||
156 | help | ||
157 | Linux allows you to create ramdisks. This utility allows you to | ||
158 | delete them and completely free all memory that was used for the | ||
159 | ramdisk. For example, if you boot Linux into a ramdisk and later | ||
160 | pivot_root, you may want to free the memory that is allocated to the | ||
161 | ramdisk. If you have no use for freeing memory from a ramdisk, leave | ||
162 | this disabled. | ||
163 | |||
164 | config FSCK_MINIX | ||
165 | bool "fsck_minix" | ||
166 | default n | ||
167 | help | ||
168 | The minix filesystem is a nice, small, compact, read-write filesystem | ||
169 | with little overhead. It is not a journaling filesystem however and | ||
170 | can experience corruption if it is not properly unmounted or if the | ||
171 | power goes off in the middle of a write. This utility allows you to | ||
172 | check for and attempt to repair any corruption that occurs to a minix | ||
173 | filesystem. | ||
174 | |||
175 | config MKFS_MINIX | ||
176 | bool "mkfs_minix" | ||
177 | default n | ||
178 | help | ||
179 | The minix filesystem is a nice, small, compact, read-write filesystem | ||
180 | with little overhead. If you wish to be able to create minix filesystems | ||
181 | this utility will do the job for you. | ||
182 | |||
183 | comment "Minix filesystem support" | ||
184 | depends on FSCK_MINIX || MKFS_MINIX | ||
185 | |||
186 | config FEATURE_MINIX2 | ||
187 | bool "Support Minix fs v2 (fsck_minix/mkfs_minix)" | ||
188 | default y | ||
189 | depends on FSCK_MINIX || MKFS_MINIX | ||
190 | help | ||
191 | If you wish to be able to create version 2 minix filesystems, enable this. | ||
192 | If you enabled 'mkfs_minix' then you almost certainly want to be using the | ||
193 | version 2 filesystem support. | ||
194 | |||
195 | config GETOPT | ||
196 | bool "getopt" | ||
197 | default n | ||
198 | help | ||
199 | The getopt utility is used to break up (parse) options in command | ||
200 | lines to make it easy to write complex shell scripts that also check | ||
201 | for legal (and illegal) options. If you want to write horribly | ||
202 | complex shell scripts, or use some horribly complex shell script | ||
203 | written by others, this utility may be for you. Most people will | ||
204 | wisely leave this disabled. | ||
205 | |||
206 | config HEXDUMP | ||
207 | bool "hexdump" | ||
208 | default n | ||
209 | help | ||
210 | The hexdump utility is used to display binary data in a readable | ||
211 | way that is comparable to the output from most hex editors. | ||
212 | |||
213 | config HWCLOCK | ||
214 | bool "hwclock" | ||
215 | default n | ||
216 | help | ||
217 | The hwclock utility is used to read and set the hardware clock | ||
218 | on a system. This is primarily used to set the current time on | ||
219 | shutdown in the hardware clock, so the hardware will keep the | ||
220 | correct time when Linux is _not_ running. | ||
221 | |||
222 | config FEATURE_HWCLOCK_LONG_OPTIONS | ||
223 | bool "Support long options (--hctosys,...)" | ||
224 | default n | ||
225 | depends on HWCLOCK && GETOPT_LONG | ||
226 | help | ||
227 | By default, the hwclock utility only uses short options. If you | ||
228 | are overly fond of its long options, such as --hctosys, --utc, etc) | ||
229 | then enable this option. | ||
230 | |||
231 | config FEATURE_HWCLOCK_ADJTIME_FHS | ||
232 | bool "Use FHS /var/lib/hwclock/adjtime" | ||
233 | default y | ||
234 | depends on HWCLOCK | ||
235 | help | ||
236 | Starting with FHS 2.3, the adjtime state file is supposed to exist | ||
237 | at /var/lib/hwclock/adjtime instead of /etc/adjtime. If you wish | ||
238 | to use the FHS behavior, answer Y here, otherwise answer N for the | ||
239 | classic /etc/adjtime path. | ||
240 | |||
241 | http://www.pathname.com/fhs/pub/fhs-2.3.html#VARLIBHWCLOCKSTATEDIRECTORYFORHWCLO | ||
242 | |||
243 | config IPCRM | ||
244 | bool "ipcrm" | ||
245 | default n | ||
246 | select FEATURE_SUID | ||
247 | help | ||
248 | The ipcrm utility allows the removal of System V interprocess | ||
249 | communication (IPC) objects and the associated data structures | ||
250 | from the system. | ||
251 | |||
252 | config IPCS | ||
253 | bool "ipcs" | ||
254 | default n | ||
255 | select FEATURE_SUID | ||
256 | help | ||
257 | The ipcs utility is used to provide information on the currently | ||
258 | allocated System V interprocess (IPC) objects in the system. | ||
259 | |||
260 | config LOSETUP | ||
261 | bool "losetup" | ||
262 | default n | ||
263 | help | ||
264 | losetup is used to associate or detach a loop device with a regular | ||
265 | file or block device, and to query the status of a loop device. This | ||
266 | version does not currently support enabling data encryption. | ||
267 | |||
268 | config MDEV | ||
269 | bool "mdev" | ||
270 | default n | ||
271 | help | ||
272 | mdev is a mini-udev implementation: call it with -s to populate | ||
273 | /dev from /sys, then "echo /sbin/mdev > /proc/sys/kernel/hotplug" to | ||
274 | have it handle hotplug events afterwards. Device names are taken | ||
275 | from sysfs. | ||
276 | |||
277 | config FEATURE_MDEV_CONF | ||
278 | bool "Support /etc/mdev.conf" | ||
279 | default n | ||
280 | depends on MDEV | ||
281 | help | ||
282 | The mdev config file contains lines that look like: | ||
283 | |||
284 | hd[a-z][0-9]* 0:3 660 | ||
285 | |||
286 | That's device name (with regex match), uid:gid, and permissions. | ||
287 | |||
288 | Config file parsing stops on the first matching line. If no config | ||
289 | entry is matched, devices are created with default 0:0 660. (Make | ||
290 | the last line match .* to override this.) | ||
291 | |||
292 | config FEATURE_MDEV_EXEC | ||
293 | bool "Support command execution at device addition/removal" | ||
294 | default n | ||
295 | depends on FEATURE_MDEV_CONF | ||
296 | help | ||
297 | This adds support for an optional field to /etc/mdev.conf, consisting | ||
298 | of a special character and a command line to run after creating the | ||
299 | corresponding device(s) and before removing, ala: | ||
300 | |||
301 | hdc root:cdrom 660 *ln -s $MDEV cdrom | ||
302 | |||
303 | The $MDEV environment variable is set to the name of the device. | ||
304 | |||
305 | The special characters and their meanings are: | ||
306 | @ Run after creating the device. | ||
307 | $ Run before removing the device. | ||
308 | * Run both after creating and before removing the device. | ||
309 | |||
310 | Commands are executed via system() so you need /bin/sh, meaning you | ||
311 | probably want to select a default shell in the Shells menu. | ||
312 | |||
313 | config MKSWAP | ||
314 | bool "mkswap" | ||
315 | default n | ||
316 | help | ||
317 | The mkswap utility is used to configure a file or disk partition as | ||
318 | Linux swap space. This allows Linux to use the entire file or | ||
319 | partition as if it were additional RAM, which can greatly increase | ||
320 | the capability of low-memory machines. This additional memory is | ||
321 | much slower than real RAM, but can be very helpful at preventing your | ||
322 | applications being killed by the Linux out of memory (OOM) killer. | ||
323 | Once you have created swap space using 'mkswap' you need to enable | ||
324 | the swap space using the 'swapon' utility. | ||
325 | |||
326 | config FEATURE_MKSWAP_V0 | ||
327 | bool "version 0 support" | ||
328 | default n | ||
329 | depends on MKSWAP | ||
330 | # depends on MKSWAP && DEPRECATED | ||
331 | help | ||
332 | Enable support for the old v0 style. | ||
333 | If your kernel is older than 2.1.117, then v0 support is the | ||
334 | only option. | ||
335 | |||
336 | config MORE | ||
337 | bool "more" | ||
338 | default n | ||
339 | help | ||
340 | more is a simple utility which allows you to read text one screen | ||
341 | sized page at a time. If you want to read text that is larger than | ||
342 | the screen, and you are using anything faster than a 300 baud modem, | ||
343 | you will probably find this utility very helpful. If you don't have | ||
344 | any need to reading text files, you can leave this disabled. | ||
345 | |||
346 | config FEATURE_USE_TERMIOS | ||
347 | bool "Use termios to manipulate the screen" | ||
348 | default y | ||
349 | depends on MORE | ||
350 | help | ||
351 | This option allows utilities such as 'more' and 'top' to determine | ||
352 | the size of the screen. If you leave this disabled, your utilities | ||
353 | that display things on the screen will be especially primitive and | ||
354 | will be unable to determine the current screen size, and will be | ||
355 | unable to move the cursor. | ||
356 | |||
357 | config MOUNT | ||
358 | bool "mount" | ||
359 | default n | ||
360 | help | ||
361 | All files and filesystems in Unix are arranged into one big directory | ||
362 | tree. The 'mount' utility is used to graft a filesystem onto a | ||
363 | particular part of the tree. A filesystem can either live on a block | ||
364 | device, or it can be accessible over the network, as is the case with | ||
365 | NFS filesystems. Most people using BusyBox will also want to enable | ||
366 | the 'mount' utility. | ||
367 | |||
368 | config FEATURE_MOUNT_NFS | ||
369 | bool "Support mounting NFS file systems" | ||
370 | default n | ||
371 | depends on MOUNT | ||
372 | depends on FEATURE_HAVE_RPC | ||
373 | select FEATURE_SYSLOG | ||
374 | help | ||
375 | Enable mounting of NFS file systems. | ||
376 | |||
377 | config FEATURE_MOUNT_CIFS | ||
378 | bool "Support mounting CIFS/SMB file systems" | ||
379 | default n | ||
380 | depends on MOUNT | ||
381 | help | ||
382 | Enable support for samba mounts. | ||
383 | |||
384 | config FEATURE_MOUNT_FLAGS | ||
385 | depends on MOUNT | ||
386 | bool "Support lots of -o flags in mount" | ||
387 | default y | ||
388 | help | ||
389 | Without this, mount only supports ro/rw/remount. With this, it | ||
390 | supports nosuid, suid, dev, nodev, exec, noexec, sync, async, atime, | ||
391 | noatime, diratime, nodiratime, loud, bind, move, shared, slave, | ||
392 | private, unbindable, rshared, rslave, rprivate, and runbindable. | ||
393 | |||
394 | config FEATURE_MOUNT_FSTAB | ||
395 | depends on MOUNT | ||
396 | bool "Support /etc/fstab and -a" | ||
397 | default y | ||
398 | help | ||
399 | Support mount all and looking for files in /etc/fstab. | ||
400 | |||
401 | config PIVOT_ROOT | ||
402 | bool "pivot_root" | ||
403 | default n | ||
404 | help | ||
405 | The pivot_root utility swaps the mount points for the root filesystem | ||
406 | with some other mounted filesystem. This allows you to do all sorts | ||
407 | of wild and crazy things with your Linux system and is far more | ||
408 | powerful than 'chroot'. | ||
409 | |||
410 | Note: This is for initrd in linux 2.4. Under initramfs (introduced | ||
411 | in linux 2.6) use switch_root instead. | ||
412 | |||
413 | config RDATE | ||
414 | bool "rdate" | ||
415 | default n | ||
416 | help | ||
417 | The rdate utility allows you to synchronize the date and time of your | ||
418 | system clock with the date and time of a remote networked system using | ||
419 | the RFC868 protocol, which is built into the inetd daemon on most | ||
420 | systems. | ||
421 | |||
422 | config READPROFILE | ||
423 | bool "readprofile" | ||
424 | default n | ||
425 | help | ||
426 | This allows you to parse /proc/profile for basic profiling. | ||
427 | |||
428 | config SETARCH | ||
429 | bool "setarch" | ||
430 | default n | ||
431 | help | ||
432 | The linux32 utility is used to create a 32bit environment for the | ||
433 | specified program (usually a shell). It only makes sense to have | ||
434 | this util on a system that supports both 64bit and 32bit userland | ||
435 | (like amd64/x86, ppc64/ppc, sparc64/sparc, etc...). | ||
436 | |||
437 | config SWAPONOFF | ||
438 | bool "swaponoff" | ||
439 | default n | ||
440 | help | ||
441 | This option enables both the 'swapon' and the 'swapoff' utilities. | ||
442 | Once you have created some swap space using 'mkswap', you also need | ||
443 | to enable your swap space with the 'swapon' utility. The 'swapoff' | ||
444 | utility is used, typically at system shutdown, to disable any swap | ||
445 | space. If you are not using any swap space, you can leave this | ||
446 | option disabled. | ||
447 | |||
448 | config SWITCH_ROOT | ||
449 | bool "switch_root" | ||
450 | default n | ||
451 | help | ||
452 | The switch_root utility is used from initramfs to select a new | ||
453 | root device. Under initramfs, you have to use this instead of | ||
454 | pivot_root. (Stop reading here if you don't care why.) | ||
455 | |||
456 | Booting with initramfs extracts a gzipped cpio archive into rootfs | ||
457 | (which is a variant of ramfs/tmpfs). Because rootfs can't be moved | ||
458 | or unmounted*, pivot_root will not work from initramfs. Instead, | ||
459 | switch_root deletes everything out of rootfs (including itself), | ||
460 | does a mount --move that overmounts rootfs with the new root, and | ||
461 | then execs the specified init program. | ||
462 | |||
463 | * Because the Linux kernel uses rootfs internally as the starting | ||
464 | and ending point for searching through the kernel's doubly linked | ||
465 | list of active mount points. That's why. | ||
466 | |||
467 | config UMOUNT | ||
468 | bool "umount" | ||
469 | default n | ||
470 | help | ||
471 | When you want to remove a mounted filesystem from its current mount point, | ||
472 | for example when you are shutting down the system, the 'umount' utility is | ||
473 | the tool to use. If you enabled the 'mount' utility, you almost certainly | ||
474 | also want to enable 'umount'. | ||
475 | |||
476 | config FEATURE_UMOUNT_ALL | ||
477 | bool "umount -a option" | ||
478 | default n | ||
479 | depends on UMOUNT | ||
480 | help | ||
481 | Support -a option to unmount all currently mounted filesystems. | ||
482 | |||
483 | comment "Common options for mount/umount" | ||
484 | depends on MOUNT || UMOUNT | ||
485 | |||
486 | config FEATURE_MOUNT_LOOP | ||
487 | bool "Support loopback mounts" | ||
488 | default n | ||
489 | depends on MOUNT || UMOUNT | ||
490 | help | ||
491 | Enabling this feature allows automatic mounting of files (containing | ||
492 | filesystem images) via the linux kernel's loopback devices. The mount | ||
493 | command will detect you are trying to mount a file instead of a block | ||
494 | device, and transparently associate the file with a loopback device. | ||
495 | The umount command will also free that loopback device. | ||
496 | |||
497 | You can still use the 'losetup' utility (to manually associate files | ||
498 | with loop devices) if you need to do something advanced, such as | ||
499 | specify an offset or cryptographic options to the loopback device. | ||
500 | (If you don't want umount to free the loop device, use "umount -D".) | ||
501 | |||
502 | config FEATURE_MTAB_SUPPORT | ||
503 | bool "Support for the old /etc/mtab file" | ||
504 | default n | ||
505 | depends on MOUNT || UMOUNT | ||
506 | help | ||
507 | Historically, Unix systems kept track of the currently mounted | ||
508 | partitions in the file "/etc/mtab". These days, the kernel exports | ||
509 | the list of currently mounted partitions in "/proc/mounts", rendering | ||
510 | the old mtab file obsolete. (In modern systems, /etc/mtab should be | ||
511 | a symlink to /proc/mounts.) | ||
512 | |||
513 | The only reason to have mount maintain an /etc/mtab file itself is if | ||
514 | your stripped-down embedded system does not have a /proc directory. | ||
515 | If you must use this, keep in mind it's inherently brittle (for | ||
516 | example a mount under chroot won't update it), can't handle modern | ||
517 | features like separate per-process filesystem namespaces, requires | ||
518 | that your /etc directory be writeable, tends to get easily confused | ||
519 | by --bind or --move mounts, won't update if you rename a directory | ||
520 | that contains a mount point, and so on. (In brief: avoid.) | ||
521 | |||
522 | About the only reason to use this is if you've removed /proc from | ||
523 | your kernel. | ||
524 | |||
525 | endmenu | ||
526 | |||
diff --git a/util-linux/Kbuild b/util-linux/Kbuild new file mode 100644 index 000000000..cc1d0e05d --- /dev/null +++ b/util-linux/Kbuild | |||
@@ -0,0 +1,32 @@ | |||
1 | # Makefile for busybox | ||
2 | # | ||
3 | # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org> | ||
4 | # | ||
5 | # Licensed under the GPL v2, see the file LICENSE in this tarball. | ||
6 | |||
7 | lib-y:= | ||
8 | lib-$(CONFIG_DMESG) +=dmesg.o | ||
9 | lib-$(CONFIG_FBSET) +=fbset.o | ||
10 | lib-$(CONFIG_FDFLUSH) +=freeramdisk.o | ||
11 | lib-$(CONFIG_FDFORMAT) +=fdformat.o | ||
12 | lib-$(CONFIG_FDISK) +=fdisk.o | ||
13 | lib-$(CONFIG_FREERAMDISK) +=freeramdisk.o | ||
14 | lib-$(CONFIG_FSCK_MINIX) +=fsck_minix.o | ||
15 | lib-$(CONFIG_GETOPT) +=getopt.o | ||
16 | lib-$(CONFIG_HEXDUMP) +=hexdump.o | ||
17 | lib-$(CONFIG_HWCLOCK) +=hwclock.o | ||
18 | lib-$(CONFIG_IPCRM) +=ipcrm.o | ||
19 | lib-$(CONFIG_IPCS) +=ipcs.o | ||
20 | lib-$(CONFIG_LOSETUP) +=losetup.o | ||
21 | lib-$(CONFIG_MDEV) +=mdev.o | ||
22 | lib-$(CONFIG_MKFS_MINIX) +=mkfs_minix.o | ||
23 | lib-$(CONFIG_MKSWAP) +=mkswap.o | ||
24 | lib-$(CONFIG_MORE) +=more.o | ||
25 | lib-$(CONFIG_MOUNT) +=mount.o | ||
26 | lib-$(CONFIG_PIVOT_ROOT) +=pivot_root.o | ||
27 | lib-$(CONFIG_RDATE) +=rdate.o | ||
28 | lib-$(CONFIG_READPROFILE) +=readprofile.o | ||
29 | lib-$(CONFIG_SETARCH) +=setarch.o | ||
30 | lib-$(CONFIG_SWAPONOFF) +=swaponoff.o | ||
31 | lib-$(CONFIG_SWITCH_ROOT) +=switch_root.o | ||
32 | lib-$(CONFIG_UMOUNT) +=umount.o | ||
diff --git a/util-linux/dmesg.c b/util-linux/dmesg.c new file mode 100644 index 000000000..658cddc38 --- /dev/null +++ b/util-linux/dmesg.c | |||
@@ -0,0 +1,53 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * | ||
4 | * dmesg - display/control kernel ring buffer. | ||
5 | * | ||
6 | * Copyright 2006 Rob Landley <rob@landley.net> | ||
7 | * Copyright 2006 Bernhard Fischer <rep.nop@aon.at> | ||
8 | * | ||
9 | * Licensed under GPLv2, see file LICENSE in this tarball for details. | ||
10 | */ | ||
11 | |||
12 | #include "busybox.h" | ||
13 | #include <unistd.h> | ||
14 | #include <sys/klog.h> | ||
15 | |||
16 | int dmesg_main(int argc, char *argv[]) | ||
17 | { | ||
18 | char *size, *level; | ||
19 | int flags = getopt32(argc, argv, "cs:n:", &size, &level); | ||
20 | |||
21 | if (flags & 4) { | ||
22 | if (klogctl(8, NULL, xatoul_range(level, 0, 10))) | ||
23 | bb_perror_msg_and_die("klogctl"); | ||
24 | } else { | ||
25 | int len; | ||
26 | char *buf; | ||
27 | |||
28 | len = (flags & 2) ? xatoul_range(size, 2, INT_MAX) : 16384; | ||
29 | buf = xmalloc(len); | ||
30 | if (0 > (len = klogctl(3 + (flags & 1), buf, len))) | ||
31 | bb_perror_msg_and_die("klogctl"); | ||
32 | |||
33 | // Skip <#> at the start of lines, and make sure we end with a newline. | ||
34 | |||
35 | if (ENABLE_FEATURE_DMESG_PRETTY) { | ||
36 | int last = '\n'; | ||
37 | int in; | ||
38 | |||
39 | for (in = 0; in<len;) { | ||
40 | if (last == '\n' && buf[in] == '<') in += 3; | ||
41 | else putchar(last = buf[in++]); | ||
42 | } | ||
43 | if (last != '\n') putchar('\n'); | ||
44 | } else { | ||
45 | write(1,buf,len); | ||
46 | if (len && buf[len-1]!='\n') putchar('\n'); | ||
47 | } | ||
48 | |||
49 | if (ENABLE_FEATURE_CLEAN_UP) free(buf); | ||
50 | } | ||
51 | |||
52 | return 0; | ||
53 | } | ||
diff --git a/util-linux/fbset.c b/util-linux/fbset.c new file mode 100644 index 000000000..1aa0a0ac1 --- /dev/null +++ b/util-linux/fbset.c | |||
@@ -0,0 +1,407 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Mini fbset implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 1999 by Randolph Chung <tausq@debian.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | * | ||
9 | * This is a from-scratch implementation of fbset; but the de facto fbset | ||
10 | * implementation was a good reference. fbset (original) is released under | ||
11 | * the GPL, and is (c) 1995-1999 by: | ||
12 | * Geert Uytterhoeven (Geert.Uytterhoeven@cs.kuleuven.ac.be) | ||
13 | */ | ||
14 | |||
15 | #include "busybox.h" | ||
16 | |||
17 | #define DEFAULTFBDEV FB_0 | ||
18 | #define DEFAULTFBMODE "/etc/fb.modes" | ||
19 | |||
20 | enum { | ||
21 | OPT_CHANGE = (1 << 0), | ||
22 | OPT_INFO = (1 << 1), | ||
23 | OPT_READMODE = (1 << 2), | ||
24 | OPT_ALL = (1 << 9), | ||
25 | |||
26 | CMD_FB = 1, | ||
27 | CMD_DB = 2, | ||
28 | CMD_GEOMETRY = 3, | ||
29 | CMD_TIMING = 4, | ||
30 | CMD_ACCEL = 5, | ||
31 | CMD_HSYNC = 6, | ||
32 | CMD_VSYNC = 7, | ||
33 | CMD_LACED = 8, | ||
34 | CMD_DOUBLE = 9, | ||
35 | /* CMD_XCOMPAT = 10, */ | ||
36 | CMD_ALL = 11, | ||
37 | CMD_INFO = 12, | ||
38 | CMD_CHANGE = 13, | ||
39 | |||
40 | #ifdef CONFIG_FEATURE_FBSET_FANCY | ||
41 | CMD_XRES = 100, | ||
42 | CMD_YRES = 101, | ||
43 | CMD_VXRES = 102, | ||
44 | CMD_VYRES = 103, | ||
45 | CMD_DEPTH = 104, | ||
46 | CMD_MATCH = 105, | ||
47 | CMD_PIXCLOCK = 106, | ||
48 | CMD_LEFT = 107, | ||
49 | CMD_RIGHT = 108, | ||
50 | CMD_UPPER = 109, | ||
51 | CMD_LOWER = 110, | ||
52 | CMD_HSLEN = 111, | ||
53 | CMD_VSLEN = 112, | ||
54 | CMD_CSYNC = 113, | ||
55 | CMD_GSYNC = 114, | ||
56 | CMD_EXTSYNC = 115, | ||
57 | CMD_BCAST = 116, | ||
58 | CMD_RGBA = 117, | ||
59 | CMD_STEP = 118, | ||
60 | CMD_MOVE = 119, | ||
61 | #endif | ||
62 | }; | ||
63 | |||
64 | static unsigned int g_options = 0; | ||
65 | |||
66 | /* Stuff stolen from the kernel's fb.h */ | ||
67 | #define FB_ACTIVATE_ALL 64 | ||
68 | enum { | ||
69 | FBIOGET_VSCREENINFO = 0x4600, | ||
70 | FBIOPUT_VSCREENINFO = 0x4601 | ||
71 | }; | ||
72 | struct fb_bitfield { | ||
73 | uint32_t offset; /* beginning of bitfield */ | ||
74 | uint32_t length; /* length of bitfield */ | ||
75 | uint32_t msb_right; /* !=0: Most significant bit is right */ | ||
76 | }; | ||
77 | struct fb_var_screeninfo { | ||
78 | uint32_t xres; /* visible resolution */ | ||
79 | uint32_t yres; | ||
80 | uint32_t xres_virtual; /* virtual resolution */ | ||
81 | uint32_t yres_virtual; | ||
82 | uint32_t xoffset; /* offset from virtual to visible */ | ||
83 | uint32_t yoffset; /* resolution */ | ||
84 | |||
85 | uint32_t bits_per_pixel; | ||
86 | uint32_t grayscale; /* !=0 Graylevels instead of colors */ | ||
87 | |||
88 | struct fb_bitfield red; /* bitfield in fb mem if true color, */ | ||
89 | struct fb_bitfield green; /* else only length is significant */ | ||
90 | struct fb_bitfield blue; | ||
91 | struct fb_bitfield transp; /* transparency */ | ||
92 | |||
93 | uint32_t nonstd; /* !=0 Non standard pixel format */ | ||
94 | |||
95 | uint32_t activate; /* see FB_ACTIVATE_x */ | ||
96 | |||
97 | uint32_t height; /* height of picture in mm */ | ||
98 | uint32_t width; /* width of picture in mm */ | ||
99 | |||
100 | uint32_t accel_flags; /* acceleration flags (hints) */ | ||
101 | |||
102 | /* Timing: All values in pixclocks, except pixclock (of course) */ | ||
103 | uint32_t pixclock; /* pixel clock in ps (pico seconds) */ | ||
104 | uint32_t left_margin; /* time from sync to picture */ | ||
105 | uint32_t right_margin; /* time from picture to sync */ | ||
106 | uint32_t upper_margin; /* time from sync to picture */ | ||
107 | uint32_t lower_margin; | ||
108 | uint32_t hsync_len; /* length of horizontal sync */ | ||
109 | uint32_t vsync_len; /* length of vertical sync */ | ||
110 | uint32_t sync; /* see FB_SYNC_x */ | ||
111 | uint32_t vmode; /* see FB_VMODE_x */ | ||
112 | uint32_t reserved[6]; /* Reserved for future compatibility */ | ||
113 | }; | ||
114 | |||
115 | |||
116 | static const struct cmdoptions_t { | ||
117 | const char name[10]; | ||
118 | const unsigned char param_count; | ||
119 | const unsigned char code; | ||
120 | } g_cmdoptions[] = { | ||
121 | { "-fb", 1, CMD_FB }, | ||
122 | { "-db", 1, CMD_DB }, | ||
123 | { "-a", 0, CMD_ALL }, | ||
124 | { "-i", 0, CMD_INFO }, | ||
125 | { "-g", 5, CMD_GEOMETRY }, | ||
126 | { "-t", 7, CMD_TIMING }, | ||
127 | { "-accel", 1, CMD_ACCEL }, | ||
128 | { "-hsync", 1, CMD_HSYNC }, | ||
129 | { "-vsync", 1, CMD_VSYNC }, | ||
130 | { "-laced", 1, CMD_LACED }, | ||
131 | { "-double", 1, CMD_DOUBLE }, | ||
132 | { "-n", 0, CMD_CHANGE }, | ||
133 | #ifdef CONFIG_FEATURE_FBSET_FANCY | ||
134 | { "-all", 0, CMD_ALL }, | ||
135 | { "-xres", 1, CMD_XRES }, | ||
136 | { "-yres", 1, CMD_YRES }, | ||
137 | { "-vxres", 1, CMD_VXRES }, | ||
138 | { "-vyres", 1, CMD_VYRES }, | ||
139 | { "-depth", 1, CMD_DEPTH }, | ||
140 | { "-match", 0, CMD_MATCH }, | ||
141 | { "-geometry", 5, CMD_GEOMETRY }, | ||
142 | { "-pixclock", 1, CMD_PIXCLOCK }, | ||
143 | { "-left", 1, CMD_LEFT }, | ||
144 | { "-right", 1, CMD_RIGHT }, | ||
145 | { "-upper", 1, CMD_UPPER }, | ||
146 | { "-lower", 1, CMD_LOWER }, | ||
147 | { "-hslen", 1, CMD_HSLEN }, | ||
148 | { "-vslen", 1, CMD_VSLEN }, | ||
149 | { "-timings", 7, CMD_TIMING }, | ||
150 | { "-csync", 1, CMD_CSYNC }, | ||
151 | { "-gsync", 1, CMD_GSYNC }, | ||
152 | { "-extsync", 1, CMD_EXTSYNC }, | ||
153 | { "-bcast", 1, CMD_BCAST }, | ||
154 | { "-rgba", 1, CMD_RGBA }, | ||
155 | { "-step", 1, CMD_STEP }, | ||
156 | { "-move", 1, CMD_MOVE }, | ||
157 | #endif | ||
158 | { "", 0, 0 } | ||
159 | }; | ||
160 | |||
161 | #ifdef CONFIG_FEATURE_FBSET_READMODE | ||
162 | /* taken from linux/fb.h */ | ||
163 | enum { | ||
164 | FB_VMODE_INTERLACED = 1, /* interlaced */ | ||
165 | FB_VMODE_DOUBLE = 2, /* double scan */ | ||
166 | FB_SYNC_HOR_HIGH_ACT = 1, /* horizontal sync high active */ | ||
167 | FB_SYNC_VERT_HIGH_ACT = 2, /* vertical sync high active */ | ||
168 | FB_SYNC_EXT = 4, /* external sync */ | ||
169 | FB_SYNC_COMP_HIGH_ACT = 8 /* composite sync high active */ | ||
170 | }; | ||
171 | #endif | ||
172 | |||
173 | static int readmode(struct fb_var_screeninfo *base, const char *fn, | ||
174 | const char *mode) | ||
175 | { | ||
176 | #ifdef CONFIG_FEATURE_FBSET_READMODE | ||
177 | FILE *f; | ||
178 | char buf[256]; | ||
179 | char *p = buf; | ||
180 | |||
181 | f = xfopen(fn, "r"); | ||
182 | while (!feof(f)) { | ||
183 | fgets(buf, sizeof(buf), f); | ||
184 | if (!(p = strstr(buf, "mode ")) && !(p = strstr(buf, "mode\t"))) | ||
185 | continue; | ||
186 | p += 5; | ||
187 | if (!(p = strstr(buf, mode))) | ||
188 | continue; | ||
189 | p += strlen(mode); | ||
190 | if (!isspace(*p) && (*p != 0) && (*p != '"') | ||
191 | && (*p != '\r') && (*p != '\n')) | ||
192 | continue; /* almost, but not quite */ | ||
193 | |||
194 | while (!feof(f)) { | ||
195 | fgets(buf, sizeof(buf), f); | ||
196 | if ((p = strstr(buf, "geometry "))) { | ||
197 | p += 9; | ||
198 | /* FIXME: catastrophic on arches with 64bit ints */ | ||
199 | sscanf(p, "%d %d %d %d %d", | ||
200 | &(base->xres), &(base->yres), | ||
201 | &(base->xres_virtual), &(base->yres_virtual), | ||
202 | &(base->bits_per_pixel)); | ||
203 | } else if ((p = strstr(buf, "timings "))) { | ||
204 | p += 8; | ||
205 | sscanf(p, "%d %d %d %d %d %d %d", | ||
206 | &(base->pixclock), | ||
207 | &(base->left_margin), &(base->right_margin), | ||
208 | &(base->upper_margin), &(base->lower_margin), | ||
209 | &(base->hsync_len), &(base->vsync_len)); | ||
210 | } else if ((p = strstr(buf, "laced "))) { | ||
211 | //p += 6; | ||
212 | if (strstr(buf, "false")) { | ||
213 | base->vmode &= ~FB_VMODE_INTERLACED; | ||
214 | } else { | ||
215 | base->vmode |= FB_VMODE_INTERLACED; | ||
216 | } | ||
217 | } else if ((p = strstr(buf, "double "))) { | ||
218 | //p += 7; | ||
219 | if (strstr(buf, "false")) { | ||
220 | base->vmode &= ~FB_VMODE_DOUBLE; | ||
221 | } else { | ||
222 | base->vmode |= FB_VMODE_DOUBLE; | ||
223 | } | ||
224 | } else if ((p = strstr(buf, "vsync "))) { | ||
225 | //p += 6; | ||
226 | if (strstr(buf, "low")) { | ||
227 | base->sync &= ~FB_SYNC_VERT_HIGH_ACT; | ||
228 | } else { | ||
229 | base->sync |= FB_SYNC_VERT_HIGH_ACT; | ||
230 | } | ||
231 | } else if ((p = strstr(buf, "hsync "))) { | ||
232 | //p += 6; | ||
233 | if (strstr(buf, "low")) { | ||
234 | base->sync &= ~FB_SYNC_HOR_HIGH_ACT; | ||
235 | } else { | ||
236 | base->sync |= FB_SYNC_HOR_HIGH_ACT; | ||
237 | } | ||
238 | } else if ((p = strstr(buf, "csync "))) { | ||
239 | //p += 6; | ||
240 | if (strstr(buf, "low")) { | ||
241 | base->sync &= ~FB_SYNC_COMP_HIGH_ACT; | ||
242 | } else { | ||
243 | base->sync |= FB_SYNC_COMP_HIGH_ACT; | ||
244 | } | ||
245 | } else if ((p = strstr(buf, "extsync "))) { | ||
246 | //p += 8; | ||
247 | if (strstr(buf, "false")) { | ||
248 | base->sync &= ~FB_SYNC_EXT; | ||
249 | } else { | ||
250 | base->sync |= FB_SYNC_EXT; | ||
251 | } | ||
252 | } | ||
253 | |||
254 | if (strstr(buf, "endmode")) | ||
255 | return 1; | ||
256 | } | ||
257 | } | ||
258 | #else | ||
259 | bb_error_msg("mode reading not compiled in"); | ||
260 | #endif | ||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | static inline void setmode(struct fb_var_screeninfo *base, | ||
265 | struct fb_var_screeninfo *set) | ||
266 | { | ||
267 | if ((int) set->xres > 0) | ||
268 | base->xres = set->xres; | ||
269 | if ((int) set->yres > 0) | ||
270 | base->yres = set->yres; | ||
271 | if ((int) set->xres_virtual > 0) | ||
272 | base->xres_virtual = set->xres_virtual; | ||
273 | if ((int) set->yres_virtual > 0) | ||
274 | base->yres_virtual = set->yres_virtual; | ||
275 | if ((int) set->bits_per_pixel > 0) | ||
276 | base->bits_per_pixel = set->bits_per_pixel; | ||
277 | } | ||
278 | |||
279 | static inline void showmode(struct fb_var_screeninfo *v) | ||
280 | { | ||
281 | double drate = 0, hrate = 0, vrate = 0; | ||
282 | |||
283 | if (v->pixclock) { | ||
284 | drate = 1e12 / v->pixclock; | ||
285 | hrate = drate / (v->left_margin + v->xres + v->right_margin + v->hsync_len); | ||
286 | vrate = hrate / (v->upper_margin + v->yres + v->lower_margin + v->vsync_len); | ||
287 | } | ||
288 | printf("\nmode \"%ux%u-%u\"\n" | ||
289 | #ifdef CONFIG_FEATURE_FBSET_FANCY | ||
290 | "\t# D: %.3f MHz, H: %.3f kHz, V: %.3f Hz\n" | ||
291 | #endif | ||
292 | "\tgeometry %u %u %u %u %u\n" | ||
293 | "\ttimings %u %u %u %u %u %u %u\n" | ||
294 | "\taccel %s\n" | ||
295 | "\trgba %u/%u,%u/%u,%u/%u,%u/%u\n" | ||
296 | "endmode\n\n", | ||
297 | v->xres, v->yres, (int) (vrate + 0.5), | ||
298 | #ifdef CONFIG_FEATURE_FBSET_FANCY | ||
299 | drate / 1e6, hrate / 1e3, vrate, | ||
300 | #endif | ||
301 | v->xres, v->yres, v->xres_virtual, v->yres_virtual, v->bits_per_pixel, | ||
302 | v->pixclock, v->left_margin, v->right_margin, v->upper_margin, v->lower_margin, | ||
303 | v->hsync_len, v->vsync_len, | ||
304 | (v->accel_flags > 0 ? "true" : "false"), | ||
305 | v->red.length, v->red.offset, v->green.length, v->green.offset, | ||
306 | v->blue.length, v->blue.offset, v->transp.length, v->transp.offset); | ||
307 | } | ||
308 | |||
309 | #ifdef STANDALONE | ||
310 | int main(int argc, char **argv) | ||
311 | #else | ||
312 | int fbset_main(int argc, char **argv) | ||
313 | #endif | ||
314 | { | ||
315 | struct fb_var_screeninfo var, varset; | ||
316 | int fh, i; | ||
317 | char *fbdev = DEFAULTFBDEV; | ||
318 | char *modefile = DEFAULTFBMODE; | ||
319 | char *thisarg, *mode = NULL; | ||
320 | |||
321 | memset(&varset, 0xFF, sizeof(varset)); | ||
322 | |||
323 | /* parse cmd args.... why do they have to make things so difficult? */ | ||
324 | argv++; | ||
325 | argc--; | ||
326 | for (; argc > 0 && (thisarg = *argv); argc--, argv++) { | ||
327 | for (i = 0; g_cmdoptions[i].name[0]; i++) { | ||
328 | if (strcmp(thisarg, g_cmdoptions[i].name)) | ||
329 | continue; | ||
330 | if (argc-1 < g_cmdoptions[i].param_count) | ||
331 | bb_show_usage(); | ||
332 | |||
333 | switch (g_cmdoptions[i].code) { | ||
334 | case CMD_FB: | ||
335 | fbdev = argv[1]; | ||
336 | break; | ||
337 | case CMD_DB: | ||
338 | modefile = argv[1]; | ||
339 | break; | ||
340 | case CMD_GEOMETRY: | ||
341 | varset.xres = xatou32(argv[1]); | ||
342 | varset.yres = xatou32(argv[2]); | ||
343 | varset.xres_virtual = xatou32(argv[3]); | ||
344 | varset.yres_virtual = xatou32(argv[4]); | ||
345 | varset.bits_per_pixel = xatou32(argv[5]); | ||
346 | break; | ||
347 | case CMD_TIMING: | ||
348 | varset.pixclock = xatou32(argv[1]); | ||
349 | varset.left_margin = xatou32(argv[2]); | ||
350 | varset.right_margin = xatou32(argv[3]); | ||
351 | varset.upper_margin = xatou32(argv[4]); | ||
352 | varset.lower_margin = xatou32(argv[5]); | ||
353 | varset.hsync_len = xatou32(argv[6]); | ||
354 | varset.vsync_len = xatou32(argv[7]); | ||
355 | break; | ||
356 | case CMD_ALL: | ||
357 | g_options |= OPT_ALL; | ||
358 | break; | ||
359 | case CMD_CHANGE: | ||
360 | g_options |= OPT_CHANGE; | ||
361 | break; | ||
362 | #ifdef CONFIG_FEATURE_FBSET_FANCY | ||
363 | case CMD_XRES: | ||
364 | varset.xres = xatou32(argv[1]); | ||
365 | break; | ||
366 | case CMD_YRES: | ||
367 | varset.yres = xatou32(argv[1]); | ||
368 | break; | ||
369 | case CMD_DEPTH: | ||
370 | varset.bits_per_pixel = xatou32(argv[1]); | ||
371 | break; | ||
372 | #endif | ||
373 | } | ||
374 | argc -= g_cmdoptions[i].param_count; | ||
375 | argv += g_cmdoptions[i].param_count; | ||
376 | break; | ||
377 | } | ||
378 | if (!g_cmdoptions[i].name[0]) { | ||
379 | if (argc != 1) | ||
380 | bb_show_usage(); | ||
381 | mode = *argv; | ||
382 | g_options |= OPT_READMODE; | ||
383 | } | ||
384 | } | ||
385 | |||
386 | fh = xopen(fbdev, O_RDONLY); | ||
387 | if (ioctl(fh, FBIOGET_VSCREENINFO, &var)) | ||
388 | bb_perror_msg_and_die("ioctl(%sT_VSCREENINFO)", "GE"); | ||
389 | if (g_options & OPT_READMODE) { | ||
390 | if (!readmode(&var, modefile, mode)) { | ||
391 | bb_error_msg_and_die("unknown video mode '%s'", mode); | ||
392 | } | ||
393 | } | ||
394 | |||
395 | setmode(&var, &varset); | ||
396 | if (g_options & OPT_CHANGE) { | ||
397 | if (g_options & OPT_ALL) | ||
398 | var.activate = FB_ACTIVATE_ALL; | ||
399 | if (ioctl(fh, FBIOPUT_VSCREENINFO, &var)) | ||
400 | bb_perror_msg_and_die("ioctl(%sT_VSCREENINFO)", "PU"); | ||
401 | } | ||
402 | showmode(&var); | ||
403 | /* Don't close the file, as exiting will take care of that */ | ||
404 | /* close(fh); */ | ||
405 | |||
406 | return EXIT_SUCCESS; | ||
407 | } | ||
diff --git a/util-linux/fdformat.c b/util-linux/fdformat.c new file mode 100644 index 000000000..0242d8d3a --- /dev/null +++ b/util-linux/fdformat.c | |||
@@ -0,0 +1,143 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* fdformat.c - Low-level formats a floppy disk - Werner Almesberger */ | ||
3 | |||
4 | /* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL> | ||
5 | * - added Native Language Support | ||
6 | * 1999-03-20 Arnaldo Carvalho de Melo <acme@conectiva.com.br> | ||
7 | * - more i18n/nls translatable strings marked | ||
8 | * | ||
9 | * 5 July 2003 -- modified for Busybox by Erik Andersen | ||
10 | */ | ||
11 | |||
12 | #include "busybox.h" | ||
13 | |||
14 | |||
15 | /* Stuff extracted from linux/fd.h */ | ||
16 | struct floppy_struct { | ||
17 | unsigned int size, /* nr of sectors total */ | ||
18 | sect, /* sectors per track */ | ||
19 | head, /* nr of heads */ | ||
20 | track, /* nr of tracks */ | ||
21 | stretch; /* !=0 means double track steps */ | ||
22 | #define FD_STRETCH 1 | ||
23 | #define FD_SWAPSIDES 2 | ||
24 | |||
25 | unsigned char gap, /* gap1 size */ | ||
26 | |||
27 | rate, /* data rate. |= 0x40 for perpendicular */ | ||
28 | #define FD_2M 0x4 | ||
29 | #define FD_SIZECODEMASK 0x38 | ||
30 | #define FD_SIZECODE(floppy) (((((floppy)->rate&FD_SIZECODEMASK)>> 3)+ 2) %8) | ||
31 | #define FD_SECTSIZE(floppy) ( (floppy)->rate & FD_2M ? \ | ||
32 | 512 : 128 << FD_SIZECODE(floppy) ) | ||
33 | #define FD_PERP 0x40 | ||
34 | |||
35 | spec1, /* stepping rate, head unload time */ | ||
36 | fmt_gap; /* gap2 size */ | ||
37 | const char * name; /* used only for predefined formats */ | ||
38 | }; | ||
39 | struct format_descr { | ||
40 | unsigned int device,head,track; | ||
41 | }; | ||
42 | #define FDFMTBEG _IO(2,0x47) | ||
43 | #define FDFMTTRK _IOW(2,0x48, struct format_descr) | ||
44 | #define FDFMTEND _IO(2,0x49) | ||
45 | #define FDGETPRM _IOR(2, 0x04, struct floppy_struct) | ||
46 | #define FD_FILL_BYTE 0xF6 /* format fill byte. */ | ||
47 | |||
48 | static void xioctl(int fd, int request, void *argp, const char *string) | ||
49 | { | ||
50 | if (ioctl(fd, request, argp) < 0) { | ||
51 | bb_perror_msg_and_die(string); | ||
52 | } | ||
53 | } | ||
54 | |||
55 | int fdformat_main(int argc,char **argv) | ||
56 | { | ||
57 | int fd, n, cyl, read_bytes, verify; | ||
58 | unsigned char *data; | ||
59 | struct stat st; | ||
60 | struct floppy_struct param; | ||
61 | struct format_descr descr; | ||
62 | |||
63 | if (argc < 2) { | ||
64 | bb_show_usage(); | ||
65 | } | ||
66 | verify = !getopt32(argc, argv, "n"); | ||
67 | argv += optind; | ||
68 | |||
69 | xstat(*argv, &st); | ||
70 | if (!S_ISBLK(st.st_mode)) { | ||
71 | bb_error_msg_and_die("%s: not a block device", *argv); | ||
72 | /* do not test major - perhaps this was an USB floppy */ | ||
73 | } | ||
74 | |||
75 | /* O_RDWR for formatting and verifying */ | ||
76 | fd = xopen(*argv, O_RDWR); | ||
77 | |||
78 | /* original message was: "Could not determine current format type" */ | ||
79 | xioctl(fd, FDGETPRM, ¶m, "FDGETPRM"); | ||
80 | |||
81 | printf("%s-sided, %d tracks, %d sec/track. Total capacity %d kB\n", | ||
82 | (param.head == 2) ? "Double" : "Single", | ||
83 | param.track, param.sect, param.size >> 1); | ||
84 | |||
85 | /* FORMAT */ | ||
86 | printf("Formatting... "); | ||
87 | xioctl(fd, FDFMTBEG, NULL, "FDFMTBEG"); | ||
88 | |||
89 | /* n == track */ | ||
90 | for (n = 0; n < param.track; n++) { | ||
91 | descr.head = 0; | ||
92 | descr.track = n; | ||
93 | xioctl(fd, FDFMTTRK, &descr, "FDFMTTRK"); | ||
94 | printf("%3d\b\b\b", n); | ||
95 | if (param.head == 2) { | ||
96 | descr.head = 1; | ||
97 | xioctl(fd, FDFMTTRK, &descr, "FDFMTTRK"); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | xioctl(fd, FDFMTEND, NULL, "FDFMTEND"); | ||
102 | printf("done\n"); | ||
103 | |||
104 | /* VERIFY */ | ||
105 | if (verify) { | ||
106 | /* n == cyl_size */ | ||
107 | n = param.sect*param.head*512; | ||
108 | |||
109 | data = xmalloc(n); | ||
110 | printf("Verifying... "); | ||
111 | for (cyl = 0; cyl < param.track; cyl++) { | ||
112 | printf("%3d\b\b\b", cyl); | ||
113 | read_bytes = safe_read(fd, data, n); | ||
114 | if (read_bytes != n) { | ||
115 | if (read_bytes < 0) { | ||
116 | bb_perror_msg(bb_msg_read_error); | ||
117 | } | ||
118 | bb_error_msg_and_die("problem reading cylinder %d, " | ||
119 | "expected %d, read %d", cyl, n, read_bytes); | ||
120 | // FIXME: maybe better seek & continue?? | ||
121 | } | ||
122 | /* Check backwards so we don't need a counter */ | ||
123 | while (--read_bytes >= 0) { | ||
124 | if (data[read_bytes] != FD_FILL_BYTE) { | ||
125 | printf("bad data in cyl %d\nContinuing... ",cyl); | ||
126 | } | ||
127 | } | ||
128 | } | ||
129 | /* There is no point in freeing blocks at the end of a program, because | ||
130 | all of the program's space is given back to the system when the process | ||
131 | terminates.*/ | ||
132 | |||
133 | if (ENABLE_FEATURE_CLEAN_UP) free(data); | ||
134 | |||
135 | printf("done\n"); | ||
136 | } | ||
137 | |||
138 | if (ENABLE_FEATURE_CLEAN_UP) close(fd); | ||
139 | |||
140 | /* Don't bother closing. Exit does | ||
141 | * that, so we can save a few bytes */ | ||
142 | return EXIT_SUCCESS; | ||
143 | } | ||
diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c new file mode 100644 index 000000000..2f87f1c60 --- /dev/null +++ b/util-linux/fdisk.c | |||
@@ -0,0 +1,3043 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* fdisk.c -- Partition table manipulator for Linux. | ||
3 | * | ||
4 | * Copyright (C) 1992 A. V. Le Blanc (LeBlanc@mcc.ac.uk) | ||
5 | * Copyright (C) 2001,2002 Vladimir Oleynik <dzo@simtreas.ru> (initial bb port) | ||
6 | * | ||
7 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | ||
8 | */ | ||
9 | |||
10 | #include <assert.h> /* assert */ | ||
11 | #include "busybox.h" | ||
12 | #define _(x) x | ||
13 | |||
14 | /* Looks like someone forgot to add this to config system */ | ||
15 | #ifndef ENABLE_FEATURE_FDISK_BLKSIZE | ||
16 | # define ENABLE_FEATURE_FDISK_BLKSIZE 0 | ||
17 | # define USE_FEATURE_FDISK_BLKSIZE(a) | ||
18 | #endif | ||
19 | |||
20 | #define DEFAULT_SECTOR_SIZE 512 | ||
21 | #define MAX_SECTOR_SIZE 2048 | ||
22 | #define SECTOR_SIZE 512 /* still used in osf/sgi/sun code */ | ||
23 | #define MAXIMUM_PARTS 60 | ||
24 | |||
25 | #define ACTIVE_FLAG 0x80 | ||
26 | |||
27 | #define EXTENDED 0x05 | ||
28 | #define WIN98_EXTENDED 0x0f | ||
29 | #define LINUX_PARTITION 0x81 | ||
30 | #define LINUX_SWAP 0x82 | ||
31 | #define LINUX_NATIVE 0x83 | ||
32 | #define LINUX_EXTENDED 0x85 | ||
33 | #define LINUX_LVM 0x8e | ||
34 | #define LINUX_RAID 0xfd | ||
35 | |||
36 | #define IS_EXTENDED(i) \ | ||
37 | ((i) == EXTENDED || (i) == WIN98_EXTENDED || (i) == LINUX_EXTENDED) | ||
38 | |||
39 | #define SIZE(a) (sizeof(a)/sizeof((a)[0])) | ||
40 | |||
41 | #define cround(n) (display_in_cyl_units ? ((n)/units_per_sector)+1 : (n)) | ||
42 | #define scround(x) (((x)+units_per_sector-1)/units_per_sector) | ||
43 | |||
44 | struct hd_geometry { | ||
45 | unsigned char heads; | ||
46 | unsigned char sectors; | ||
47 | unsigned short cylinders; | ||
48 | unsigned long start; | ||
49 | }; | ||
50 | |||
51 | #define HDIO_GETGEO 0x0301 /* get device geometry */ | ||
52 | |||
53 | struct systypes { | ||
54 | const char *name; | ||
55 | }; | ||
56 | |||
57 | static unsigned sector_size = DEFAULT_SECTOR_SIZE; | ||
58 | static unsigned user_set_sector_size; | ||
59 | static unsigned sector_offset = 1; | ||
60 | |||
61 | /* | ||
62 | * Raw disk label. For DOS-type partition tables the MBR, | ||
63 | * with descriptions of the primary partitions. | ||
64 | */ | ||
65 | #if (MAX_SECTOR_SIZE) > (BUFSIZ+1) | ||
66 | static char MBRbuffer[MAX_SECTOR_SIZE]; | ||
67 | #else | ||
68 | # define MBRbuffer bb_common_bufsiz1 | ||
69 | #endif | ||
70 | |||
71 | #if ENABLE_FEATURE_OSF_LABEL | ||
72 | static int possibly_osf_label; | ||
73 | #endif | ||
74 | |||
75 | static unsigned heads, sectors, cylinders; | ||
76 | static void update_units(void); | ||
77 | |||
78 | |||
79 | /* | ||
80 | * return partition name - uses static storage unless buf is supplied | ||
81 | */ | ||
82 | static const char * | ||
83 | partname(const char *dev, int pno, int lth) | ||
84 | { | ||
85 | static char buffer[80]; | ||
86 | const char *p; | ||
87 | int w, wp; | ||
88 | int bufsiz; | ||
89 | char *bufp; | ||
90 | |||
91 | bufp = buffer; | ||
92 | bufsiz = sizeof(buffer); | ||
93 | |||
94 | w = strlen(dev); | ||
95 | p = ""; | ||
96 | |||
97 | if (isdigit(dev[w-1])) | ||
98 | p = "p"; | ||
99 | |||
100 | /* devfs kludge - note: fdisk partition names are not supposed | ||
101 | to equal kernel names, so there is no reason to do this */ | ||
102 | if (strcmp(dev + w - 4, "disc") == 0) { | ||
103 | w -= 4; | ||
104 | p = "part"; | ||
105 | } | ||
106 | |||
107 | wp = strlen(p); | ||
108 | |||
109 | if (lth) { | ||
110 | snprintf(bufp, bufsiz, "%*.*s%s%-2u", | ||
111 | lth-wp-2, w, dev, p, pno); | ||
112 | } else { | ||
113 | snprintf(bufp, bufsiz, "%.*s%s%-2u", w, dev, p, pno); | ||
114 | } | ||
115 | return bufp; | ||
116 | } | ||
117 | |||
118 | struct partition { | ||
119 | unsigned char boot_ind; /* 0x80 - active */ | ||
120 | unsigned char head; /* starting head */ | ||
121 | unsigned char sector; /* starting sector */ | ||
122 | unsigned char cyl; /* starting cylinder */ | ||
123 | unsigned char sys_ind; /* What partition type */ | ||
124 | unsigned char end_head; /* end head */ | ||
125 | unsigned char end_sector; /* end sector */ | ||
126 | unsigned char end_cyl; /* end cylinder */ | ||
127 | unsigned char start4[4]; /* starting sector counting from 0 */ | ||
128 | unsigned char size4[4]; /* nr of sectors in partition */ | ||
129 | } ATTRIBUTE_PACKED; | ||
130 | |||
131 | enum failure { | ||
132 | ioctl_error, unable_to_open, unable_to_read, unable_to_seek, | ||
133 | unable_to_write | ||
134 | }; | ||
135 | |||
136 | enum label_type { | ||
137 | label_dos, label_sun, label_sgi, label_aix, label_osf | ||
138 | }; | ||
139 | #define LABEL_IS_DOS (label_dos == current_label_type) | ||
140 | |||
141 | #if ENABLE_FEATURE_SUN_LABEL | ||
142 | #define LABEL_IS_SUN (label_sun == current_label_type) | ||
143 | #define STATIC_SUN static | ||
144 | #else | ||
145 | #define LABEL_IS_SUN 0 | ||
146 | #define STATIC_SUN extern | ||
147 | #endif | ||
148 | |||
149 | #if ENABLE_FEATURE_SGI_LABEL | ||
150 | #define LABEL_IS_SGI (label_sgi == current_label_type) | ||
151 | #define STATIC_SGI static | ||
152 | #else | ||
153 | #define LABEL_IS_SGI 0 | ||
154 | #define STATIC_SGI extern | ||
155 | #endif | ||
156 | |||
157 | #if ENABLE_FEATURE_AIX_LABEL | ||
158 | #define LABEL_IS_AIX (label_aix == current_label_type) | ||
159 | #define STATIC_AIX static | ||
160 | #else | ||
161 | #define LABEL_IS_AIX 0 | ||
162 | #define STATIC_AIX extern | ||
163 | #endif | ||
164 | |||
165 | #if ENABLE_FEATURE_OSF_LABEL | ||
166 | #define LABEL_IS_OSF (label_osf == current_label_type) | ||
167 | #define STATIC_OSF static | ||
168 | #else | ||
169 | #define LABEL_IS_OSF 0 | ||
170 | #define STATIC_OSF extern | ||
171 | #endif | ||
172 | |||
173 | enum action { fdisk, require, try_only, create_empty_dos, create_empty_sun }; | ||
174 | |||
175 | static enum label_type current_label_type; | ||
176 | |||
177 | static const char *disk_device; | ||
178 | static int fd; /* the disk */ | ||
179 | static int partitions = 4; /* maximum partition + 1 */ | ||
180 | static int display_in_cyl_units = 1; | ||
181 | static unsigned units_per_sector = 1; | ||
182 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
183 | static void change_units(void); | ||
184 | static void reread_partition_table(int leave); | ||
185 | static void delete_partition(int i); | ||
186 | static int get_partition(int warn, int max); | ||
187 | static void list_types(const struct systypes *sys); | ||
188 | static unsigned read_int(unsigned low, unsigned dflt, unsigned high, unsigned base, char *mesg); | ||
189 | #endif | ||
190 | static const char *partition_type(unsigned char type); | ||
191 | static void fdisk_fatal(enum failure why) ATTRIBUTE_NORETURN; | ||
192 | static void get_geometry(void); | ||
193 | static int get_boot(enum action what); | ||
194 | |||
195 | #define PLURAL 0 | ||
196 | #define SINGULAR 1 | ||
197 | |||
198 | #define hex_val(c) ({ \ | ||
199 | char _c = (c); \ | ||
200 | isdigit(_c) ? _c - '0' : \ | ||
201 | tolower(_c) + 10 - 'a'; \ | ||
202 | }) | ||
203 | |||
204 | |||
205 | #define LINE_LENGTH 800 | ||
206 | #define pt_offset(b, n) ((struct partition *)((b) + 0x1be + \ | ||
207 | (n) * sizeof(struct partition))) | ||
208 | #define sector(s) ((s) & 0x3f) | ||
209 | #define cylinder(s, c) ((c) | (((s) & 0xc0) << 2)) | ||
210 | |||
211 | #define hsc2sector(h,s,c) (sector(s) - 1 + sectors * \ | ||
212 | ((h) + heads * cylinder(s,c))) | ||
213 | #define set_hsc(h,s,c,sector) { \ | ||
214 | s = sector % sectors + 1; \ | ||
215 | sector /= sectors; \ | ||
216 | h = sector % heads; \ | ||
217 | sector /= heads; \ | ||
218 | c = sector & 0xff; \ | ||
219 | s |= (sector >> 2) & 0xc0; \ | ||
220 | } | ||
221 | |||
222 | |||
223 | static int32_t get_start_sect(const struct partition *p); | ||
224 | static int32_t get_nr_sects(const struct partition *p); | ||
225 | |||
226 | /* | ||
227 | * per partition table entry data | ||
228 | * | ||
229 | * The four primary partitions have the same sectorbuffer (MBRbuffer) | ||
230 | * and have NULL ext_pointer. | ||
231 | * Each logical partition table entry has two pointers, one for the | ||
232 | * partition and one link to the next one. | ||
233 | */ | ||
234 | static struct pte { | ||
235 | struct partition *part_table; /* points into sectorbuffer */ | ||
236 | struct partition *ext_pointer; /* points into sectorbuffer */ | ||
237 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
238 | char changed; /* boolean */ | ||
239 | #endif | ||
240 | off_t offset; /* disk sector number */ | ||
241 | char *sectorbuffer; /* disk sector contents */ | ||
242 | } ptes[MAXIMUM_PARTS]; | ||
243 | |||
244 | |||
245 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
246 | static void | ||
247 | set_all_unchanged(void) | ||
248 | { | ||
249 | int i; | ||
250 | |||
251 | for (i = 0; i < MAXIMUM_PARTS; i++) | ||
252 | ptes[i].changed = 0; | ||
253 | } | ||
254 | |||
255 | extern inline void | ||
256 | set_changed(int i) | ||
257 | { | ||
258 | ptes[i].changed = 1; | ||
259 | } | ||
260 | #endif /* CONFIG_FEATURE_FDISK_WRITABLE */ | ||
261 | |||
262 | extern inline struct partition * | ||
263 | get_part_table(int i) | ||
264 | { | ||
265 | return ptes[i].part_table; | ||
266 | } | ||
267 | |||
268 | static const char * | ||
269 | str_units(int n) | ||
270 | { /* n==1: use singular */ | ||
271 | if (n == 1) | ||
272 | return display_in_cyl_units ? _("cylinder") : _("sector"); | ||
273 | else | ||
274 | return display_in_cyl_units ? _("cylinders") : _("sectors"); | ||
275 | } | ||
276 | |||
277 | static int | ||
278 | valid_part_table_flag(const char *mbuffer) | ||
279 | { | ||
280 | return (mbuffer[510] == 0x55 && (uint8_t)mbuffer[511] == 0xaa); | ||
281 | } | ||
282 | |||
283 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
284 | extern inline void | ||
285 | write_part_table_flag(char *b) | ||
286 | { | ||
287 | b[510] = 0x55; | ||
288 | b[511] = 0xaa; | ||
289 | } | ||
290 | |||
291 | static char line_buffer[LINE_LENGTH]; | ||
292 | static char *line_ptr; | ||
293 | |||
294 | /* read line; return 0 or first char */ | ||
295 | static int | ||
296 | read_line(void) | ||
297 | { | ||
298 | fflush(stdout); /* requested by niles@scyld.com */ | ||
299 | line_ptr = line_buffer; | ||
300 | if (!fgets(line_buffer, LINE_LENGTH, stdin)) { | ||
301 | /* error or eof */ | ||
302 | bb_error_msg_and_die("\ngot EOF, exiting"); | ||
303 | } | ||
304 | while (*line_ptr && !isgraph(*line_ptr)) | ||
305 | line_ptr++; | ||
306 | return *line_ptr; | ||
307 | } | ||
308 | |||
309 | static char | ||
310 | read_nonempty(const char *mesg) | ||
311 | { | ||
312 | do { | ||
313 | fputs(mesg, stdout); | ||
314 | } while (!read_line()); | ||
315 | return *line_ptr; | ||
316 | } | ||
317 | |||
318 | static char | ||
319 | read_maybe_empty(const char *mesg) | ||
320 | { | ||
321 | fputs(mesg, stdout); | ||
322 | if (!read_line()) { | ||
323 | line_ptr = line_buffer; | ||
324 | *line_ptr = '\n'; | ||
325 | line_ptr[1] = 0; | ||
326 | } | ||
327 | return *line_ptr; | ||
328 | } | ||
329 | |||
330 | static int | ||
331 | read_hex(const struct systypes *sys) | ||
332 | { | ||
333 | unsigned long v; | ||
334 | while (1) { | ||
335 | read_nonempty(_("Hex code (type L to list codes): ")); | ||
336 | if (*line_ptr == 'l' || *line_ptr == 'L') { | ||
337 | list_types(sys); | ||
338 | continue; | ||
339 | } | ||
340 | v = bb_strtoul(line_ptr, NULL, 16); | ||
341 | if (errno || v > 0xff) continue; | ||
342 | return v; | ||
343 | } | ||
344 | } | ||
345 | #endif /* CONFIG_FEATURE_FDISK_WRITABLE */ | ||
346 | |||
347 | #include "fdisk_aix.c" | ||
348 | |||
349 | typedef struct { | ||
350 | unsigned char info[128]; /* Informative text string */ | ||
351 | unsigned char spare0[14]; | ||
352 | struct sun_info { | ||
353 | unsigned char spare1; | ||
354 | unsigned char id; | ||
355 | unsigned char spare2; | ||
356 | unsigned char flags; | ||
357 | } infos[8]; | ||
358 | unsigned char spare1[246]; /* Boot information etc. */ | ||
359 | unsigned short rspeed; /* Disk rotational speed */ | ||
360 | unsigned short pcylcount; /* Physical cylinder count */ | ||
361 | unsigned short sparecyl; /* extra sects per cylinder */ | ||
362 | unsigned char spare2[4]; /* More magic... */ | ||
363 | unsigned short ilfact; /* Interleave factor */ | ||
364 | unsigned short ncyl; /* Data cylinder count */ | ||
365 | unsigned short nacyl; /* Alt. cylinder count */ | ||
366 | unsigned short ntrks; /* Tracks per cylinder */ | ||
367 | unsigned short nsect; /* Sectors per track */ | ||
368 | unsigned char spare3[4]; /* Even more magic... */ | ||
369 | struct sun_partinfo { | ||
370 | uint32_t start_cylinder; | ||
371 | uint32_t num_sectors; | ||
372 | } partitions[8]; | ||
373 | unsigned short magic; /* Magic number */ | ||
374 | unsigned short csum; /* Label xor'd checksum */ | ||
375 | } sun_partition; | ||
376 | #define sunlabel ((sun_partition *)MBRbuffer) | ||
377 | #define SUNOS_SWAP 3 | ||
378 | #define SUN_WHOLE_DISK 5 | ||
379 | STATIC_OSF void bsd_select(void); | ||
380 | STATIC_OSF void xbsd_print_disklabel(int); | ||
381 | #include "fdisk_osf.c" | ||
382 | |||
383 | #define SGI_VOLHDR 0x00 | ||
384 | /* 1 and 2 were used for drive types no longer supported by SGI */ | ||
385 | #define SGI_SWAP 0x03 | ||
386 | /* 4 and 5 were for filesystem types SGI haven't ever supported on MIPS CPUs */ | ||
387 | #define SGI_VOLUME 0x06 | ||
388 | #define SGI_EFS 0x07 | ||
389 | #define SGI_LVOL 0x08 | ||
390 | #define SGI_RLVOL 0x09 | ||
391 | #define SGI_XFS 0x0a | ||
392 | #define SGI_XFSLOG 0x0b | ||
393 | #define SGI_XLV 0x0c | ||
394 | #define SGI_XVM 0x0d | ||
395 | #define SGI_ENTIRE_DISK SGI_VOLUME | ||
396 | #if defined(CONFIG_FEATURE_SGI_LABEL) || defined(CONFIG_FEATURE_SUN_LABEL) | ||
397 | static uint16_t | ||
398 | __swap16(uint16_t x) | ||
399 | { | ||
400 | return (x << 8) | (x >> 8); | ||
401 | } | ||
402 | |||
403 | static uint32_t | ||
404 | __swap32(uint32_t x) | ||
405 | { | ||
406 | return (x << 24) | | ||
407 | ((x & 0xFF00) << 8) | | ||
408 | ((x & 0xFF0000) >> 8) | | ||
409 | (x >> 24); | ||
410 | } | ||
411 | #endif | ||
412 | |||
413 | STATIC_SGI const struct systypes sgi_sys_types[]; | ||
414 | STATIC_SGI unsigned sgi_get_num_sectors(int i); | ||
415 | STATIC_SGI int sgi_get_sysid(int i); | ||
416 | STATIC_SGI void sgi_delete_partition(int i); | ||
417 | STATIC_SGI void sgi_change_sysid(int i, int sys); | ||
418 | STATIC_SGI void sgi_list_table(int xtra); | ||
419 | STATIC_SGI void sgi_set_xcyl(void); | ||
420 | STATIC_SGI int verify_sgi(int verbose); | ||
421 | STATIC_SGI void sgi_add_partition(int n, int sys); | ||
422 | STATIC_SGI void sgi_set_swappartition(int i); | ||
423 | STATIC_SGI const char *sgi_get_bootfile(void); | ||
424 | STATIC_SGI void sgi_set_bootfile(const char* aFile); | ||
425 | STATIC_SGI void create_sgiinfo(void); | ||
426 | STATIC_SGI void sgi_write_table(void); | ||
427 | STATIC_SGI void sgi_set_bootpartition(int i); | ||
428 | |||
429 | #include "fdisk_sgi.c" | ||
430 | |||
431 | STATIC_SUN const struct systypes sun_sys_types[]; | ||
432 | STATIC_SUN void sun_delete_partition(int i); | ||
433 | STATIC_SUN void sun_change_sysid(int i, int sys); | ||
434 | STATIC_SUN void sun_list_table(int xtra); | ||
435 | STATIC_SUN void sun_set_xcyl(void); | ||
436 | STATIC_SUN void add_sun_partition(int n, int sys); | ||
437 | STATIC_SUN void sun_set_alt_cyl(void); | ||
438 | STATIC_SUN void sun_set_ncyl(int cyl); | ||
439 | STATIC_SUN void sun_set_xcyl(void); | ||
440 | STATIC_SUN void sun_set_ilfact(void); | ||
441 | STATIC_SUN void sun_set_rspeed(void); | ||
442 | STATIC_SUN void sun_set_pcylcount(void); | ||
443 | STATIC_SUN void toggle_sunflags(int i, unsigned char mask); | ||
444 | STATIC_SUN void verify_sun(void); | ||
445 | STATIC_SUN void sun_write_table(void); | ||
446 | #include "fdisk_sun.c" | ||
447 | |||
448 | /* DOS partition types */ | ||
449 | |||
450 | static const struct systypes i386_sys_types[] = { | ||
451 | { "\x00" "Empty" }, | ||
452 | { "\x01" "FAT12" }, | ||
453 | { "\x04" "FAT16 <32M" }, | ||
454 | { "\x05" "Extended" }, /* DOS 3.3+ extended partition */ | ||
455 | { "\x06" "FAT16" }, /* DOS 16-bit >=32M */ | ||
456 | { "\x07" "HPFS/NTFS" }, /* OS/2 IFS, eg, HPFS or NTFS or QNX */ | ||
457 | { "\x0a" "OS/2 Boot Manager" },/* OS/2 Boot Manager */ | ||
458 | { "\x0b" "Win95 FAT32" }, | ||
459 | { "\x0c" "Win95 FAT32 (LBA)" },/* LBA really is 'Extended Int 13h' */ | ||
460 | { "\x0e" "Win95 FAT16 (LBA)" }, | ||
461 | { "\x0f" "Win95 Ext'd (LBA)" }, | ||
462 | { "\x11" "Hidden FAT12" }, | ||
463 | { "\x12" "Compaq diagnostics" }, | ||
464 | { "\x14" "Hidden FAT16 <32M" }, | ||
465 | { "\x16" "Hidden FAT16" }, | ||
466 | { "\x17" "Hidden HPFS/NTFS" }, | ||
467 | { "\x1b" "Hidden Win95 FAT32" }, | ||
468 | { "\x1c" "Hidden Win95 FAT32 (LBA)" }, | ||
469 | { "\x1e" "Hidden Win95 FAT16 (LBA)" }, | ||
470 | { "\x3c" "PartitionMagic recovery" }, | ||
471 | { "\x41" "PPC PReP Boot" }, | ||
472 | { "\x42" "SFS" }, | ||
473 | { "\x63" "GNU HURD or SysV" }, /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */ | ||
474 | { "\x80" "Old Minix" }, /* Minix 1.4a and earlier */ | ||
475 | { "\x81" "Minix / old Linux" },/* Minix 1.4b and later */ | ||
476 | { "\x82" "Linux swap" }, /* also Solaris */ | ||
477 | { "\x83" "Linux" }, | ||
478 | { "\x84" "OS/2 hidden C: drive" }, | ||
479 | { "\x85" "Linux extended" }, | ||
480 | { "\x86" "NTFS volume set" }, | ||
481 | { "\x87" "NTFS volume set" }, | ||
482 | { "\x8e" "Linux LVM" }, | ||
483 | { "\x9f" "BSD/OS" }, /* BSDI */ | ||
484 | { "\xa0" "IBM Thinkpad hibernation" }, | ||
485 | { "\xa5" "FreeBSD" }, /* various BSD flavours */ | ||
486 | { "\xa6" "OpenBSD" }, | ||
487 | { "\xa8" "Darwin UFS" }, | ||
488 | { "\xa9" "NetBSD" }, | ||
489 | { "\xab" "Darwin boot" }, | ||
490 | { "\xb7" "BSDI fs" }, | ||
491 | { "\xb8" "BSDI swap" }, | ||
492 | { "\xbe" "Solaris boot" }, | ||
493 | { "\xeb" "BeOS fs" }, | ||
494 | { "\xee" "EFI GPT" }, /* Intel EFI GUID Partition Table */ | ||
495 | { "\xef" "EFI (FAT-12/16/32)" },/* Intel EFI System Partition */ | ||
496 | { "\xf0" "Linux/PA-RISC boot" },/* Linux/PA-RISC boot loader */ | ||
497 | { "\xf2" "DOS secondary" }, /* DOS 3.3+ secondary */ | ||
498 | { "\xfd" "Linux raid autodetect" },/* New (2.2.x) raid partition with | ||
499 | autodetect using persistent | ||
500 | superblock */ | ||
501 | #if 0 /* ENABLE_WEIRD_PARTITION_TYPES */ | ||
502 | { "\x02" "XENIX root" }, | ||
503 | { "\x03" "XENIX usr" }, | ||
504 | { "\x08" "AIX" }, /* AIX boot (AIX -- PS/2 port) or SplitDrive */ | ||
505 | { "\x09" "AIX bootable" }, /* AIX data or Coherent */ | ||
506 | { "\x10" "OPUS" }, | ||
507 | { "\x18" "AST SmartSleep" }, | ||
508 | { "\x24" "NEC DOS" }, | ||
509 | { "\x39" "Plan 9" }, | ||
510 | { "\x40" "Venix 80286" }, | ||
511 | { "\x4d" "QNX4.x" }, | ||
512 | { "\x4e" "QNX4.x 2nd part" }, | ||
513 | { "\x4f" "QNX4.x 3rd part" }, | ||
514 | { "\x50" "OnTrack DM" }, | ||
515 | { "\x51" "OnTrack DM6 Aux1" }, /* (or Novell) */ | ||
516 | { "\x52" "CP/M" }, /* CP/M or Microport SysV/AT */ | ||
517 | { "\x53" "OnTrack DM6 Aux3" }, | ||
518 | { "\x54" "OnTrackDM6" }, | ||
519 | { "\x55" "EZ-Drive" }, | ||
520 | { "\x56" "Golden Bow" }, | ||
521 | { "\x5c" "Priam Edisk" }, | ||
522 | { "\x61" "SpeedStor" }, | ||
523 | { "\x64" "Novell Netware 286" }, | ||
524 | { "\x65" "Novell Netware 386" }, | ||
525 | { "\x70" "DiskSecure Multi-Boot" }, | ||
526 | { "\x75" "PC/IX" }, | ||
527 | { "\x93" "Amoeba" }, | ||
528 | { "\x94" "Amoeba BBT" }, /* (bad block table) */ | ||
529 | { "\xa7" "NeXTSTEP" }, | ||
530 | { "\xbb" "Boot Wizard hidden" }, | ||
531 | { "\xc1" "DRDOS/sec (FAT-12)" }, | ||
532 | { "\xc4" "DRDOS/sec (FAT-16 < 32M)" }, | ||
533 | { "\xc6" "DRDOS/sec (FAT-16)" }, | ||
534 | { "\xc7" "Syrinx" }, | ||
535 | { "\xda" "Non-FS data" }, | ||
536 | { "\xdb" "CP/M / CTOS / ..." },/* CP/M or Concurrent CP/M or | ||
537 | Concurrent DOS or CTOS */ | ||
538 | { "\xde" "Dell Utility" }, /* Dell PowerEdge Server utilities */ | ||
539 | { "\xdf" "BootIt" }, /* BootIt EMBRM */ | ||
540 | { "\xe1" "DOS access" }, /* DOS access or SpeedStor 12-bit FAT | ||
541 | extended partition */ | ||
542 | { "\xe3" "DOS R/O" }, /* DOS R/O or SpeedStor */ | ||
543 | { "\xe4" "SpeedStor" }, /* SpeedStor 16-bit FAT extended | ||
544 | partition < 1024 cyl. */ | ||
545 | { "\xf1" "SpeedStor" }, | ||
546 | { "\xf4" "SpeedStor" }, /* SpeedStor large partition */ | ||
547 | { "\xfe" "LANstep" }, /* SpeedStor >1024 cyl. or LANstep */ | ||
548 | { "\xff" "BBT" }, /* Xenix Bad Block Table */ | ||
549 | #endif | ||
550 | { 0 } | ||
551 | }; | ||
552 | |||
553 | |||
554 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
555 | /* start_sect and nr_sects are stored little endian on all machines */ | ||
556 | /* moreover, they are not aligned correctly */ | ||
557 | static void | ||
558 | store4_little_endian(unsigned char *cp, unsigned val) | ||
559 | { | ||
560 | cp[0] = val; | ||
561 | cp[1] = val >> 8; | ||
562 | cp[2] = val >> 16; | ||
563 | cp[3] = val >> 24; | ||
564 | } | ||
565 | #endif /* CONFIG_FEATURE_FDISK_WRITABLE */ | ||
566 | |||
567 | static unsigned | ||
568 | read4_little_endian(const unsigned char *cp) | ||
569 | { | ||
570 | return cp[0] + (cp[1] << 8) + (cp[2] << 16) + (cp[3] << 24); | ||
571 | } | ||
572 | |||
573 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
574 | static void | ||
575 | set_start_sect(struct partition *p, unsigned start_sect) | ||
576 | { | ||
577 | store4_little_endian(p->start4, start_sect); | ||
578 | } | ||
579 | #endif | ||
580 | |||
581 | static int32_t | ||
582 | get_start_sect(const struct partition *p) | ||
583 | { | ||
584 | return read4_little_endian(p->start4); | ||
585 | } | ||
586 | |||
587 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
588 | static void | ||
589 | set_nr_sects(struct partition *p, int32_t nr_sects) | ||
590 | { | ||
591 | store4_little_endian(p->size4, nr_sects); | ||
592 | } | ||
593 | #endif | ||
594 | |||
595 | static int32_t | ||
596 | get_nr_sects(const struct partition *p) | ||
597 | { | ||
598 | return read4_little_endian(p->size4); | ||
599 | } | ||
600 | |||
601 | /* normally O_RDWR, -l option gives O_RDONLY */ | ||
602 | static int type_open = O_RDWR; | ||
603 | |||
604 | |||
605 | static int ext_index; /* the prime extended partition */ | ||
606 | static int listing; /* no aborts for fdisk -l */ | ||
607 | static int dos_compatible_flag = ~0; | ||
608 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
609 | static int dos_changed; | ||
610 | static int nowarn; /* no warnings for fdisk -l/-s */ | ||
611 | #endif | ||
612 | |||
613 | |||
614 | |||
615 | static unsigned user_cylinders, user_heads, user_sectors; | ||
616 | static unsigned pt_heads, pt_sectors; | ||
617 | static unsigned kern_heads, kern_sectors; | ||
618 | |||
619 | static off_t extended_offset; /* offset of link pointers */ | ||
620 | |||
621 | static unsigned long long total_number_of_sectors; | ||
622 | |||
623 | |||
624 | static jmp_buf listingbuf; | ||
625 | |||
626 | static void fdisk_fatal(enum failure why) | ||
627 | { | ||
628 | const char *message; | ||
629 | |||
630 | if (listing) { | ||
631 | close(fd); | ||
632 | longjmp(listingbuf, 1); | ||
633 | } | ||
634 | |||
635 | switch (why) { | ||
636 | case unable_to_open: | ||
637 | message = "\nUnable to open %s"; | ||
638 | break; | ||
639 | case unable_to_read: | ||
640 | message = "\nUnable to read %s"; | ||
641 | break; | ||
642 | case unable_to_seek: | ||
643 | message = "\nUnable to seek on %s"; | ||
644 | break; | ||
645 | case unable_to_write: | ||
646 | message = "\nUnable to write %s"; | ||
647 | break; | ||
648 | case ioctl_error: | ||
649 | message = "\nBLKGETSIZE ioctl failed on %s"; | ||
650 | break; | ||
651 | default: | ||
652 | message = "\nFatal error"; | ||
653 | } | ||
654 | |||
655 | bb_error_msg_and_die(message, disk_device); | ||
656 | } | ||
657 | |||
658 | static void | ||
659 | seek_sector(off_t secno) | ||
660 | { | ||
661 | off_t offset = secno * sector_size; | ||
662 | if (lseek(fd, offset, SEEK_SET) == (off_t) -1) | ||
663 | fdisk_fatal(unable_to_seek); | ||
664 | } | ||
665 | |||
666 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
667 | static void | ||
668 | write_sector(off_t secno, char *buf) | ||
669 | { | ||
670 | seek_sector(secno); | ||
671 | if (write(fd, buf, sector_size) != sector_size) | ||
672 | fdisk_fatal(unable_to_write); | ||
673 | } | ||
674 | #endif | ||
675 | |||
676 | /* Allocate a buffer and read a partition table sector */ | ||
677 | static void | ||
678 | read_pte(struct pte *pe, off_t offset) | ||
679 | { | ||
680 | pe->offset = offset; | ||
681 | pe->sectorbuffer = (char *) xmalloc(sector_size); | ||
682 | seek_sector(offset); | ||
683 | if (read(fd, pe->sectorbuffer, sector_size) != sector_size) | ||
684 | fdisk_fatal(unable_to_read); | ||
685 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
686 | pe->changed = 0; | ||
687 | #endif | ||
688 | pe->part_table = pe->ext_pointer = NULL; | ||
689 | } | ||
690 | |||
691 | static unsigned | ||
692 | get_partition_start(const struct pte *pe) | ||
693 | { | ||
694 | return pe->offset + get_start_sect(pe->part_table); | ||
695 | } | ||
696 | |||
697 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
698 | /* | ||
699 | * Avoid warning about DOS partitions when no DOS partition was changed. | ||
700 | * Here a heuristic "is probably dos partition". | ||
701 | * We might also do the opposite and warn in all cases except | ||
702 | * for "is probably nondos partition". | ||
703 | */ | ||
704 | static int | ||
705 | is_dos_partition(int t) | ||
706 | { | ||
707 | return (t == 1 || t == 4 || t == 6 || | ||
708 | t == 0x0b || t == 0x0c || t == 0x0e || | ||
709 | t == 0x11 || t == 0x12 || t == 0x14 || t == 0x16 || | ||
710 | t == 0x1b || t == 0x1c || t == 0x1e || t == 0x24 || | ||
711 | t == 0xc1 || t == 0xc4 || t == 0xc6); | ||
712 | } | ||
713 | |||
714 | static void | ||
715 | menu(void) | ||
716 | { | ||
717 | if (LABEL_IS_SUN) { | ||
718 | puts(_("Command action")); | ||
719 | puts(_("\ta\ttoggle a read only flag")); /* sun */ | ||
720 | puts(_("\tb\tedit bsd disklabel")); | ||
721 | puts(_("\tc\ttoggle the mountable flag")); /* sun */ | ||
722 | puts(_("\td\tdelete a partition")); | ||
723 | puts(_("\tl\tlist known partition types")); | ||
724 | puts(_("\tm\tprint this menu")); | ||
725 | puts(_("\tn\tadd a new partition")); | ||
726 | puts(_("\to\tcreate a new empty DOS partition table")); | ||
727 | puts(_("\tp\tprint the partition table")); | ||
728 | puts(_("\tq\tquit without saving changes")); | ||
729 | puts(_("\ts\tcreate a new empty Sun disklabel")); /* sun */ | ||
730 | puts(_("\tt\tchange a partition's system id")); | ||
731 | puts(_("\tu\tchange display/entry units")); | ||
732 | puts(_("\tv\tverify the partition table")); | ||
733 | puts(_("\tw\twrite table to disk and exit")); | ||
734 | #if ENABLE_FEATURE_FDISK_ADVANCED | ||
735 | puts(_("\tx\textra functionality (experts only)")); | ||
736 | #endif | ||
737 | } else | ||
738 | if (LABEL_IS_SGI) { | ||
739 | puts(_("Command action")); | ||
740 | puts(_("\ta\tselect bootable partition")); /* sgi flavour */ | ||
741 | puts(_("\tb\tedit bootfile entry")); /* sgi */ | ||
742 | puts(_("\tc\tselect sgi swap partition")); /* sgi flavour */ | ||
743 | puts(_("\td\tdelete a partition")); | ||
744 | puts(_("\tl\tlist known partition types")); | ||
745 | puts(_("\tm\tprint this menu")); | ||
746 | puts(_("\tn\tadd a new partition")); | ||
747 | puts(_("\to\tcreate a new empty DOS partition table")); | ||
748 | puts(_("\tp\tprint the partition table")); | ||
749 | puts(_("\tq\tquit without saving changes")); | ||
750 | puts(_("\ts\tcreate a new empty Sun disklabel")); /* sun */ | ||
751 | puts(_("\tt\tchange a partition's system id")); | ||
752 | puts(_("\tu\tchange display/entry units")); | ||
753 | puts(_("\tv\tverify the partition table")); | ||
754 | puts(_("\tw\twrite table to disk and exit")); | ||
755 | } else | ||
756 | if (LABEL_IS_AIX) { | ||
757 | puts(_("Command action")); | ||
758 | puts(_("\tm\tprint this menu")); | ||
759 | puts(_("\to\tcreate a new empty DOS partition table")); | ||
760 | puts(_("\tq\tquit without saving changes")); | ||
761 | puts(_("\ts\tcreate a new empty Sun disklabel")); /* sun */ | ||
762 | } else | ||
763 | { | ||
764 | puts(_("Command action")); | ||
765 | puts(_("\ta\ttoggle a bootable flag")); | ||
766 | puts(_("\tb\tedit bsd disklabel")); | ||
767 | puts(_("\tc\ttoggle the dos compatibility flag")); | ||
768 | puts(_("\td\tdelete a partition")); | ||
769 | puts(_("\tl\tlist known partition types")); | ||
770 | puts(_("\tm\tprint this menu")); | ||
771 | puts(_("\tn\tadd a new partition")); | ||
772 | puts(_("\to\tcreate a new empty DOS partition table")); | ||
773 | puts(_("\tp\tprint the partition table")); | ||
774 | puts(_("\tq\tquit without saving changes")); | ||
775 | puts(_("\ts\tcreate a new empty Sun disklabel")); /* sun */ | ||
776 | puts(_("\tt\tchange a partition's system id")); | ||
777 | puts(_("\tu\tchange display/entry units")); | ||
778 | puts(_("\tv\tverify the partition table")); | ||
779 | puts(_("\tw\twrite table to disk and exit")); | ||
780 | #if ENABLE_FEATURE_FDISK_ADVANCED | ||
781 | puts(_("\tx\textra functionality (experts only)")); | ||
782 | #endif | ||
783 | } | ||
784 | } | ||
785 | #endif /* CONFIG_FEATURE_FDISK_WRITABLE */ | ||
786 | |||
787 | |||
788 | #if ENABLE_FEATURE_FDISK_ADVANCED | ||
789 | static void | ||
790 | xmenu(void) | ||
791 | { | ||
792 | if (LABEL_IS_SUN) { | ||
793 | puts(_("Command action")); | ||
794 | puts(_("\ta\tchange number of alternate cylinders")); /*sun*/ | ||
795 | puts(_("\tc\tchange number of cylinders")); | ||
796 | puts(_("\td\tprint the raw data in the partition table")); | ||
797 | puts(_("\te\tchange number of extra sectors per cylinder"));/*sun*/ | ||
798 | puts(_("\th\tchange number of heads")); | ||
799 | puts(_("\ti\tchange interleave factor")); /*sun*/ | ||
800 | puts(_("\to\tchange rotation speed (rpm)")); /*sun*/ | ||
801 | puts(_("\tm\tprint this menu")); | ||
802 | puts(_("\tp\tprint the partition table")); | ||
803 | puts(_("\tq\tquit without saving changes")); | ||
804 | puts(_("\tr\treturn to main menu")); | ||
805 | puts(_("\ts\tchange number of sectors/track")); | ||
806 | puts(_("\tv\tverify the partition table")); | ||
807 | puts(_("\tw\twrite table to disk and exit")); | ||
808 | puts(_("\ty\tchange number of physical cylinders")); /*sun*/ | ||
809 | } else | ||
810 | if (LABEL_IS_SGI) { | ||
811 | puts(_("Command action")); | ||
812 | puts(_("\tb\tmove beginning of data in a partition")); /* !sun */ | ||
813 | puts(_("\tc\tchange number of cylinders")); | ||
814 | puts(_("\td\tprint the raw data in the partition table")); | ||
815 | puts(_("\te\tlist extended partitions")); /* !sun */ | ||
816 | puts(_("\tg\tcreate an IRIX (SGI) partition table"));/* sgi */ | ||
817 | puts(_("\th\tchange number of heads")); | ||
818 | puts(_("\tm\tprint this menu")); | ||
819 | puts(_("\tp\tprint the partition table")); | ||
820 | puts(_("\tq\tquit without saving changes")); | ||
821 | puts(_("\tr\treturn to main menu")); | ||
822 | puts(_("\ts\tchange number of sectors/track")); | ||
823 | puts(_("\tv\tverify the partition table")); | ||
824 | puts(_("\tw\twrite table to disk and exit")); | ||
825 | } else | ||
826 | if (LABEL_IS_AIX) { | ||
827 | puts(_("Command action")); | ||
828 | puts(_("\tb\tmove beginning of data in a partition")); /* !sun */ | ||
829 | puts(_("\tc\tchange number of cylinders")); | ||
830 | puts(_("\td\tprint the raw data in the partition table")); | ||
831 | puts(_("\te\tlist extended partitions")); /* !sun */ | ||
832 | puts(_("\tg\tcreate an IRIX (SGI) partition table"));/* sgi */ | ||
833 | puts(_("\th\tchange number of heads")); | ||
834 | puts(_("\tm\tprint this menu")); | ||
835 | puts(_("\tp\tprint the partition table")); | ||
836 | puts(_("\tq\tquit without saving changes")); | ||
837 | puts(_("\tr\treturn to main menu")); | ||
838 | puts(_("\ts\tchange number of sectors/track")); | ||
839 | puts(_("\tv\tverify the partition table")); | ||
840 | puts(_("\tw\twrite table to disk and exit")); | ||
841 | } else { | ||
842 | puts(_("Command action")); | ||
843 | puts(_("\tb\tmove beginning of data in a partition")); /* !sun */ | ||
844 | puts(_("\tc\tchange number of cylinders")); | ||
845 | puts(_("\td\tprint the raw data in the partition table")); | ||
846 | puts(_("\te\tlist extended partitions")); /* !sun */ | ||
847 | puts(_("\tf\tfix partition order")); /* !sun, !aix, !sgi */ | ||
848 | #if ENABLE_FEATURE_SGI_LABEL | ||
849 | puts(_("\tg\tcreate an IRIX (SGI) partition table"));/* sgi */ | ||
850 | #endif | ||
851 | puts(_("\th\tchange number of heads")); | ||
852 | puts(_("\tm\tprint this menu")); | ||
853 | puts(_("\tp\tprint the partition table")); | ||
854 | puts(_("\tq\tquit without saving changes")); | ||
855 | puts(_("\tr\treturn to main menu")); | ||
856 | puts(_("\ts\tchange number of sectors/track")); | ||
857 | puts(_("\tv\tverify the partition table")); | ||
858 | puts(_("\tw\twrite table to disk and exit")); | ||
859 | } | ||
860 | } | ||
861 | #endif /* ADVANCED mode */ | ||
862 | |||
863 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
864 | static const struct systypes * | ||
865 | get_sys_types(void) | ||
866 | { | ||
867 | return ( | ||
868 | LABEL_IS_SUN ? sun_sys_types : | ||
869 | LABEL_IS_SGI ? sgi_sys_types : | ||
870 | i386_sys_types); | ||
871 | } | ||
872 | #else | ||
873 | #define get_sys_types() i386_sys_types | ||
874 | #endif /* CONFIG_FEATURE_FDISK_WRITABLE */ | ||
875 | |||
876 | static const char *partition_type(unsigned char type) | ||
877 | { | ||
878 | int i; | ||
879 | const struct systypes *types = get_sys_types(); | ||
880 | |||
881 | for (i = 0; types[i].name; i++) | ||
882 | if ((unsigned char )types[i].name[0] == type) | ||
883 | return types[i].name + 1; | ||
884 | |||
885 | return _("Unknown"); | ||
886 | } | ||
887 | |||
888 | |||
889 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
890 | static int | ||
891 | get_sysid(int i) | ||
892 | { | ||
893 | return LABEL_IS_SUN ? sunlabel->infos[i].id : | ||
894 | (LABEL_IS_SGI ? sgi_get_sysid(i) : | ||
895 | ptes[i].part_table->sys_ind); | ||
896 | } | ||
897 | |||
898 | void list_types(const struct systypes *sys) | ||
899 | { | ||
900 | unsigned last[4], done = 0, next = 0, size; | ||
901 | int i; | ||
902 | |||
903 | for (i = 0; sys[i].name; i++); | ||
904 | size = i; | ||
905 | |||
906 | for (i = 3; i >= 0; i--) | ||
907 | last[3 - i] = done += (size + i - done) / (i + 1); | ||
908 | i = done = 0; | ||
909 | |||
910 | do { | ||
911 | printf("%c%2x %-15.15s", i ? ' ' : '\n', | ||
912 | (unsigned char)sys[next].name[0], | ||
913 | partition_type((unsigned char)sys[next].name[0])); | ||
914 | next = last[i++] + done; | ||
915 | if (i > 3 || next >= last[i]) { | ||
916 | i = 0; | ||
917 | next = ++done; | ||
918 | } | ||
919 | } while (done < last[0]); | ||
920 | putchar('\n'); | ||
921 | } | ||
922 | #endif /* CONFIG_FEATURE_FDISK_WRITABLE */ | ||
923 | |||
924 | static int | ||
925 | is_cleared_partition(const struct partition *p) | ||
926 | { | ||
927 | return !(!p || p->boot_ind || p->head || p->sector || p->cyl || | ||
928 | p->sys_ind || p->end_head || p->end_sector || p->end_cyl || | ||
929 | get_start_sect(p) || get_nr_sects(p)); | ||
930 | } | ||
931 | |||
932 | static void | ||
933 | clear_partition(struct partition *p) | ||
934 | { | ||
935 | if (!p) | ||
936 | return; | ||
937 | memset(p, 0, sizeof(struct partition)); | ||
938 | } | ||
939 | |||
940 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
941 | static void | ||
942 | set_partition(int i, int doext, off_t start, off_t stop, int sysid) | ||
943 | { | ||
944 | struct partition *p; | ||
945 | off_t offset; | ||
946 | |||
947 | if (doext) { | ||
948 | p = ptes[i].ext_pointer; | ||
949 | offset = extended_offset; | ||
950 | } else { | ||
951 | p = ptes[i].part_table; | ||
952 | offset = ptes[i].offset; | ||
953 | } | ||
954 | p->boot_ind = 0; | ||
955 | p->sys_ind = sysid; | ||
956 | set_start_sect(p, start - offset); | ||
957 | set_nr_sects(p, stop - start + 1); | ||
958 | if (dos_compatible_flag && (start/(sectors*heads) > 1023)) | ||
959 | start = heads*sectors*1024 - 1; | ||
960 | set_hsc(p->head, p->sector, p->cyl, start); | ||
961 | if (dos_compatible_flag && (stop/(sectors*heads) > 1023)) | ||
962 | stop = heads*sectors*1024 - 1; | ||
963 | set_hsc(p->end_head, p->end_sector, p->end_cyl, stop); | ||
964 | ptes[i].changed = 1; | ||
965 | } | ||
966 | #endif | ||
967 | |||
968 | static int | ||
969 | test_c(const char **m, const char *mesg) | ||
970 | { | ||
971 | int val = 0; | ||
972 | if (!*m) | ||
973 | printf(_("You must set")); | ||
974 | else { | ||
975 | printf(" %s", *m); | ||
976 | val = 1; | ||
977 | } | ||
978 | *m = mesg; | ||
979 | return val; | ||
980 | } | ||
981 | |||
982 | static int | ||
983 | warn_geometry(void) | ||
984 | { | ||
985 | const char *m = NULL; | ||
986 | int prev = 0; | ||
987 | |||
988 | if (!heads) | ||
989 | prev = test_c(&m, _("heads")); | ||
990 | if (!sectors) | ||
991 | prev = test_c(&m, _("sectors")); | ||
992 | if (!cylinders) | ||
993 | prev = test_c(&m, _("cylinders")); | ||
994 | if (!m) | ||
995 | return 0; | ||
996 | |||
997 | printf("%s%s.\n" | ||
998 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
999 | "You can do this from the extra functions menu.\n" | ||
1000 | #endif | ||
1001 | , prev ? _(" and ") : " ", m); | ||
1002 | |||
1003 | return 1; | ||
1004 | } | ||
1005 | |||
1006 | static void update_units(void) | ||
1007 | { | ||
1008 | int cyl_units = heads * sectors; | ||
1009 | |||
1010 | if (display_in_cyl_units && cyl_units) | ||
1011 | units_per_sector = cyl_units; | ||
1012 | else | ||
1013 | units_per_sector = 1; /* in sectors */ | ||
1014 | } | ||
1015 | |||
1016 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
1017 | static void | ||
1018 | warn_cylinders(void) | ||
1019 | { | ||
1020 | if (LABEL_IS_DOS && cylinders > 1024 && !nowarn) | ||
1021 | printf(_("\n" | ||
1022 | "The number of cylinders for this disk is set to %d.\n" | ||
1023 | "There is nothing wrong with that, but this is larger than 1024,\n" | ||
1024 | "and could in certain setups cause problems with:\n" | ||
1025 | "1) software that runs at boot time (e.g., old versions of LILO)\n" | ||
1026 | "2) booting and partitioning software from other OSs\n" | ||
1027 | " (e.g., DOS FDISK, OS/2 FDISK)\n"), | ||
1028 | cylinders); | ||
1029 | } | ||
1030 | #endif | ||
1031 | |||
1032 | static void | ||
1033 | read_extended(int ext) | ||
1034 | { | ||
1035 | int i; | ||
1036 | struct pte *pex; | ||
1037 | struct partition *p, *q; | ||
1038 | |||
1039 | ext_index = ext; | ||
1040 | pex = &ptes[ext]; | ||
1041 | pex->ext_pointer = pex->part_table; | ||
1042 | |||
1043 | p = pex->part_table; | ||
1044 | if (!get_start_sect(p)) { | ||
1045 | printf(_("Bad offset in primary extended partition\n")); | ||
1046 | return; | ||
1047 | } | ||
1048 | |||
1049 | while (IS_EXTENDED(p->sys_ind)) { | ||
1050 | struct pte *pe = &ptes[partitions]; | ||
1051 | |||
1052 | if (partitions >= MAXIMUM_PARTS) { | ||
1053 | /* This is not a Linux restriction, but | ||
1054 | this program uses arrays of size MAXIMUM_PARTS. | ||
1055 | Do not try to 'improve' this test. */ | ||
1056 | struct pte *pre = &ptes[partitions-1]; | ||
1057 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
1058 | printf(_("Warning: deleting partitions after %d\n"), | ||
1059 | partitions); | ||
1060 | pre->changed = 1; | ||
1061 | #endif | ||
1062 | clear_partition(pre->ext_pointer); | ||
1063 | return; | ||
1064 | } | ||
1065 | |||
1066 | read_pte(pe, extended_offset + get_start_sect(p)); | ||
1067 | |||
1068 | if (!extended_offset) | ||
1069 | extended_offset = get_start_sect(p); | ||
1070 | |||
1071 | q = p = pt_offset(pe->sectorbuffer, 0); | ||
1072 | for (i = 0; i < 4; i++, p++) if (get_nr_sects(p)) { | ||
1073 | if (IS_EXTENDED(p->sys_ind)) { | ||
1074 | if (pe->ext_pointer) | ||
1075 | printf(_("Warning: extra link " | ||
1076 | "pointer in partition table" | ||
1077 | " %d\n"), partitions + 1); | ||
1078 | else | ||
1079 | pe->ext_pointer = p; | ||
1080 | } else if (p->sys_ind) { | ||
1081 | if (pe->part_table) | ||
1082 | printf(_("Warning: ignoring extra " | ||
1083 | "data in partition table" | ||
1084 | " %d\n"), partitions + 1); | ||
1085 | else | ||
1086 | pe->part_table = p; | ||
1087 | } | ||
1088 | } | ||
1089 | |||
1090 | /* very strange code here... */ | ||
1091 | if (!pe->part_table) { | ||
1092 | if (q != pe->ext_pointer) | ||
1093 | pe->part_table = q; | ||
1094 | else | ||
1095 | pe->part_table = q + 1; | ||
1096 | } | ||
1097 | if (!pe->ext_pointer) { | ||
1098 | if (q != pe->part_table) | ||
1099 | pe->ext_pointer = q; | ||
1100 | else | ||
1101 | pe->ext_pointer = q + 1; | ||
1102 | } | ||
1103 | |||
1104 | p = pe->ext_pointer; | ||
1105 | partitions++; | ||
1106 | } | ||
1107 | |||
1108 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
1109 | /* remove empty links */ | ||
1110 | remove: | ||
1111 | for (i = 4; i < partitions; i++) { | ||
1112 | struct pte *pe = &ptes[i]; | ||
1113 | |||
1114 | if (!get_nr_sects(pe->part_table) && | ||
1115 | (partitions > 5 || ptes[4].part_table->sys_ind)) { | ||
1116 | printf("omitting empty partition (%d)\n", i+1); | ||
1117 | delete_partition(i); | ||
1118 | goto remove; /* numbering changed */ | ||
1119 | } | ||
1120 | } | ||
1121 | #endif | ||
1122 | } | ||
1123 | |||
1124 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
1125 | static void | ||
1126 | create_doslabel(void) | ||
1127 | { | ||
1128 | int i; | ||
1129 | |||
1130 | printf( | ||
1131 | _("Building a new DOS disklabel. Changes will remain in memory only,\n" | ||
1132 | "until you decide to write them. After that, of course, the previous\n" | ||
1133 | "content won't be recoverable.\n\n")); | ||
1134 | |||
1135 | current_label_type = label_dos; | ||
1136 | |||
1137 | #if ENABLE_FEATURE_OSF_LABEL | ||
1138 | possibly_osf_label = 0; | ||
1139 | #endif | ||
1140 | partitions = 4; | ||
1141 | |||
1142 | for (i = 510-64; i < 510; i++) | ||
1143 | MBRbuffer[i] = 0; | ||
1144 | write_part_table_flag(MBRbuffer); | ||
1145 | extended_offset = 0; | ||
1146 | set_all_unchanged(); | ||
1147 | set_changed(0); | ||
1148 | get_boot(create_empty_dos); | ||
1149 | } | ||
1150 | #endif /* CONFIG_FEATURE_FDISK_WRITABLE */ | ||
1151 | |||
1152 | static void | ||
1153 | get_sectorsize(void) | ||
1154 | { | ||
1155 | if (!user_set_sector_size) { | ||
1156 | int arg; | ||
1157 | if (ioctl(fd, BLKSSZGET, &arg) == 0) | ||
1158 | sector_size = arg; | ||
1159 | if (sector_size != DEFAULT_SECTOR_SIZE) | ||
1160 | printf(_("Note: sector size is %d (not %d)\n"), | ||
1161 | sector_size, DEFAULT_SECTOR_SIZE); | ||
1162 | } | ||
1163 | } | ||
1164 | |||
1165 | static void | ||
1166 | get_kernel_geometry(void) | ||
1167 | { | ||
1168 | struct hd_geometry geometry; | ||
1169 | |||
1170 | if (!ioctl(fd, HDIO_GETGEO, &geometry)) { | ||
1171 | kern_heads = geometry.heads; | ||
1172 | kern_sectors = geometry.sectors; | ||
1173 | /* never use geometry.cylinders - it is truncated */ | ||
1174 | } | ||
1175 | } | ||
1176 | |||
1177 | static void | ||
1178 | get_partition_table_geometry(void) | ||
1179 | { | ||
1180 | const unsigned char *bufp = (const unsigned char *)MBRbuffer; | ||
1181 | struct partition *p; | ||
1182 | int i, h, s, hh, ss; | ||
1183 | int first = 1; | ||
1184 | int bad = 0; | ||
1185 | |||
1186 | if (!(valid_part_table_flag((char*)bufp))) | ||
1187 | return; | ||
1188 | |||
1189 | hh = ss = 0; | ||
1190 | for (i = 0; i < 4; i++) { | ||
1191 | p = pt_offset(bufp, i); | ||
1192 | if (p->sys_ind != 0) { | ||
1193 | h = p->end_head + 1; | ||
1194 | s = (p->end_sector & 077); | ||
1195 | if (first) { | ||
1196 | hh = h; | ||
1197 | ss = s; | ||
1198 | first = 0; | ||
1199 | } else if (hh != h || ss != s) | ||
1200 | bad = 1; | ||
1201 | } | ||
1202 | } | ||
1203 | |||
1204 | if (!first && !bad) { | ||
1205 | pt_heads = hh; | ||
1206 | pt_sectors = ss; | ||
1207 | } | ||
1208 | } | ||
1209 | |||
1210 | static void | ||
1211 | get_geometry(void) | ||
1212 | { | ||
1213 | int sec_fac; | ||
1214 | unsigned long long bytes; /* really u64 */ | ||
1215 | |||
1216 | get_sectorsize(); | ||
1217 | sec_fac = sector_size / 512; | ||
1218 | #if ENABLE_FEATURE_SUN_LABEL | ||
1219 | guess_device_type(); | ||
1220 | #endif | ||
1221 | heads = cylinders = sectors = 0; | ||
1222 | kern_heads = kern_sectors = 0; | ||
1223 | pt_heads = pt_sectors = 0; | ||
1224 | |||
1225 | get_kernel_geometry(); | ||
1226 | get_partition_table_geometry(); | ||
1227 | |||
1228 | heads = user_heads ? user_heads : | ||
1229 | pt_heads ? pt_heads : | ||
1230 | kern_heads ? kern_heads : 255; | ||
1231 | sectors = user_sectors ? user_sectors : | ||
1232 | pt_sectors ? pt_sectors : | ||
1233 | kern_sectors ? kern_sectors : 63; | ||
1234 | if (ioctl(fd, BLKGETSIZE64, &bytes) == 0) { | ||
1235 | /* got bytes */ | ||
1236 | } else { | ||
1237 | unsigned long longsectors; | ||
1238 | |||
1239 | if (ioctl(fd, BLKGETSIZE, &longsectors)) | ||
1240 | longsectors = 0; | ||
1241 | bytes = ((unsigned long long) longsectors) << 9; | ||
1242 | } | ||
1243 | |||
1244 | total_number_of_sectors = (bytes >> 9); | ||
1245 | |||
1246 | sector_offset = 1; | ||
1247 | if (dos_compatible_flag) | ||
1248 | sector_offset = sectors; | ||
1249 | |||
1250 | cylinders = total_number_of_sectors / (heads * sectors * sec_fac); | ||
1251 | if (!cylinders) | ||
1252 | cylinders = user_cylinders; | ||
1253 | } | ||
1254 | |||
1255 | /* | ||
1256 | * Read MBR. Returns: | ||
1257 | * -1: no 0xaa55 flag present (possibly entire disk BSD) | ||
1258 | * 0: found or created label | ||
1259 | * 1: I/O error | ||
1260 | */ | ||
1261 | static int | ||
1262 | get_boot(enum action what) | ||
1263 | { | ||
1264 | int i; | ||
1265 | |||
1266 | partitions = 4; | ||
1267 | |||
1268 | for (i = 0; i < 4; i++) { | ||
1269 | struct pte *pe = &ptes[i]; | ||
1270 | |||
1271 | pe->part_table = pt_offset(MBRbuffer, i); | ||
1272 | pe->ext_pointer = NULL; | ||
1273 | pe->offset = 0; | ||
1274 | pe->sectorbuffer = MBRbuffer; | ||
1275 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
1276 | pe->changed = (what == create_empty_dos); | ||
1277 | #endif | ||
1278 | } | ||
1279 | |||
1280 | #if ENABLE_FEATURE_SUN_LABEL | ||
1281 | if (what == create_empty_sun && check_sun_label()) | ||
1282 | return 0; | ||
1283 | #endif | ||
1284 | |||
1285 | memset(MBRbuffer, 0, 512); | ||
1286 | |||
1287 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
1288 | if (what == create_empty_dos) | ||
1289 | goto got_dos_table; /* skip reading disk */ | ||
1290 | |||
1291 | if ((fd = open(disk_device, type_open)) < 0) { | ||
1292 | if ((fd = open(disk_device, O_RDONLY)) < 0) { | ||
1293 | if (what == try_only) | ||
1294 | return 1; | ||
1295 | fdisk_fatal(unable_to_open); | ||
1296 | } else | ||
1297 | printf(_("You will not be able to write " | ||
1298 | "the partition table.\n")); | ||
1299 | } | ||
1300 | |||
1301 | if (512 != read(fd, MBRbuffer, 512)) { | ||
1302 | if (what == try_only) | ||
1303 | return 1; | ||
1304 | fdisk_fatal(unable_to_read); | ||
1305 | } | ||
1306 | #else | ||
1307 | if ((fd = open(disk_device, O_RDONLY)) < 0) | ||
1308 | return 1; | ||
1309 | if (512 != read(fd, MBRbuffer, 512)) | ||
1310 | return 1; | ||
1311 | #endif | ||
1312 | |||
1313 | get_geometry(); | ||
1314 | |||
1315 | update_units(); | ||
1316 | |||
1317 | #if ENABLE_FEATURE_SUN_LABEL | ||
1318 | if (check_sun_label()) | ||
1319 | return 0; | ||
1320 | #endif | ||
1321 | |||
1322 | #if ENABLE_FEATURE_SGI_LABEL | ||
1323 | if (check_sgi_label()) | ||
1324 | return 0; | ||
1325 | #endif | ||
1326 | |||
1327 | #if ENABLE_FEATURE_AIX_LABEL | ||
1328 | if (check_aix_label()) | ||
1329 | return 0; | ||
1330 | #endif | ||
1331 | |||
1332 | #if ENABLE_FEATURE_OSF_LABEL | ||
1333 | if (check_osf_label()) { | ||
1334 | possibly_osf_label = 1; | ||
1335 | if (!valid_part_table_flag(MBRbuffer)) { | ||
1336 | current_label_type = label_osf; | ||
1337 | return 0; | ||
1338 | } | ||
1339 | printf(_("This disk has both DOS and BSD magic.\n" | ||
1340 | "Give the 'b' command to go to BSD mode.\n")); | ||
1341 | } | ||
1342 | #endif | ||
1343 | |||
1344 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
1345 | got_dos_table: | ||
1346 | #endif | ||
1347 | |||
1348 | if (!valid_part_table_flag(MBRbuffer)) { | ||
1349 | #ifndef CONFIG_FEATURE_FDISK_WRITABLE | ||
1350 | return -1; | ||
1351 | #else | ||
1352 | switch (what) { | ||
1353 | case fdisk: | ||
1354 | printf(_("Device contains neither a valid DOS " | ||
1355 | "partition table, nor Sun, SGI or OSF " | ||
1356 | "disklabel\n")); | ||
1357 | #ifdef __sparc__ | ||
1358 | #if ENABLE_FEATURE_SUN_LABEL | ||
1359 | create_sunlabel(); | ||
1360 | #endif | ||
1361 | #else | ||
1362 | create_doslabel(); | ||
1363 | #endif | ||
1364 | return 0; | ||
1365 | case try_only: | ||
1366 | return -1; | ||
1367 | case create_empty_dos: | ||
1368 | #if ENABLE_FEATURE_SUN_LABEL | ||
1369 | case create_empty_sun: | ||
1370 | #endif | ||
1371 | break; | ||
1372 | default: | ||
1373 | bb_error_msg_and_die(_("internal error")); | ||
1374 | } | ||
1375 | #endif /* CONFIG_FEATURE_FDISK_WRITABLE */ | ||
1376 | } | ||
1377 | |||
1378 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
1379 | warn_cylinders(); | ||
1380 | #endif | ||
1381 | warn_geometry(); | ||
1382 | |||
1383 | for (i = 0; i < 4; i++) { | ||
1384 | struct pte *pe = &ptes[i]; | ||
1385 | |||
1386 | if (IS_EXTENDED(pe->part_table->sys_ind)) { | ||
1387 | if (partitions != 4) | ||
1388 | printf(_("Ignoring extra extended " | ||
1389 | "partition %d\n"), i + 1); | ||
1390 | else | ||
1391 | read_extended(i); | ||
1392 | } | ||
1393 | } | ||
1394 | |||
1395 | for (i = 3; i < partitions; i++) { | ||
1396 | struct pte *pe = &ptes[i]; | ||
1397 | |||
1398 | if (!valid_part_table_flag(pe->sectorbuffer)) { | ||
1399 | printf(_("Warning: invalid flag 0x%02x,0x%02x of partition " | ||
1400 | "table %d will be corrected by w(rite)\n"), | ||
1401 | pe->sectorbuffer[510], | ||
1402 | pe->sectorbuffer[511], | ||
1403 | i + 1); | ||
1404 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
1405 | pe->changed = 1; | ||
1406 | #endif | ||
1407 | } | ||
1408 | } | ||
1409 | |||
1410 | return 0; | ||
1411 | } | ||
1412 | |||
1413 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
1414 | /* | ||
1415 | * Print the message MESG, then read an integer between LOW and HIGH (inclusive). | ||
1416 | * If the user hits Enter, DFLT is returned. | ||
1417 | * Answers like +10 are interpreted as offsets from BASE. | ||
1418 | * | ||
1419 | * There is no default if DFLT is not between LOW and HIGH. | ||
1420 | */ | ||
1421 | static unsigned | ||
1422 | read_int(unsigned low, unsigned dflt, unsigned high, unsigned base, char *mesg) | ||
1423 | { | ||
1424 | unsigned i; | ||
1425 | int default_ok = 1; | ||
1426 | const char *fmt = "%s (%u-%u, default %u): "; | ||
1427 | |||
1428 | if (dflt < low || dflt > high) { | ||
1429 | fmt = "%s (%u-%u): "; | ||
1430 | default_ok = 0; | ||
1431 | } | ||
1432 | |||
1433 | while (1) { | ||
1434 | int use_default = default_ok; | ||
1435 | |||
1436 | /* ask question and read answer */ | ||
1437 | do { | ||
1438 | printf(fmt, mesg, low, high, dflt); | ||
1439 | read_maybe_empty(""); | ||
1440 | } while (*line_ptr != '\n' && !isdigit(*line_ptr) | ||
1441 | && *line_ptr != '-' && *line_ptr != '+'); | ||
1442 | |||
1443 | if (*line_ptr == '+' || *line_ptr == '-') { | ||
1444 | int minus = (*line_ptr == '-'); | ||
1445 | int absolute = 0; | ||
1446 | |||
1447 | i = atoi(line_ptr + 1); | ||
1448 | |||
1449 | while (isdigit(*++line_ptr)) | ||
1450 | use_default = 0; | ||
1451 | |||
1452 | switch (*line_ptr) { | ||
1453 | case 'c': | ||
1454 | case 'C': | ||
1455 | if (!display_in_cyl_units) | ||
1456 | i *= heads * sectors; | ||
1457 | break; | ||
1458 | case 'K': | ||
1459 | absolute = 1024; | ||
1460 | break; | ||
1461 | case 'k': | ||
1462 | absolute = 1000; | ||
1463 | break; | ||
1464 | case 'm': | ||
1465 | case 'M': | ||
1466 | absolute = 1000000; | ||
1467 | break; | ||
1468 | case 'g': | ||
1469 | case 'G': | ||
1470 | absolute = 1000000000; | ||
1471 | break; | ||
1472 | default: | ||
1473 | break; | ||
1474 | } | ||
1475 | if (absolute) { | ||
1476 | unsigned long long bytes; | ||
1477 | unsigned long unit; | ||
1478 | |||
1479 | bytes = (unsigned long long) i * absolute; | ||
1480 | unit = sector_size * units_per_sector; | ||
1481 | bytes += unit/2; /* round */ | ||
1482 | bytes /= unit; | ||
1483 | i = bytes; | ||
1484 | } | ||
1485 | if (minus) | ||
1486 | i = -i; | ||
1487 | i += base; | ||
1488 | } else { | ||
1489 | i = atoi(line_ptr); | ||
1490 | while (isdigit(*line_ptr)) { | ||
1491 | line_ptr++; | ||
1492 | use_default = 0; | ||
1493 | } | ||
1494 | } | ||
1495 | if (use_default) | ||
1496 | printf(_("Using default value %u\n"), i = dflt); | ||
1497 | if (i >= low && i <= high) | ||
1498 | break; | ||
1499 | else | ||
1500 | printf(_("Value is out of range\n")); | ||
1501 | } | ||
1502 | return i; | ||
1503 | } | ||
1504 | |||
1505 | static int | ||
1506 | get_partition(int warn, int max) | ||
1507 | { | ||
1508 | struct pte *pe; | ||
1509 | int i; | ||
1510 | |||
1511 | i = read_int(1, 0, max, 0, _("Partition number")) - 1; | ||
1512 | pe = &ptes[i]; | ||
1513 | |||
1514 | if (warn) { | ||
1515 | if ((!LABEL_IS_SUN && !LABEL_IS_SGI && !pe->part_table->sys_ind) | ||
1516 | || (LABEL_IS_SUN && (!sunlabel->partitions[i].num_sectors || !sunlabel->infos[i].id)) | ||
1517 | || (LABEL_IS_SGI && !sgi_get_num_sectors(i)) | ||
1518 | ) { | ||
1519 | printf(_("Warning: partition %d has empty type\n"), i+1); | ||
1520 | } | ||
1521 | } | ||
1522 | return i; | ||
1523 | } | ||
1524 | |||
1525 | static int | ||
1526 | get_existing_partition(int warn, int max) | ||
1527 | { | ||
1528 | int pno = -1; | ||
1529 | int i; | ||
1530 | |||
1531 | for (i = 0; i < max; i++) { | ||
1532 | struct pte *pe = &ptes[i]; | ||
1533 | struct partition *p = pe->part_table; | ||
1534 | |||
1535 | if (p && !is_cleared_partition(p)) { | ||
1536 | if (pno >= 0) | ||
1537 | goto not_unique; | ||
1538 | pno = i; | ||
1539 | } | ||
1540 | } | ||
1541 | if (pno >= 0) { | ||
1542 | printf(_("Selected partition %d\n"), pno+1); | ||
1543 | return pno; | ||
1544 | } | ||
1545 | printf(_("No partition is defined yet!\n")); | ||
1546 | return -1; | ||
1547 | |||
1548 | not_unique: | ||
1549 | return get_partition(warn, max); | ||
1550 | } | ||
1551 | |||
1552 | static int | ||
1553 | get_nonexisting_partition(int warn, int max) | ||
1554 | { | ||
1555 | int pno = -1; | ||
1556 | int i; | ||
1557 | |||
1558 | for (i = 0; i < max; i++) { | ||
1559 | struct pte *pe = &ptes[i]; | ||
1560 | struct partition *p = pe->part_table; | ||
1561 | |||
1562 | if (p && is_cleared_partition(p)) { | ||
1563 | if (pno >= 0) | ||
1564 | goto not_unique; | ||
1565 | pno = i; | ||
1566 | } | ||
1567 | } | ||
1568 | if (pno >= 0) { | ||
1569 | printf(_("Selected partition %d\n"), pno+1); | ||
1570 | return pno; | ||
1571 | } | ||
1572 | printf(_("All primary partitions have been defined already!\n")); | ||
1573 | return -1; | ||
1574 | |||
1575 | not_unique: | ||
1576 | return get_partition(warn, max); | ||
1577 | } | ||
1578 | |||
1579 | |||
1580 | static void | ||
1581 | change_units(void) | ||
1582 | { | ||
1583 | display_in_cyl_units = !display_in_cyl_units; | ||
1584 | update_units(); | ||
1585 | printf(_("Changing display/entry units to %s\n"), | ||
1586 | str_units(PLURAL)); | ||
1587 | } | ||
1588 | |||
1589 | static void | ||
1590 | toggle_active(int i) | ||
1591 | { | ||
1592 | struct pte *pe = &ptes[i]; | ||
1593 | struct partition *p = pe->part_table; | ||
1594 | |||
1595 | if (IS_EXTENDED(p->sys_ind) && !p->boot_ind) | ||
1596 | printf(_("WARNING: Partition %d is an extended partition\n"), i + 1); | ||
1597 | p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG); | ||
1598 | pe->changed = 1; | ||
1599 | } | ||
1600 | |||
1601 | static void | ||
1602 | toggle_dos_compatibility_flag(void) | ||
1603 | { | ||
1604 | dos_compatible_flag = ~dos_compatible_flag; | ||
1605 | if (dos_compatible_flag) { | ||
1606 | sector_offset = sectors; | ||
1607 | printf(_("DOS Compatibility flag is set\n")); | ||
1608 | } | ||
1609 | else { | ||
1610 | sector_offset = 1; | ||
1611 | printf(_("DOS Compatibility flag is not set\n")); | ||
1612 | } | ||
1613 | } | ||
1614 | |||
1615 | static void | ||
1616 | delete_partition(int i) | ||
1617 | { | ||
1618 | struct pte *pe = &ptes[i]; | ||
1619 | struct partition *p = pe->part_table; | ||
1620 | struct partition *q = pe->ext_pointer; | ||
1621 | |||
1622 | /* Note that for the fifth partition (i == 4) we don't actually | ||
1623 | * decrement partitions. | ||
1624 | */ | ||
1625 | |||
1626 | if (warn_geometry()) | ||
1627 | return; /* C/H/S not set */ | ||
1628 | pe->changed = 1; | ||
1629 | |||
1630 | if (LABEL_IS_SUN) { | ||
1631 | sun_delete_partition(i); | ||
1632 | return; | ||
1633 | } | ||
1634 | if (LABEL_IS_SGI) { | ||
1635 | sgi_delete_partition(i); | ||
1636 | return; | ||
1637 | } | ||
1638 | |||
1639 | if (i < 4) { | ||
1640 | if (IS_EXTENDED(p->sys_ind) && i == ext_index) { | ||
1641 | partitions = 4; | ||
1642 | ptes[ext_index].ext_pointer = NULL; | ||
1643 | extended_offset = 0; | ||
1644 | } | ||
1645 | clear_partition(p); | ||
1646 | return; | ||
1647 | } | ||
1648 | |||
1649 | if (!q->sys_ind && i > 4) { | ||
1650 | /* the last one in the chain - just delete */ | ||
1651 | --partitions; | ||
1652 | --i; | ||
1653 | clear_partition(ptes[i].ext_pointer); | ||
1654 | ptes[i].changed = 1; | ||
1655 | } else { | ||
1656 | /* not the last one - further ones will be moved down */ | ||
1657 | if (i > 4) { | ||
1658 | /* delete this link in the chain */ | ||
1659 | p = ptes[i-1].ext_pointer; | ||
1660 | *p = *q; | ||
1661 | set_start_sect(p, get_start_sect(q)); | ||
1662 | set_nr_sects(p, get_nr_sects(q)); | ||
1663 | ptes[i-1].changed = 1; | ||
1664 | } else if (partitions > 5) { /* 5 will be moved to 4 */ | ||
1665 | /* the first logical in a longer chain */ | ||
1666 | pe = &ptes[5]; | ||
1667 | |||
1668 | if (pe->part_table) /* prevent SEGFAULT */ | ||
1669 | set_start_sect(pe->part_table, | ||
1670 | get_partition_start(pe) - | ||
1671 | extended_offset); | ||
1672 | pe->offset = extended_offset; | ||
1673 | pe->changed = 1; | ||
1674 | } | ||
1675 | |||
1676 | if (partitions > 5) { | ||
1677 | partitions--; | ||
1678 | while (i < partitions) { | ||
1679 | ptes[i] = ptes[i+1]; | ||
1680 | i++; | ||
1681 | } | ||
1682 | } else | ||
1683 | /* the only logical: clear only */ | ||
1684 | clear_partition(ptes[i].part_table); | ||
1685 | } | ||
1686 | } | ||
1687 | |||
1688 | static void | ||
1689 | change_sysid(void) | ||
1690 | { | ||
1691 | int i, sys, origsys; | ||
1692 | struct partition *p; | ||
1693 | |||
1694 | /* If sgi_label then don't use get_existing_partition, | ||
1695 | let the user select a partition, since get_existing_partition() | ||
1696 | only works for Linux like partition tables. */ | ||
1697 | if (!LABEL_IS_SGI) { | ||
1698 | i = get_existing_partition(0, partitions); | ||
1699 | } else { | ||
1700 | i = get_partition(0, partitions); | ||
1701 | } | ||
1702 | if (i == -1) | ||
1703 | return; | ||
1704 | p = ptes[i].part_table; | ||
1705 | origsys = sys = get_sysid(i); | ||
1706 | |||
1707 | /* if changing types T to 0 is allowed, then | ||
1708 | the reverse change must be allowed, too */ | ||
1709 | if (!sys && !LABEL_IS_SGI && !LABEL_IS_SUN && !get_nr_sects(p)) { | ||
1710 | printf(_("Partition %d does not exist yet!\n"), i + 1); | ||
1711 | return; | ||
1712 | } | ||
1713 | while (1) { | ||
1714 | sys = read_hex (get_sys_types()); | ||
1715 | |||
1716 | if (!sys && !LABEL_IS_SGI && !LABEL_IS_SUN) { | ||
1717 | printf(_("Type 0 means free space to many systems\n" | ||
1718 | "(but not to Linux). Having partitions of\n" | ||
1719 | "type 0 is probably unwise. You can delete\n" | ||
1720 | "a partition using the 'd' command.\n")); | ||
1721 | /* break; */ | ||
1722 | } | ||
1723 | |||
1724 | if (!LABEL_IS_SUN && !LABEL_IS_SGI) { | ||
1725 | if (IS_EXTENDED(sys) != IS_EXTENDED(p->sys_ind)) { | ||
1726 | printf(_("You cannot change a partition into" | ||
1727 | " an extended one or vice versa\n" | ||
1728 | "Delete it first.\n")); | ||
1729 | break; | ||
1730 | } | ||
1731 | } | ||
1732 | |||
1733 | if (sys < 256) { | ||
1734 | if (LABEL_IS_SUN && i == 2 && sys != SUN_WHOLE_DISK) | ||
1735 | printf(_("Consider leaving partition 3 " | ||
1736 | "as Whole disk (5),\n" | ||
1737 | "as SunOS/Solaris expects it and " | ||
1738 | "even Linux likes it.\n\n")); | ||
1739 | if (LABEL_IS_SGI && | ||
1740 | ( | ||
1741 | (i == 10 && sys != SGI_ENTIRE_DISK) || | ||
1742 | (i == 8 && sys != 0) | ||
1743 | ) | ||
1744 | ){ | ||
1745 | printf(_("Consider leaving partition 9 " | ||
1746 | "as volume header (0),\nand " | ||
1747 | "partition 11 as entire volume (6)" | ||
1748 | "as IRIX expects it.\n\n")); | ||
1749 | } | ||
1750 | if (sys == origsys) | ||
1751 | break; | ||
1752 | if (LABEL_IS_SUN) { | ||
1753 | sun_change_sysid(i, sys); | ||
1754 | } else if (LABEL_IS_SGI) { | ||
1755 | sgi_change_sysid(i, sys); | ||
1756 | } else | ||
1757 | p->sys_ind = sys; | ||
1758 | |||
1759 | printf(_("Changed system type of partition %d " | ||
1760 | "to %x (%s)\n"), i + 1, sys, | ||
1761 | partition_type(sys)); | ||
1762 | ptes[i].changed = 1; | ||
1763 | if (is_dos_partition(origsys) || | ||
1764 | is_dos_partition(sys)) | ||
1765 | dos_changed = 1; | ||
1766 | break; | ||
1767 | } | ||
1768 | } | ||
1769 | } | ||
1770 | #endif /* CONFIG_FEATURE_FDISK_WRITABLE */ | ||
1771 | |||
1772 | |||
1773 | /* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993, | ||
1774 | * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross, | ||
1775 | * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S. | ||
1776 | * Lubkin Oct. 1991). */ | ||
1777 | |||
1778 | static void | ||
1779 | long2chs(ulong ls, unsigned *c, unsigned *h, unsigned *s) | ||
1780 | { | ||
1781 | int spc = heads * sectors; | ||
1782 | |||
1783 | *c = ls / spc; | ||
1784 | ls = ls % spc; | ||
1785 | *h = ls / sectors; | ||
1786 | *s = ls % sectors + 1; /* sectors count from 1 */ | ||
1787 | } | ||
1788 | |||
1789 | static void | ||
1790 | check_consistency(const struct partition *p, int partition) | ||
1791 | { | ||
1792 | unsigned pbc, pbh, pbs; /* physical beginning c, h, s */ | ||
1793 | unsigned pec, peh, pes; /* physical ending c, h, s */ | ||
1794 | unsigned lbc, lbh, lbs; /* logical beginning c, h, s */ | ||
1795 | unsigned lec, leh, les; /* logical ending c, h, s */ | ||
1796 | |||
1797 | if (!heads || !sectors || (partition >= 4)) | ||
1798 | return; /* do not check extended partitions */ | ||
1799 | |||
1800 | /* physical beginning c, h, s */ | ||
1801 | pbc = (p->cyl & 0xff) | ((p->sector << 2) & 0x300); | ||
1802 | pbh = p->head; | ||
1803 | pbs = p->sector & 0x3f; | ||
1804 | |||
1805 | /* physical ending c, h, s */ | ||
1806 | pec = (p->end_cyl & 0xff) | ((p->end_sector << 2) & 0x300); | ||
1807 | peh = p->end_head; | ||
1808 | pes = p->end_sector & 0x3f; | ||
1809 | |||
1810 | /* compute logical beginning (c, h, s) */ | ||
1811 | long2chs(get_start_sect(p), &lbc, &lbh, &lbs); | ||
1812 | |||
1813 | /* compute logical ending (c, h, s) */ | ||
1814 | long2chs(get_start_sect(p) + get_nr_sects(p) - 1, &lec, &leh, &les); | ||
1815 | |||
1816 | /* Same physical / logical beginning? */ | ||
1817 | if (cylinders <= 1024 && (pbc != lbc || pbh != lbh || pbs != lbs)) { | ||
1818 | printf(_("Partition %d has different physical/logical " | ||
1819 | "beginnings (non-Linux?):\n"), partition + 1); | ||
1820 | printf(_(" phys=(%d, %d, %d) "), pbc, pbh, pbs); | ||
1821 | printf(_("logical=(%d, %d, %d)\n"),lbc, lbh, lbs); | ||
1822 | } | ||
1823 | |||
1824 | /* Same physical / logical ending? */ | ||
1825 | if (cylinders <= 1024 && (pec != lec || peh != leh || pes != les)) { | ||
1826 | printf(_("Partition %d has different physical/logical " | ||
1827 | "endings:\n"), partition + 1); | ||
1828 | printf(_(" phys=(%d, %d, %d) "), pec, peh, pes); | ||
1829 | printf(_("logical=(%d, %d, %d)\n"),lec, leh, les); | ||
1830 | } | ||
1831 | |||
1832 | /* Ending on cylinder boundary? */ | ||
1833 | if (peh != (heads - 1) || pes != sectors) { | ||
1834 | printf(_("Partition %i does not end on cylinder boundary.\n"), | ||
1835 | partition + 1); | ||
1836 | } | ||
1837 | } | ||
1838 | |||
1839 | static void | ||
1840 | list_disk_geometry(void) | ||
1841 | { | ||
1842 | long long bytes = (total_number_of_sectors << 9); | ||
1843 | long megabytes = bytes/1000000; | ||
1844 | |||
1845 | if (megabytes < 10000) | ||
1846 | printf(_("\nDisk %s: %ld MB, %lld bytes\n"), | ||
1847 | disk_device, megabytes, bytes); | ||
1848 | else | ||
1849 | printf(_("\nDisk %s: %ld.%ld GB, %lld bytes\n"), | ||
1850 | disk_device, megabytes/1000, (megabytes/100)%10, bytes); | ||
1851 | printf(_("%d heads, %d sectors/track, %d cylinders"), | ||
1852 | heads, sectors, cylinders); | ||
1853 | if (units_per_sector == 1) | ||
1854 | printf(_(", total %llu sectors"), | ||
1855 | total_number_of_sectors / (sector_size/512)); | ||
1856 | printf(_("\nUnits = %s of %d * %d = %d bytes\n\n"), | ||
1857 | str_units(PLURAL), | ||
1858 | units_per_sector, sector_size, units_per_sector * sector_size); | ||
1859 | } | ||
1860 | |||
1861 | /* | ||
1862 | * Check whether partition entries are ordered by their starting positions. | ||
1863 | * Return 0 if OK. Return i if partition i should have been earlier. | ||
1864 | * Two separate checks: primary and logical partitions. | ||
1865 | */ | ||
1866 | static int | ||
1867 | wrong_p_order(int *prev) | ||
1868 | { | ||
1869 | const struct pte *pe; | ||
1870 | const struct partition *p; | ||
1871 | off_t last_p_start_pos = 0, p_start_pos; | ||
1872 | int i, last_i = 0; | ||
1873 | |||
1874 | for (i = 0 ; i < partitions; i++) { | ||
1875 | if (i == 4) { | ||
1876 | last_i = 4; | ||
1877 | last_p_start_pos = 0; | ||
1878 | } | ||
1879 | pe = &ptes[i]; | ||
1880 | if ((p = pe->part_table)->sys_ind) { | ||
1881 | p_start_pos = get_partition_start(pe); | ||
1882 | |||
1883 | if (last_p_start_pos > p_start_pos) { | ||
1884 | if (prev) | ||
1885 | *prev = last_i; | ||
1886 | return i; | ||
1887 | } | ||
1888 | |||
1889 | last_p_start_pos = p_start_pos; | ||
1890 | last_i = i; | ||
1891 | } | ||
1892 | } | ||
1893 | return 0; | ||
1894 | } | ||
1895 | |||
1896 | #if ENABLE_FEATURE_FDISK_ADVANCED | ||
1897 | /* | ||
1898 | * Fix the chain of logicals. | ||
1899 | * extended_offset is unchanged, the set of sectors used is unchanged | ||
1900 | * The chain is sorted so that sectors increase, and so that | ||
1901 | * starting sectors increase. | ||
1902 | * | ||
1903 | * After this it may still be that cfdisk doesnt like the table. | ||
1904 | * (This is because cfdisk considers expanded parts, from link to | ||
1905 | * end of partition, and these may still overlap.) | ||
1906 | * Now | ||
1907 | * sfdisk /dev/hda > ohda; sfdisk /dev/hda < ohda | ||
1908 | * may help. | ||
1909 | */ | ||
1910 | static void | ||
1911 | fix_chain_of_logicals(void) | ||
1912 | { | ||
1913 | int j, oj, ojj, sj, sjj; | ||
1914 | struct partition *pj,*pjj,tmp; | ||
1915 | |||
1916 | /* Stage 1: sort sectors but leave sector of part 4 */ | ||
1917 | /* (Its sector is the global extended_offset.) */ | ||
1918 | stage1: | ||
1919 | for (j = 5; j < partitions-1; j++) { | ||
1920 | oj = ptes[j].offset; | ||
1921 | ojj = ptes[j+1].offset; | ||
1922 | if (oj > ojj) { | ||
1923 | ptes[j].offset = ojj; | ||
1924 | ptes[j+1].offset = oj; | ||
1925 | pj = ptes[j].part_table; | ||
1926 | set_start_sect(pj, get_start_sect(pj)+oj-ojj); | ||
1927 | pjj = ptes[j+1].part_table; | ||
1928 | set_start_sect(pjj, get_start_sect(pjj)+ojj-oj); | ||
1929 | set_start_sect(ptes[j-1].ext_pointer, | ||
1930 | ojj-extended_offset); | ||
1931 | set_start_sect(ptes[j].ext_pointer, | ||
1932 | oj-extended_offset); | ||
1933 | goto stage1; | ||
1934 | } | ||
1935 | } | ||
1936 | |||
1937 | /* Stage 2: sort starting sectors */ | ||
1938 | stage2: | ||
1939 | for (j = 4; j < partitions-1; j++) { | ||
1940 | pj = ptes[j].part_table; | ||
1941 | pjj = ptes[j+1].part_table; | ||
1942 | sj = get_start_sect(pj); | ||
1943 | sjj = get_start_sect(pjj); | ||
1944 | oj = ptes[j].offset; | ||
1945 | ojj = ptes[j+1].offset; | ||
1946 | if (oj+sj > ojj+sjj) { | ||
1947 | tmp = *pj; | ||
1948 | *pj = *pjj; | ||
1949 | *pjj = tmp; | ||
1950 | set_start_sect(pj, ojj+sjj-oj); | ||
1951 | set_start_sect(pjj, oj+sj-ojj); | ||
1952 | goto stage2; | ||
1953 | } | ||
1954 | } | ||
1955 | |||
1956 | /* Probably something was changed */ | ||
1957 | for (j = 4; j < partitions; j++) | ||
1958 | ptes[j].changed = 1; | ||
1959 | } | ||
1960 | |||
1961 | |||
1962 | static void | ||
1963 | fix_partition_table_order(void) | ||
1964 | { | ||
1965 | struct pte *pei, *pek; | ||
1966 | int i,k; | ||
1967 | |||
1968 | if (!wrong_p_order(NULL)) { | ||
1969 | printf(_("Nothing to do. Ordering is correct already.\n\n")); | ||
1970 | return; | ||
1971 | } | ||
1972 | |||
1973 | while ((i = wrong_p_order(&k)) != 0 && i < 4) { | ||
1974 | /* partition i should have come earlier, move it */ | ||
1975 | /* We have to move data in the MBR */ | ||
1976 | struct partition *pi, *pk, *pe, pbuf; | ||
1977 | pei = &ptes[i]; | ||
1978 | pek = &ptes[k]; | ||
1979 | |||
1980 | pe = pei->ext_pointer; | ||
1981 | pei->ext_pointer = pek->ext_pointer; | ||
1982 | pek->ext_pointer = pe; | ||
1983 | |||
1984 | pi = pei->part_table; | ||
1985 | pk = pek->part_table; | ||
1986 | |||
1987 | memmove(&pbuf, pi, sizeof(struct partition)); | ||
1988 | memmove(pi, pk, sizeof(struct partition)); | ||
1989 | memmove(pk, &pbuf, sizeof(struct partition)); | ||
1990 | |||
1991 | pei->changed = pek->changed = 1; | ||
1992 | } | ||
1993 | |||
1994 | if (i) | ||
1995 | fix_chain_of_logicals(); | ||
1996 | |||
1997 | printf("Done.\n"); | ||
1998 | |||
1999 | } | ||
2000 | #endif | ||
2001 | |||
2002 | static void | ||
2003 | list_table(int xtra) | ||
2004 | { | ||
2005 | const struct partition *p; | ||
2006 | int i, w; | ||
2007 | |||
2008 | if (LABEL_IS_SUN) { | ||
2009 | sun_list_table(xtra); | ||
2010 | return; | ||
2011 | } | ||
2012 | if (LABEL_IS_SUN) { | ||
2013 | sgi_list_table(xtra); | ||
2014 | return; | ||
2015 | } | ||
2016 | |||
2017 | list_disk_geometry(); | ||
2018 | |||
2019 | if (LABEL_IS_OSF) { | ||
2020 | xbsd_print_disklabel(xtra); | ||
2021 | return; | ||
2022 | } | ||
2023 | |||
2024 | /* Heuristic: we list partition 3 of /dev/foo as /dev/foo3, | ||
2025 | but if the device name ends in a digit, say /dev/foo1, | ||
2026 | then the partition is called /dev/foo1p3. */ | ||
2027 | w = strlen(disk_device); | ||
2028 | if (w && isdigit(disk_device[w-1])) | ||
2029 | w++; | ||
2030 | if (w < 5) | ||
2031 | w = 5; | ||
2032 | |||
2033 | // 1 12345678901 12345678901 12345678901 12 | ||
2034 | printf(_("%*s Boot Start End Blocks Id System\n"), | ||
2035 | w+1, _("Device")); | ||
2036 | |||
2037 | for (i = 0; i < partitions; i++) { | ||
2038 | const struct pte *pe = &ptes[i]; | ||
2039 | off_t psects; | ||
2040 | off_t pblocks; | ||
2041 | unsigned podd; | ||
2042 | |||
2043 | p = pe->part_table; | ||
2044 | if (!p || is_cleared_partition(p)) | ||
2045 | continue; | ||
2046 | |||
2047 | psects = get_nr_sects(p); | ||
2048 | pblocks = psects; | ||
2049 | podd = 0; | ||
2050 | |||
2051 | if (sector_size < 1024) { | ||
2052 | pblocks /= (1024 / sector_size); | ||
2053 | podd = psects % (1024 / sector_size); | ||
2054 | } | ||
2055 | if (sector_size > 1024) | ||
2056 | pblocks *= (sector_size / 1024); | ||
2057 | |||
2058 | printf("%s %c %11llu %11llu %11llu%c %2x %s\n", | ||
2059 | partname(disk_device, i+1, w+2), | ||
2060 | !p->boot_ind ? ' ' : p->boot_ind == ACTIVE_FLAG /* boot flag */ | ||
2061 | ? '*' : '?', | ||
2062 | (unsigned long long) cround(get_partition_start(pe)), /* start */ | ||
2063 | (unsigned long long) cround(get_partition_start(pe) + psects /* end */ | ||
2064 | - (psects ? 1 : 0)), | ||
2065 | (unsigned long long) pblocks, podd ? '+' : ' ', /* odd flag on end */ | ||
2066 | p->sys_ind, /* type id */ | ||
2067 | partition_type(p->sys_ind)); /* type name */ | ||
2068 | |||
2069 | check_consistency(p, i); | ||
2070 | } | ||
2071 | |||
2072 | /* Is partition table in disk order? It need not be, but... */ | ||
2073 | /* partition table entries are not checked for correct order if this | ||
2074 | is a sgi, sun or aix labeled disk... */ | ||
2075 | if (LABEL_IS_DOS && wrong_p_order(NULL)) { | ||
2076 | /* FIXME */ | ||
2077 | printf(_("\nPartition table entries are not in disk order\n")); | ||
2078 | } | ||
2079 | } | ||
2080 | |||
2081 | #if ENABLE_FEATURE_FDISK_ADVANCED | ||
2082 | static void | ||
2083 | x_list_table(int extend) | ||
2084 | { | ||
2085 | const struct pte *pe; | ||
2086 | const struct partition *p; | ||
2087 | int i; | ||
2088 | |||
2089 | printf(_("\nDisk %s: %d heads, %d sectors, %d cylinders\n\n"), | ||
2090 | disk_device, heads, sectors, cylinders); | ||
2091 | printf(_("Nr AF Hd Sec Cyl Hd Sec Cyl Start Size ID\n")); | ||
2092 | for (i = 0 ; i < partitions; i++) { | ||
2093 | pe = &ptes[i]; | ||
2094 | p = (extend ? pe->ext_pointer : pe->part_table); | ||
2095 | if (p != NULL) { | ||
2096 | printf("%2d %02x%4d%4d%5d%4d%4d%5d%11u%11u %02x\n", | ||
2097 | i + 1, p->boot_ind, p->head, | ||
2098 | sector(p->sector), | ||
2099 | cylinder(p->sector, p->cyl), p->end_head, | ||
2100 | sector(p->end_sector), | ||
2101 | cylinder(p->end_sector, p->end_cyl), | ||
2102 | get_start_sect(p), get_nr_sects(p), p->sys_ind); | ||
2103 | if (p->sys_ind) | ||
2104 | check_consistency(p, i); | ||
2105 | } | ||
2106 | } | ||
2107 | } | ||
2108 | #endif | ||
2109 | |||
2110 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
2111 | static void | ||
2112 | fill_bounds(off_t *first, off_t *last) | ||
2113 | { | ||
2114 | int i; | ||
2115 | const struct pte *pe = &ptes[0]; | ||
2116 | const struct partition *p; | ||
2117 | |||
2118 | for (i = 0; i < partitions; pe++,i++) { | ||
2119 | p = pe->part_table; | ||
2120 | if (!p->sys_ind || IS_EXTENDED(p->sys_ind)) { | ||
2121 | first[i] = 0xffffffff; | ||
2122 | last[i] = 0; | ||
2123 | } else { | ||
2124 | first[i] = get_partition_start(pe); | ||
2125 | last[i] = first[i] + get_nr_sects(p) - 1; | ||
2126 | } | ||
2127 | } | ||
2128 | } | ||
2129 | |||
2130 | static void | ||
2131 | check(int n, unsigned h, unsigned s, unsigned c, off_t start) | ||
2132 | { | ||
2133 | off_t total, real_s, real_c; | ||
2134 | |||
2135 | real_s = sector(s) - 1; | ||
2136 | real_c = cylinder(s, c); | ||
2137 | total = (real_c * sectors + real_s) * heads + h; | ||
2138 | if (!total) | ||
2139 | printf(_("Warning: partition %d contains sector 0\n"), n); | ||
2140 | if (h >= heads) | ||
2141 | printf(_("Partition %d: head %d greater than maximum %d\n"), | ||
2142 | n, h + 1, heads); | ||
2143 | if (real_s >= sectors) | ||
2144 | printf(_("Partition %d: sector %d greater than " | ||
2145 | "maximum %d\n"), n, s, sectors); | ||
2146 | if (real_c >= cylinders) | ||
2147 | printf(_("Partitions %d: cylinder %llu greater than " | ||
2148 | "maximum %d\n"), n, (unsigned long long)real_c + 1, cylinders); | ||
2149 | if (cylinders <= 1024 && start != total) | ||
2150 | printf(_("Partition %d: previous sectors %llu disagrees with " | ||
2151 | "total %llu\n"), n, (unsigned long long)start, (unsigned long long)total); | ||
2152 | } | ||
2153 | |||
2154 | static void | ||
2155 | verify(void) | ||
2156 | { | ||
2157 | int i, j; | ||
2158 | unsigned total = 1; | ||
2159 | off_t first[partitions], last[partitions]; | ||
2160 | struct partition *p; | ||
2161 | |||
2162 | if (warn_geometry()) | ||
2163 | return; | ||
2164 | |||
2165 | if (LABEL_IS_SUN) { | ||
2166 | verify_sun(); | ||
2167 | return; | ||
2168 | } | ||
2169 | if (LABEL_IS_SGI) { | ||
2170 | verify_sgi(1); | ||
2171 | return; | ||
2172 | } | ||
2173 | |||
2174 | fill_bounds(first, last); | ||
2175 | for (i = 0; i < partitions; i++) { | ||
2176 | struct pte *pe = &ptes[i]; | ||
2177 | |||
2178 | p = pe->part_table; | ||
2179 | if (p->sys_ind && !IS_EXTENDED(p->sys_ind)) { | ||
2180 | check_consistency(p, i); | ||
2181 | if (get_partition_start(pe) < first[i]) | ||
2182 | printf(_("Warning: bad start-of-data in " | ||
2183 | "partition %d\n"), i + 1); | ||
2184 | check(i + 1, p->end_head, p->end_sector, p->end_cyl, | ||
2185 | last[i]); | ||
2186 | total += last[i] + 1 - first[i]; | ||
2187 | for (j = 0; j < i; j++) | ||
2188 | if ((first[i] >= first[j] && first[i] <= last[j]) | ||
2189 | || ((last[i] <= last[j] && last[i] >= first[j]))) { | ||
2190 | printf(_("Warning: partition %d overlaps " | ||
2191 | "partition %d.\n"), j + 1, i + 1); | ||
2192 | total += first[i] >= first[j] ? | ||
2193 | first[i] : first[j]; | ||
2194 | total -= last[i] <= last[j] ? | ||
2195 | last[i] : last[j]; | ||
2196 | } | ||
2197 | } | ||
2198 | } | ||
2199 | |||
2200 | if (extended_offset) { | ||
2201 | struct pte *pex = &ptes[ext_index]; | ||
2202 | off_t e_last = get_start_sect(pex->part_table) + | ||
2203 | get_nr_sects(pex->part_table) - 1; | ||
2204 | |||
2205 | for (i = 4; i < partitions; i++) { | ||
2206 | total++; | ||
2207 | p = ptes[i].part_table; | ||
2208 | if (!p->sys_ind) { | ||
2209 | if (i != 4 || i + 1 < partitions) | ||
2210 | printf(_("Warning: partition %d " | ||
2211 | "is empty\n"), i + 1); | ||
2212 | } | ||
2213 | else if (first[i] < extended_offset || | ||
2214 | last[i] > e_last) | ||
2215 | printf(_("Logical partition %d not entirely in " | ||
2216 | "partition %d\n"), i + 1, ext_index + 1); | ||
2217 | } | ||
2218 | } | ||
2219 | |||
2220 | if (total > heads * sectors * cylinders) | ||
2221 | printf(_("Total allocated sectors %d greater than the maximum " | ||
2222 | "%d\n"), total, heads * sectors * cylinders); | ||
2223 | else if ((total = heads * sectors * cylinders - total) != 0) | ||
2224 | printf(_("%d unallocated sectors\n"), total); | ||
2225 | } | ||
2226 | |||
2227 | static void | ||
2228 | add_partition(int n, int sys) | ||
2229 | { | ||
2230 | char mesg[256]; /* 48 does not suffice in Japanese */ | ||
2231 | int i, num_read = 0; | ||
2232 | struct partition *p = ptes[n].part_table; | ||
2233 | struct partition *q = ptes[ext_index].part_table; | ||
2234 | long long llimit; | ||
2235 | off_t start, stop = 0, limit, temp, | ||
2236 | first[partitions], last[partitions]; | ||
2237 | |||
2238 | if (p && p->sys_ind) { | ||
2239 | printf(_("Partition %d is already defined. Delete " | ||
2240 | "it before re-adding it.\n"), n + 1); | ||
2241 | return; | ||
2242 | } | ||
2243 | fill_bounds(first, last); | ||
2244 | if (n < 4) { | ||
2245 | start = sector_offset; | ||
2246 | if (display_in_cyl_units || !total_number_of_sectors) | ||
2247 | llimit = heads * sectors * cylinders - 1; | ||
2248 | else | ||
2249 | llimit = total_number_of_sectors - 1; | ||
2250 | limit = llimit; | ||
2251 | if (limit != llimit) | ||
2252 | limit = 0x7fffffff; | ||
2253 | if (extended_offset) { | ||
2254 | first[ext_index] = extended_offset; | ||
2255 | last[ext_index] = get_start_sect(q) + | ||
2256 | get_nr_sects(q) - 1; | ||
2257 | } | ||
2258 | } else { | ||
2259 | start = extended_offset + sector_offset; | ||
2260 | limit = get_start_sect(q) + get_nr_sects(q) - 1; | ||
2261 | } | ||
2262 | if (display_in_cyl_units) | ||
2263 | for (i = 0; i < partitions; i++) | ||
2264 | first[i] = (cround(first[i]) - 1) * units_per_sector; | ||
2265 | |||
2266 | snprintf(mesg, sizeof(mesg), _("First %s"), str_units(SINGULAR)); | ||
2267 | do { | ||
2268 | temp = start; | ||
2269 | for (i = 0; i < partitions; i++) { | ||
2270 | int lastplusoff; | ||
2271 | |||
2272 | if (start == ptes[i].offset) | ||
2273 | start += sector_offset; | ||
2274 | lastplusoff = last[i] + ((n < 4) ? 0 : sector_offset); | ||
2275 | if (start >= first[i] && start <= lastplusoff) | ||
2276 | start = lastplusoff + 1; | ||
2277 | } | ||
2278 | if (start > limit) | ||
2279 | break; | ||
2280 | if (start >= temp+units_per_sector && num_read) { | ||
2281 | printf(_("Sector %"OFF_FMT"d is already allocated\n"), temp); | ||
2282 | temp = start; | ||
2283 | num_read = 0; | ||
2284 | } | ||
2285 | if (!num_read && start == temp) { | ||
2286 | off_t saved_start; | ||
2287 | |||
2288 | saved_start = start; | ||
2289 | start = read_int(cround(saved_start), cround(saved_start), cround(limit), | ||
2290 | 0, mesg); | ||
2291 | if (display_in_cyl_units) { | ||
2292 | start = (start - 1) * units_per_sector; | ||
2293 | if (start < saved_start) start = saved_start; | ||
2294 | } | ||
2295 | num_read = 1; | ||
2296 | } | ||
2297 | } while (start != temp || !num_read); | ||
2298 | if (n > 4) { /* NOT for fifth partition */ | ||
2299 | struct pte *pe = &ptes[n]; | ||
2300 | |||
2301 | pe->offset = start - sector_offset; | ||
2302 | if (pe->offset == extended_offset) { /* must be corrected */ | ||
2303 | pe->offset++; | ||
2304 | if (sector_offset == 1) | ||
2305 | start++; | ||
2306 | } | ||
2307 | } | ||
2308 | |||
2309 | for (i = 0; i < partitions; i++) { | ||
2310 | struct pte *pe = &ptes[i]; | ||
2311 | |||
2312 | if (start < pe->offset && limit >= pe->offset) | ||
2313 | limit = pe->offset - 1; | ||
2314 | if (start < first[i] && limit >= first[i]) | ||
2315 | limit = first[i] - 1; | ||
2316 | } | ||
2317 | if (start > limit) { | ||
2318 | printf(_("No free sectors available\n")); | ||
2319 | if (n > 4) | ||
2320 | partitions--; | ||
2321 | return; | ||
2322 | } | ||
2323 | if (cround(start) == cround(limit)) { | ||
2324 | stop = limit; | ||
2325 | } else { | ||
2326 | snprintf(mesg, sizeof(mesg), | ||
2327 | _("Last %s or +size or +sizeM or +sizeK"), | ||
2328 | str_units(SINGULAR)); | ||
2329 | stop = read_int(cround(start), cround(limit), cround(limit), | ||
2330 | cround(start), mesg); | ||
2331 | if (display_in_cyl_units) { | ||
2332 | stop = stop * units_per_sector - 1; | ||
2333 | if (stop >limit) | ||
2334 | stop = limit; | ||
2335 | } | ||
2336 | } | ||
2337 | |||
2338 | set_partition(n, 0, start, stop, sys); | ||
2339 | if (n > 4) | ||
2340 | set_partition(n - 1, 1, ptes[n].offset, stop, EXTENDED); | ||
2341 | |||
2342 | if (IS_EXTENDED(sys)) { | ||
2343 | struct pte *pe4 = &ptes[4]; | ||
2344 | struct pte *pen = &ptes[n]; | ||
2345 | |||
2346 | ext_index = n; | ||
2347 | pen->ext_pointer = p; | ||
2348 | pe4->offset = extended_offset = start; | ||
2349 | pe4->sectorbuffer = xzalloc(sector_size); | ||
2350 | pe4->part_table = pt_offset(pe4->sectorbuffer, 0); | ||
2351 | pe4->ext_pointer = pe4->part_table + 1; | ||
2352 | pe4->changed = 1; | ||
2353 | partitions = 5; | ||
2354 | } | ||
2355 | } | ||
2356 | |||
2357 | static void | ||
2358 | add_logical(void) | ||
2359 | { | ||
2360 | if (partitions > 5 || ptes[4].part_table->sys_ind) { | ||
2361 | struct pte *pe = &ptes[partitions]; | ||
2362 | |||
2363 | pe->sectorbuffer = xzalloc(sector_size); | ||
2364 | pe->part_table = pt_offset(pe->sectorbuffer, 0); | ||
2365 | pe->ext_pointer = pe->part_table + 1; | ||
2366 | pe->offset = 0; | ||
2367 | pe->changed = 1; | ||
2368 | partitions++; | ||
2369 | } | ||
2370 | add_partition(partitions - 1, LINUX_NATIVE); | ||
2371 | } | ||
2372 | |||
2373 | static void | ||
2374 | new_partition(void) | ||
2375 | { | ||
2376 | int i, free_primary = 0; | ||
2377 | |||
2378 | if (warn_geometry()) | ||
2379 | return; | ||
2380 | |||
2381 | if (LABEL_IS_SUN) { | ||
2382 | add_sun_partition(get_partition(0, partitions), LINUX_NATIVE); | ||
2383 | return; | ||
2384 | } | ||
2385 | if (LABEL_IS_SGI) { | ||
2386 | sgi_add_partition(get_partition(0, partitions), LINUX_NATIVE); | ||
2387 | return; | ||
2388 | } | ||
2389 | if (LABEL_IS_AIX) { | ||
2390 | printf(_("\tSorry - this fdisk cannot handle AIX disk labels." | ||
2391 | "\n\tIf you want to add DOS-type partitions, create" | ||
2392 | "\n\ta new empty DOS partition table first. (Use o.)" | ||
2393 | "\n\tWARNING: " | ||
2394 | "This will destroy the present disk contents.\n")); | ||
2395 | return; | ||
2396 | } | ||
2397 | |||
2398 | for (i = 0; i < 4; i++) | ||
2399 | free_primary += !ptes[i].part_table->sys_ind; | ||
2400 | |||
2401 | if (!free_primary && partitions >= MAXIMUM_PARTS) { | ||
2402 | printf(_("The maximum number of partitions has been created\n")); | ||
2403 | return; | ||
2404 | } | ||
2405 | |||
2406 | if (!free_primary) { | ||
2407 | if (extended_offset) | ||
2408 | add_logical(); | ||
2409 | else | ||
2410 | printf(_("You must delete some partition and add " | ||
2411 | "an extended partition first\n")); | ||
2412 | } else { | ||
2413 | char c, line[LINE_LENGTH]; | ||
2414 | snprintf(line, sizeof(line), "%s\n %s\n p primary " | ||
2415 | "partition (1-4)\n", | ||
2416 | "Command action", (extended_offset ? | ||
2417 | "l logical (5 or over)" : "e extended")); | ||
2418 | while (1) { | ||
2419 | c = read_nonempty(line); | ||
2420 | if (c == 'p' || c == 'P') { | ||
2421 | i = get_nonexisting_partition(0, 4); | ||
2422 | if (i >= 0) | ||
2423 | add_partition(i, LINUX_NATIVE); | ||
2424 | return; | ||
2425 | } | ||
2426 | else if (c == 'l' && extended_offset) { | ||
2427 | add_logical(); | ||
2428 | return; | ||
2429 | } | ||
2430 | else if (c == 'e' && !extended_offset) { | ||
2431 | i = get_nonexisting_partition(0, 4); | ||
2432 | if (i >= 0) | ||
2433 | add_partition(i, EXTENDED); | ||
2434 | return; | ||
2435 | } | ||
2436 | else | ||
2437 | printf(_("Invalid partition number " | ||
2438 | "for type '%c'\n"), c); | ||
2439 | } | ||
2440 | } | ||
2441 | } | ||
2442 | |||
2443 | static void | ||
2444 | write_table(void) | ||
2445 | { | ||
2446 | int i; | ||
2447 | |||
2448 | if (LABEL_IS_DOS) { | ||
2449 | for (i = 0; i < 3; i++) | ||
2450 | if (ptes[i].changed) | ||
2451 | ptes[3].changed = 1; | ||
2452 | for (i = 3; i < partitions; i++) { | ||
2453 | struct pte *pe = &ptes[i]; | ||
2454 | |||
2455 | if (pe->changed) { | ||
2456 | write_part_table_flag(pe->sectorbuffer); | ||
2457 | write_sector(pe->offset, pe->sectorbuffer); | ||
2458 | } | ||
2459 | } | ||
2460 | } | ||
2461 | else if (LABEL_IS_SGI) { | ||
2462 | /* no test on change? the printf below might be mistaken */ | ||
2463 | sgi_write_table(); | ||
2464 | } | ||
2465 | else if (LABEL_IS_SUN) { | ||
2466 | int needw = 0; | ||
2467 | |||
2468 | for (i = 0; i < 8; i++) | ||
2469 | if (ptes[i].changed) | ||
2470 | needw = 1; | ||
2471 | if (needw) | ||
2472 | sun_write_table(); | ||
2473 | } | ||
2474 | |||
2475 | printf(_("The partition table has been altered!\n\n")); | ||
2476 | reread_partition_table(1); | ||
2477 | } | ||
2478 | |||
2479 | static void | ||
2480 | reread_partition_table(int leave) | ||
2481 | { | ||
2482 | int error = 0; | ||
2483 | int i; | ||
2484 | |||
2485 | printf(_("Calling ioctl() to re-read partition table.\n")); | ||
2486 | sync(); | ||
2487 | sleep(2); | ||
2488 | if ((i = ioctl(fd, BLKRRPART)) != 0) { | ||
2489 | error = errno; | ||
2490 | } else { | ||
2491 | /* some kernel versions (1.2.x) seem to have trouble | ||
2492 | rereading the partition table, but if asked to do it | ||
2493 | twice, the second time works. - biro@yggdrasil.com */ | ||
2494 | sync(); | ||
2495 | sleep(2); | ||
2496 | if ((i = ioctl(fd, BLKRRPART)) != 0) | ||
2497 | error = errno; | ||
2498 | } | ||
2499 | |||
2500 | if (i) { | ||
2501 | printf(_("\nWARNING: Re-reading the partition table " | ||
2502 | "failed with error %d: %s.\n" | ||
2503 | "The kernel still uses the old table.\n" | ||
2504 | "The new table will be used " | ||
2505 | "at the next reboot.\n"), | ||
2506 | error, strerror(error)); | ||
2507 | } | ||
2508 | |||
2509 | if (dos_changed) | ||
2510 | printf( | ||
2511 | _("\nWARNING: If you have created or modified any DOS 6.x\n" | ||
2512 | "partitions, please see the fdisk manual page for additional\n" | ||
2513 | "information.\n")); | ||
2514 | |||
2515 | if (leave) { | ||
2516 | close(fd); | ||
2517 | |||
2518 | printf(_("Syncing disks.\n")); | ||
2519 | sync(); | ||
2520 | sleep(4); /* for sync() */ | ||
2521 | exit(!!i); | ||
2522 | } | ||
2523 | } | ||
2524 | #endif /* CONFIG_FEATURE_FDISK_WRITABLE */ | ||
2525 | |||
2526 | #if ENABLE_FEATURE_FDISK_ADVANCED | ||
2527 | #define MAX_PER_LINE 16 | ||
2528 | static void | ||
2529 | print_buffer(char *pbuffer) | ||
2530 | { | ||
2531 | int i,l; | ||
2532 | |||
2533 | for (i = 0, l = 0; i < sector_size; i++, l++) { | ||
2534 | if (l == 0) | ||
2535 | printf("0x%03X:", i); | ||
2536 | printf(" %02X", (unsigned char) pbuffer[i]); | ||
2537 | if (l == MAX_PER_LINE - 1) { | ||
2538 | puts(""); | ||
2539 | l = -1; | ||
2540 | } | ||
2541 | } | ||
2542 | if (l > 0) | ||
2543 | puts(""); | ||
2544 | puts(""); | ||
2545 | } | ||
2546 | |||
2547 | |||
2548 | static void | ||
2549 | print_raw(void) | ||
2550 | { | ||
2551 | int i; | ||
2552 | |||
2553 | printf(_("Device: %s\n"), disk_device); | ||
2554 | if (LABEL_IS_SGI || LABEL_IS_SUN) | ||
2555 | print_buffer(MBRbuffer); | ||
2556 | else { | ||
2557 | for (i = 3; i < partitions; i++) | ||
2558 | print_buffer(ptes[i].sectorbuffer); | ||
2559 | } | ||
2560 | } | ||
2561 | |||
2562 | static void | ||
2563 | move_begin(int i) | ||
2564 | { | ||
2565 | struct pte *pe = &ptes[i]; | ||
2566 | struct partition *p = pe->part_table; | ||
2567 | off_t new, first; | ||
2568 | |||
2569 | if (warn_geometry()) | ||
2570 | return; | ||
2571 | if (!p->sys_ind || !get_nr_sects(p) || IS_EXTENDED(p->sys_ind)) { | ||
2572 | printf(_("Partition %d has no data area\n"), i + 1); | ||
2573 | return; | ||
2574 | } | ||
2575 | first = get_partition_start(pe); | ||
2576 | new = read_int(first, first, first + get_nr_sects(p) - 1, first, | ||
2577 | _("New beginning of data")) - pe->offset; | ||
2578 | |||
2579 | if (new != get_nr_sects(p)) { | ||
2580 | first = get_nr_sects(p) + get_start_sect(p) - new; | ||
2581 | set_nr_sects(p, first); | ||
2582 | set_start_sect(p, new); | ||
2583 | pe->changed = 1; | ||
2584 | } | ||
2585 | } | ||
2586 | |||
2587 | static void | ||
2588 | xselect(void) | ||
2589 | { | ||
2590 | char c; | ||
2591 | |||
2592 | while (1) { | ||
2593 | putchar('\n'); | ||
2594 | c = tolower(read_nonempty(_("Expert command (m for help): "))); | ||
2595 | switch (c) { | ||
2596 | case 'a': | ||
2597 | if (LABEL_IS_SUN) | ||
2598 | sun_set_alt_cyl(); | ||
2599 | break; | ||
2600 | case 'b': | ||
2601 | if (LABEL_IS_DOS) | ||
2602 | move_begin(get_partition(0, partitions)); | ||
2603 | break; | ||
2604 | case 'c': | ||
2605 | user_cylinders = cylinders = | ||
2606 | read_int(1, cylinders, 1048576, 0, | ||
2607 | _("Number of cylinders")); | ||
2608 | if (LABEL_IS_SUN) | ||
2609 | sun_set_ncyl(cylinders); | ||
2610 | if (LABEL_IS_DOS) | ||
2611 | warn_cylinders(); | ||
2612 | break; | ||
2613 | case 'd': | ||
2614 | print_raw(); | ||
2615 | break; | ||
2616 | case 'e': | ||
2617 | if (LABEL_IS_SGI) | ||
2618 | sgi_set_xcyl(); | ||
2619 | else if (LABEL_IS_SUN) | ||
2620 | sun_set_xcyl(); | ||
2621 | else if (LABEL_IS_DOS) | ||
2622 | x_list_table(1); | ||
2623 | break; | ||
2624 | case 'f': | ||
2625 | if (LABEL_IS_DOS) | ||
2626 | fix_partition_table_order(); | ||
2627 | break; | ||
2628 | case 'g': | ||
2629 | #if ENABLE_FEATURE_SGI_LABEL | ||
2630 | create_sgilabel(); | ||
2631 | #endif | ||
2632 | break; | ||
2633 | case 'h': | ||
2634 | user_heads = heads = read_int(1, heads, 256, 0, | ||
2635 | _("Number of heads")); | ||
2636 | update_units(); | ||
2637 | break; | ||
2638 | case 'i': | ||
2639 | if (LABEL_IS_SUN) | ||
2640 | sun_set_ilfact(); | ||
2641 | break; | ||
2642 | case 'o': | ||
2643 | if (LABEL_IS_SUN) | ||
2644 | sun_set_rspeed(); | ||
2645 | break; | ||
2646 | case 'p': | ||
2647 | if (LABEL_IS_SUN) | ||
2648 | list_table(1); | ||
2649 | else | ||
2650 | x_list_table(0); | ||
2651 | break; | ||
2652 | case 'q': | ||
2653 | close(fd); | ||
2654 | puts(""); | ||
2655 | exit(0); | ||
2656 | case 'r': | ||
2657 | return; | ||
2658 | case 's': | ||
2659 | user_sectors = sectors = read_int(1, sectors, 63, 0, | ||
2660 | _("Number of sectors")); | ||
2661 | if (dos_compatible_flag) { | ||
2662 | sector_offset = sectors; | ||
2663 | printf(_("Warning: setting sector offset for DOS " | ||
2664 | "compatiblity\n")); | ||
2665 | } | ||
2666 | update_units(); | ||
2667 | break; | ||
2668 | case 'v': | ||
2669 | verify(); | ||
2670 | break; | ||
2671 | case 'w': | ||
2672 | write_table(); /* does not return */ | ||
2673 | break; | ||
2674 | case 'y': | ||
2675 | if (LABEL_IS_SUN) | ||
2676 | sun_set_pcylcount(); | ||
2677 | break; | ||
2678 | default: | ||
2679 | xmenu(); | ||
2680 | } | ||
2681 | } | ||
2682 | } | ||
2683 | #endif /* ADVANCED mode */ | ||
2684 | |||
2685 | static int | ||
2686 | is_ide_cdrom_or_tape(const char *device) | ||
2687 | { | ||
2688 | FILE *procf; | ||
2689 | char buf[100]; | ||
2690 | struct stat statbuf; | ||
2691 | int is_ide = 0; | ||
2692 | |||
2693 | /* No device was given explicitly, and we are trying some | ||
2694 | likely things. But opening /dev/hdc may produce errors like | ||
2695 | "hdc: tray open or drive not ready" | ||
2696 | if it happens to be a CD-ROM drive. It even happens that | ||
2697 | the process hangs on the attempt to read a music CD. | ||
2698 | So try to be careful. This only works since 2.1.73. */ | ||
2699 | |||
2700 | if (strncmp("/dev/hd", device, 7)) | ||
2701 | return 0; | ||
2702 | |||
2703 | snprintf(buf, sizeof(buf), "/proc/ide/%s/media", device+5); | ||
2704 | procf = fopen(buf, "r"); | ||
2705 | if (procf != NULL && fgets(buf, sizeof(buf), procf)) | ||
2706 | is_ide = (!strncmp(buf, "cdrom", 5) || | ||
2707 | !strncmp(buf, "tape", 4)); | ||
2708 | else | ||
2709 | /* Now when this proc file does not exist, skip the | ||
2710 | device when it is read-only. */ | ||
2711 | if (stat(device, &statbuf) == 0) | ||
2712 | is_ide = ((statbuf.st_mode & 0222) == 0); | ||
2713 | |||
2714 | if (procf) | ||
2715 | fclose(procf); | ||
2716 | return is_ide; | ||
2717 | } | ||
2718 | |||
2719 | |||
2720 | static void | ||
2721 | try(const char *device, int user_specified) | ||
2722 | { | ||
2723 | int gb; | ||
2724 | |||
2725 | disk_device = device; | ||
2726 | if (setjmp(listingbuf)) | ||
2727 | return; | ||
2728 | if (!user_specified) | ||
2729 | if (is_ide_cdrom_or_tape(device)) | ||
2730 | return; | ||
2731 | if ((fd = open(disk_device, type_open)) >= 0) { | ||
2732 | gb = get_boot(try_only); | ||
2733 | if (gb > 0) { /* I/O error */ | ||
2734 | close(fd); | ||
2735 | } else if (gb < 0) { /* no DOS signature */ | ||
2736 | list_disk_geometry(); | ||
2737 | if (LABEL_IS_AIX) { | ||
2738 | return; | ||
2739 | } | ||
2740 | #if ENABLE_FEATURE_OSF_LABEL | ||
2741 | if (btrydev(device) < 0) | ||
2742 | #endif | ||
2743 | printf(_("Disk %s doesn't contain a valid " | ||
2744 | "partition table\n"), device); | ||
2745 | close(fd); | ||
2746 | } else { | ||
2747 | close(fd); | ||
2748 | list_table(0); | ||
2749 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
2750 | if (!LABEL_IS_SUN && partitions > 4){ | ||
2751 | delete_partition(ext_index); | ||
2752 | } | ||
2753 | #endif | ||
2754 | } | ||
2755 | } else { | ||
2756 | /* Ignore other errors, since we try IDE | ||
2757 | and SCSI hard disks which may not be | ||
2758 | installed on the system. */ | ||
2759 | if (errno == EACCES) { | ||
2760 | printf(_("Cannot open %s\n"), device); | ||
2761 | return; | ||
2762 | } | ||
2763 | } | ||
2764 | } | ||
2765 | |||
2766 | /* for fdisk -l: try all things in /proc/partitions | ||
2767 | that look like a partition name (do not end in a digit) */ | ||
2768 | static void | ||
2769 | tryprocpt(void) | ||
2770 | { | ||
2771 | FILE *procpt; | ||
2772 | char line[100], ptname[100], devname[120], *s; | ||
2773 | int ma, mi, sz; | ||
2774 | |||
2775 | procpt = fopen_or_warn("/proc/partitions", "r"); | ||
2776 | |||
2777 | while (fgets(line, sizeof(line), procpt)) { | ||
2778 | if (sscanf(line, " %d %d %d %[^\n ]", | ||
2779 | &ma, &mi, &sz, ptname) != 4) | ||
2780 | continue; | ||
2781 | for (s = ptname; *s; s++); | ||
2782 | if (isdigit(s[-1])) | ||
2783 | continue; | ||
2784 | sprintf(devname, "/dev/%s", ptname); | ||
2785 | try(devname, 0); | ||
2786 | } | ||
2787 | #if ENABLE_FEATURE_CLEAN_UP | ||
2788 | fclose(procpt); | ||
2789 | #endif | ||
2790 | } | ||
2791 | |||
2792 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
2793 | static void | ||
2794 | unknown_command(int c) | ||
2795 | { | ||
2796 | printf(_("%c: unknown command\n"), c); | ||
2797 | } | ||
2798 | #endif | ||
2799 | |||
2800 | int fdisk_main(int argc, char **argv) | ||
2801 | { | ||
2802 | char *str_b, *str_C, *str_H, *str_S; | ||
2803 | unsigned opt; | ||
2804 | int c; | ||
2805 | /* | ||
2806 | * fdisk -v | ||
2807 | * fdisk -l [-b sectorsize] [-u] device ... | ||
2808 | * fdisk -s [partition] ... | ||
2809 | * fdisk [-b sectorsize] [-u] device | ||
2810 | * | ||
2811 | * Options -C, -H, -S set the geometry. | ||
2812 | */ | ||
2813 | enum { | ||
2814 | OPT_b = 1 << 0, | ||
2815 | OPT_C = 1 << 1, | ||
2816 | OPT_H = 1 << 2, | ||
2817 | OPT_l = 1 << 3, | ||
2818 | OPT_S = 1 << 4, | ||
2819 | OPT_u = 1 << 5, | ||
2820 | OPT_s = (1 << 6) * ENABLE_FEATURE_FDISK_BLKSIZE, | ||
2821 | }; | ||
2822 | opt = getopt32(argc, argv, "b:C:H:lS:u" USE_FEATURE_FDISK_BLKSIZE("s"), | ||
2823 | &str_b, &str_C, &str_H, &str_S); | ||
2824 | argc -= optind; | ||
2825 | argv += optind; | ||
2826 | if (opt & OPT_b) { // -b | ||
2827 | /* Ugly: this sector size is really per device, | ||
2828 | so cannot be combined with multiple disks, | ||
2829 | and the same goes for the C/H/S options. | ||
2830 | */ | ||
2831 | sector_size = xatoi_u(str_b); | ||
2832 | if (sector_size != 512 && sector_size != 1024 && | ||
2833 | sector_size != 2048) | ||
2834 | bb_show_usage(); | ||
2835 | sector_offset = 2; | ||
2836 | user_set_sector_size = 1; | ||
2837 | } | ||
2838 | if (opt & OPT_C) user_cylinders = xatoi_u(str_C); // -C | ||
2839 | if (opt & OPT_H) { // -H | ||
2840 | user_heads = xatoi_u(str_H); | ||
2841 | if (user_heads <= 0 || user_heads >= 256) | ||
2842 | user_heads = 0; | ||
2843 | } | ||
2844 | //if (opt & OPT_l) // -l | ||
2845 | if (opt & OPT_S) { // -S | ||
2846 | user_sectors = xatoi_u(str_S); | ||
2847 | if (user_sectors <= 0 || user_sectors >= 64) | ||
2848 | user_sectors = 0; | ||
2849 | } | ||
2850 | if (opt & OPT_u) display_in_cyl_units = 0; // -u | ||
2851 | //if (opt & OPT_s) // -s | ||
2852 | |||
2853 | if (user_set_sector_size && argc != 1) | ||
2854 | printf(_("Warning: the -b (set sector size) option should" | ||
2855 | " be used with one specified device\n")); | ||
2856 | |||
2857 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
2858 | if (opt & OPT_l) { | ||
2859 | nowarn = 1; | ||
2860 | #endif | ||
2861 | type_open = O_RDONLY; | ||
2862 | if (argc > 0) { | ||
2863 | int k; | ||
2864 | #if __GNUC__ | ||
2865 | /* avoid gcc warning: | ||
2866 | variable `k' might be clobbered by `longjmp' */ | ||
2867 | (void)&k; | ||
2868 | #endif | ||
2869 | listing = 1; | ||
2870 | for (k = 0; k < argc; k++) | ||
2871 | try(argv[k], 1); | ||
2872 | } else { | ||
2873 | /* we no longer have default device names */ | ||
2874 | /* but, we can use /proc/partitions instead */ | ||
2875 | tryprocpt(); | ||
2876 | } | ||
2877 | return 0; | ||
2878 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
2879 | } | ||
2880 | #endif | ||
2881 | |||
2882 | #if ENABLE_FEATURE_FDISK_BLKSIZE | ||
2883 | if (opt & OPT_s) { | ||
2884 | long size; | ||
2885 | int j; | ||
2886 | |||
2887 | nowarn = 1; | ||
2888 | type_open = O_RDONLY; | ||
2889 | |||
2890 | if (argc <= 0) | ||
2891 | bb_show_usage(); | ||
2892 | |||
2893 | for (j = 0; j < argc; j++) { | ||
2894 | disk_device = argv[j]; | ||
2895 | fd = open(disk_device, type_open); | ||
2896 | if (fd < 0) | ||
2897 | fdisk_fatal(unable_to_open); | ||
2898 | if (ioctl(fd, BLKGETSIZE, &size)) | ||
2899 | fdisk_fatal(ioctl_error); | ||
2900 | close(fd); | ||
2901 | if (argc == 1) | ||
2902 | printf("%ld\n", size/2); | ||
2903 | else | ||
2904 | printf("%s: %ld\n", argv[j], size/2); | ||
2905 | } | ||
2906 | return 0; | ||
2907 | } | ||
2908 | #endif | ||
2909 | |||
2910 | #if ENABLE_FEATURE_FDISK_WRITABLE | ||
2911 | if (argc != 1) | ||
2912 | bb_show_usage(); | ||
2913 | |||
2914 | disk_device = argv[0]; | ||
2915 | get_boot(fdisk); | ||
2916 | |||
2917 | if (LABEL_IS_OSF) { | ||
2918 | /* OSF label, and no DOS label */ | ||
2919 | printf(_("Detected an OSF/1 disklabel on %s, entering " | ||
2920 | "disklabel mode.\n"), disk_device); | ||
2921 | bsd_select(); | ||
2922 | /*Why do we do this? It seems to be counter-intuitive*/ | ||
2923 | current_label_type = label_dos; | ||
2924 | /* If we return we may want to make an empty DOS label? */ | ||
2925 | } | ||
2926 | |||
2927 | while (1) { | ||
2928 | putchar('\n'); | ||
2929 | c = tolower(read_nonempty(_("Command (m for help): "))); | ||
2930 | switch (c) { | ||
2931 | case 'a': | ||
2932 | if (LABEL_IS_DOS) | ||
2933 | toggle_active(get_partition(1, partitions)); | ||
2934 | else if (LABEL_IS_SUN) | ||
2935 | toggle_sunflags(get_partition(1, partitions), | ||
2936 | 0x01); | ||
2937 | else if (LABEL_IS_SGI) | ||
2938 | sgi_set_bootpartition( | ||
2939 | get_partition(1, partitions)); | ||
2940 | else | ||
2941 | unknown_command(c); | ||
2942 | break; | ||
2943 | case 'b': | ||
2944 | if (LABEL_IS_SGI) { | ||
2945 | printf(_("\nThe current boot file is: %s\n"), | ||
2946 | sgi_get_bootfile()); | ||
2947 | if (read_maybe_empty(_("Please enter the name of the " | ||
2948 | "new boot file: ")) == '\n') | ||
2949 | printf(_("Boot file unchanged\n")); | ||
2950 | else | ||
2951 | sgi_set_bootfile(line_ptr); | ||
2952 | } | ||
2953 | #if ENABLE_FEATURE_OSF_LABEL | ||
2954 | else | ||
2955 | bsd_select(); | ||
2956 | #endif | ||
2957 | break; | ||
2958 | case 'c': | ||
2959 | if (LABEL_IS_DOS) | ||
2960 | toggle_dos_compatibility_flag(); | ||
2961 | else if (LABEL_IS_SUN) | ||
2962 | toggle_sunflags(get_partition(1, partitions), | ||
2963 | 0x10); | ||
2964 | else if (LABEL_IS_SGI) | ||
2965 | sgi_set_swappartition( | ||
2966 | get_partition(1, partitions)); | ||
2967 | else | ||
2968 | unknown_command(c); | ||
2969 | break; | ||
2970 | case 'd': | ||
2971 | { | ||
2972 | int j; | ||
2973 | /* If sgi_label then don't use get_existing_partition, | ||
2974 | let the user select a partition, since | ||
2975 | get_existing_partition() only works for Linux-like | ||
2976 | partition tables */ | ||
2977 | if (!LABEL_IS_SGI) { | ||
2978 | j = get_existing_partition(1, partitions); | ||
2979 | } else { | ||
2980 | j = get_partition(1, partitions); | ||
2981 | } | ||
2982 | if (j >= 0) | ||
2983 | delete_partition(j); | ||
2984 | } | ||
2985 | break; | ||
2986 | case 'i': | ||
2987 | if (LABEL_IS_SGI) | ||
2988 | create_sgiinfo(); | ||
2989 | else | ||
2990 | unknown_command(c); | ||
2991 | case 'l': | ||
2992 | list_types(get_sys_types()); | ||
2993 | break; | ||
2994 | case 'm': | ||
2995 | menu(); | ||
2996 | break; | ||
2997 | case 'n': | ||
2998 | new_partition(); | ||
2999 | break; | ||
3000 | case 'o': | ||
3001 | create_doslabel(); | ||
3002 | break; | ||
3003 | case 'p': | ||
3004 | list_table(0); | ||
3005 | break; | ||
3006 | case 'q': | ||
3007 | close(fd); | ||
3008 | puts(""); | ||
3009 | return 0; | ||
3010 | case 's': | ||
3011 | #if ENABLE_FEATURE_SUN_LABEL | ||
3012 | create_sunlabel(); | ||
3013 | #endif | ||
3014 | break; | ||
3015 | case 't': | ||
3016 | change_sysid(); | ||
3017 | break; | ||
3018 | case 'u': | ||
3019 | change_units(); | ||
3020 | break; | ||
3021 | case 'v': | ||
3022 | verify(); | ||
3023 | break; | ||
3024 | case 'w': | ||
3025 | write_table(); /* does not return */ | ||
3026 | break; | ||
3027 | #if ENABLE_FEATURE_FDISK_ADVANCED | ||
3028 | case 'x': | ||
3029 | if (LABEL_IS_SGI) { | ||
3030 | printf(_("\n\tSorry, no experts menu for SGI " | ||
3031 | "partition tables available.\n\n")); | ||
3032 | } else | ||
3033 | xselect(); | ||
3034 | break; | ||
3035 | #endif | ||
3036 | default: | ||
3037 | unknown_command(c); | ||
3038 | menu(); | ||
3039 | } | ||
3040 | } | ||
3041 | return 0; | ||
3042 | #endif /* CONFIG_FEATURE_FDISK_WRITABLE */ | ||
3043 | } | ||
diff --git a/util-linux/fdisk_aix.c b/util-linux/fdisk_aix.c new file mode 100644 index 000000000..a3d5fe15f --- /dev/null +++ b/util-linux/fdisk_aix.c | |||
@@ -0,0 +1,76 @@ | |||
1 | #ifdef CONFIG_FEATURE_AIX_LABEL | ||
2 | /* | ||
3 | * Copyright (C) Andreas Neuper, Sep 1998. | ||
4 | * This file may be redistributed under | ||
5 | * the terms of the GNU Public License. | ||
6 | */ | ||
7 | |||
8 | typedef struct { | ||
9 | unsigned int magic; /* expect AIX_LABEL_MAGIC */ | ||
10 | unsigned int fillbytes1[124]; | ||
11 | unsigned int physical_volume_id; | ||
12 | unsigned int fillbytes2[124]; | ||
13 | } aix_partition; | ||
14 | |||
15 | #define AIX_LABEL_MAGIC 0xc9c2d4c1 | ||
16 | #define AIX_LABEL_MAGIC_SWAPPED 0xc1d4c2c9 | ||
17 | #define AIX_INFO_MAGIC 0x00072959 | ||
18 | #define AIX_INFO_MAGIC_SWAPPED 0x59290700 | ||
19 | |||
20 | #define aixlabel ((aix_partition *)MBRbuffer) | ||
21 | |||
22 | |||
23 | /* | ||
24 | Changes: | ||
25 | * 1999-03-20 Arnaldo Carvalho de Melo <acme@conectiva.com.br> | ||
26 | * Internationalization | ||
27 | * | ||
28 | * 2003-03-20 Phillip Kesling <pkesling@sgi.com> | ||
29 | * Some fixes | ||
30 | */ | ||
31 | |||
32 | static int aix_other_endian; | ||
33 | static short aix_volumes = 1; | ||
34 | |||
35 | /* | ||
36 | * only dealing with free blocks here | ||
37 | */ | ||
38 | |||
39 | static void | ||
40 | aix_info(void) | ||
41 | { | ||
42 | puts( | ||
43 | _("\n\tThere is a valid AIX label on this disk.\n" | ||
44 | "\tUnfortunately Linux cannot handle these\n" | ||
45 | "\tdisks at the moment. Nevertheless some\n" | ||
46 | "\tadvice:\n" | ||
47 | "\t1. fdisk will destroy its contents on write.\n" | ||
48 | "\t2. Be sure that this disk is NOT a still vital\n" | ||
49 | "\t part of a volume group. (Otherwise you may\n" | ||
50 | "\t erase the other disks as well, if unmirrored.)\n" | ||
51 | "\t3. Before deleting this physical volume be sure\n" | ||
52 | "\t to remove the disk logically from your AIX\n" | ||
53 | "\t machine. (Otherwise you become an AIXpert).") | ||
54 | ); | ||
55 | } | ||
56 | |||
57 | static int | ||
58 | check_aix_label(void) | ||
59 | { | ||
60 | if (aixlabel->magic != AIX_LABEL_MAGIC && | ||
61 | aixlabel->magic != AIX_LABEL_MAGIC_SWAPPED) { | ||
62 | current_label_type = 0; | ||
63 | aix_other_endian = 0; | ||
64 | return 0; | ||
65 | } | ||
66 | aix_other_endian = (aixlabel->magic == AIX_LABEL_MAGIC_SWAPPED); | ||
67 | update_units(); | ||
68 | current_label_type = label_aix; | ||
69 | partitions = 1016; | ||
70 | aix_volumes = 15; | ||
71 | aix_info(); | ||
72 | /*aix_nolabel();*/ /* %% */ | ||
73 | /*aix_label = 1;*/ /* %% */ | ||
74 | return 1; | ||
75 | } | ||
76 | #endif /* AIX_LABEL */ | ||
diff --git a/util-linux/fdisk_osf.c b/util-linux/fdisk_osf.c new file mode 100644 index 000000000..bff2371e4 --- /dev/null +++ b/util-linux/fdisk_osf.c | |||
@@ -0,0 +1,1046 @@ | |||
1 | #ifdef CONFIG_FEATURE_OSF_LABEL | ||
2 | /* | ||
3 | * Copyright (c) 1987, 1988 Regents of the University of California. | ||
4 | * All rights reserved. | ||
5 | * | ||
6 | * Redistribution and use in source and binary forms, with or without | ||
7 | * modification, are permitted provided that the following conditions | ||
8 | * are met: | ||
9 | * 1. Redistributions of source code must retain the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer. | ||
11 | * 2. Redistributions in binary form must reproduce the above copyright | ||
12 | * notice, this list of conditions and the following disclaimer in the | ||
13 | * documentation and/or other materials provided with the distribution. | ||
14 | * 3. All advertising materials mentioning features or use of this software | ||
15 | * must display the following acknowledgment: | ||
16 | * This product includes software developed by the University of | ||
17 | * California, Berkeley and its contributors. | ||
18 | * 4. Neither the name of the University nor the names of its contributors | ||
19 | * may be used to endorse or promote products derived from this software | ||
20 | * without specific prior written permission. | ||
21 | * | ||
22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||
23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
32 | * SUCH DAMAGE. | ||
33 | */ | ||
34 | |||
35 | |||
36 | #ifndef BSD_DISKMAGIC | ||
37 | #define BSD_DISKMAGIC ((uint32_t) 0x82564557) | ||
38 | #endif | ||
39 | |||
40 | #ifndef BSD_MAXPARTITIONS | ||
41 | #define BSD_MAXPARTITIONS 16 | ||
42 | #endif | ||
43 | |||
44 | #define BSD_LINUX_BOOTDIR "/usr/ucb/mdec" | ||
45 | |||
46 | #if defined (i386) || defined (__sparc__) || defined (__arm__) || defined (__m68k__) || defined (__mips__) || defined (__s390__) || defined (__sh__) || defined(__x86_64__) | ||
47 | #define BSD_LABELSECTOR 1 | ||
48 | #define BSD_LABELOFFSET 0 | ||
49 | #elif defined (__alpha__) || defined (__powerpc__) || defined (__ia64__) || defined (__hppa__) | ||
50 | #define BSD_LABELSECTOR 0 | ||
51 | #define BSD_LABELOFFSET 64 | ||
52 | #elif defined (__s390__) || defined (__s390x__) | ||
53 | #define BSD_LABELSECTOR 1 | ||
54 | #define BSD_LABELOFFSET 0 | ||
55 | #else | ||
56 | #error unknown architecture | ||
57 | #endif | ||
58 | |||
59 | #define BSD_BBSIZE 8192 /* size of boot area, with label */ | ||
60 | #define BSD_SBSIZE 8192 /* max size of fs superblock */ | ||
61 | |||
62 | struct xbsd_disklabel { | ||
63 | uint32_t d_magic; /* the magic number */ | ||
64 | int16_t d_type; /* drive type */ | ||
65 | int16_t d_subtype; /* controller/d_type specific */ | ||
66 | char d_typename[16]; /* type name, e.g. "eagle" */ | ||
67 | char d_packname[16]; /* pack identifier */ | ||
68 | /* disk geometry: */ | ||
69 | uint32_t d_secsize; /* # of bytes per sector */ | ||
70 | uint32_t d_nsectors; /* # of data sectors per track */ | ||
71 | uint32_t d_ntracks; /* # of tracks per cylinder */ | ||
72 | uint32_t d_ncylinders; /* # of data cylinders per unit */ | ||
73 | uint32_t d_secpercyl; /* # of data sectors per cylinder */ | ||
74 | uint32_t d_secperunit; /* # of data sectors per unit */ | ||
75 | /* | ||
76 | * Spares (bad sector replacements) below | ||
77 | * are not counted in d_nsectors or d_secpercyl. | ||
78 | * Spare sectors are assumed to be physical sectors | ||
79 | * which occupy space at the end of each track and/or cylinder. | ||
80 | */ | ||
81 | uint16_t d_sparespertrack; /* # of spare sectors per track */ | ||
82 | uint16_t d_sparespercyl; /* # of spare sectors per cylinder */ | ||
83 | /* | ||
84 | * Alternate cylinders include maintenance, replacement, | ||
85 | * configuration description areas, etc. | ||
86 | */ | ||
87 | uint32_t d_acylinders; /* # of alt. cylinders per unit */ | ||
88 | |||
89 | /* hardware characteristics: */ | ||
90 | /* | ||
91 | * d_interleave, d_trackskew and d_cylskew describe perturbations | ||
92 | * in the media format used to compensate for a slow controller. | ||
93 | * Interleave is physical sector interleave, set up by the formatter | ||
94 | * or controller when formatting. When interleaving is in use, | ||
95 | * logically adjacent sectors are not physically contiguous, | ||
96 | * but instead are separated by some number of sectors. | ||
97 | * It is specified as the ratio of physical sectors traversed | ||
98 | * per logical sector. Thus an interleave of 1:1 implies contiguous | ||
99 | * layout, while 2:1 implies that logical sector 0 is separated | ||
100 | * by one sector from logical sector 1. | ||
101 | * d_trackskew is the offset of sector 0 on track N | ||
102 | * relative to sector 0 on track N-1 on the same cylinder. | ||
103 | * Finally, d_cylskew is the offset of sector 0 on cylinder N | ||
104 | * relative to sector 0 on cylinder N-1. | ||
105 | */ | ||
106 | uint16_t d_rpm; /* rotational speed */ | ||
107 | uint16_t d_interleave; /* hardware sector interleave */ | ||
108 | uint16_t d_trackskew; /* sector 0 skew, per track */ | ||
109 | uint16_t d_cylskew; /* sector 0 skew, per cylinder */ | ||
110 | uint32_t d_headswitch; /* head switch time, usec */ | ||
111 | uint32_t d_trkseek; /* track-to-track seek, usec */ | ||
112 | uint32_t d_flags; /* generic flags */ | ||
113 | #define NDDATA 5 | ||
114 | uint32_t d_drivedata[NDDATA]; /* drive-type specific information */ | ||
115 | #define NSPARE 5 | ||
116 | uint32_t d_spare[NSPARE]; /* reserved for future use */ | ||
117 | uint32_t d_magic2; /* the magic number (again) */ | ||
118 | uint16_t d_checksum; /* xor of data incl. partitions */ | ||
119 | /* filesystem and partition information: */ | ||
120 | uint16_t d_npartitions; /* number of partitions in following */ | ||
121 | uint32_t d_bbsize; /* size of boot area at sn0, bytes */ | ||
122 | uint32_t d_sbsize; /* max size of fs superblock, bytes */ | ||
123 | struct xbsd_partition { /* the partition table */ | ||
124 | uint32_t p_size; /* number of sectors in partition */ | ||
125 | uint32_t p_offset; /* starting sector */ | ||
126 | uint32_t p_fsize; /* filesystem basic fragment size */ | ||
127 | uint8_t p_fstype; /* filesystem type, see below */ | ||
128 | uint8_t p_frag; /* filesystem fragments per block */ | ||
129 | uint16_t p_cpg; /* filesystem cylinders per group */ | ||
130 | } d_partitions[BSD_MAXPARTITIONS]; /* actually may be more */ | ||
131 | }; | ||
132 | |||
133 | /* d_type values: */ | ||
134 | #define BSD_DTYPE_SMD 1 /* SMD, XSMD; VAX hp/up */ | ||
135 | #define BSD_DTYPE_MSCP 2 /* MSCP */ | ||
136 | #define BSD_DTYPE_DEC 3 /* other DEC (rk, rl) */ | ||
137 | #define BSD_DTYPE_SCSI 4 /* SCSI */ | ||
138 | #define BSD_DTYPE_ESDI 5 /* ESDI interface */ | ||
139 | #define BSD_DTYPE_ST506 6 /* ST506 etc. */ | ||
140 | #define BSD_DTYPE_HPIB 7 /* CS/80 on HP-IB */ | ||
141 | #define BSD_DTYPE_HPFL 8 /* HP Fiber-link */ | ||
142 | #define BSD_DTYPE_FLOPPY 10 /* floppy */ | ||
143 | |||
144 | /* d_subtype values: */ | ||
145 | #define BSD_DSTYPE_INDOSPART 0x8 /* is inside dos partition */ | ||
146 | #define BSD_DSTYPE_DOSPART(s) ((s) & 3) /* dos partition number */ | ||
147 | #define BSD_DSTYPE_GEOMETRY 0x10 /* drive params in label */ | ||
148 | |||
149 | static const char * const xbsd_dktypenames[] = { | ||
150 | "unknown", | ||
151 | "SMD", | ||
152 | "MSCP", | ||
153 | "old DEC", | ||
154 | "SCSI", | ||
155 | "ESDI", | ||
156 | "ST506", | ||
157 | "HP-IB", | ||
158 | "HP-FL", | ||
159 | "type 9", | ||
160 | "floppy", | ||
161 | 0 | ||
162 | }; | ||
163 | #define BSD_DKMAXTYPES (sizeof(xbsd_dktypenames) / sizeof(xbsd_dktypenames[0]) - 1) | ||
164 | |||
165 | /* | ||
166 | * Filesystem type and version. | ||
167 | * Used to interpret other filesystem-specific | ||
168 | * per-partition information. | ||
169 | */ | ||
170 | #define BSD_FS_UNUSED 0 /* unused */ | ||
171 | #define BSD_FS_SWAP 1 /* swap */ | ||
172 | #define BSD_FS_V6 2 /* Sixth Edition */ | ||
173 | #define BSD_FS_V7 3 /* Seventh Edition */ | ||
174 | #define BSD_FS_SYSV 4 /* System V */ | ||
175 | #define BSD_FS_V71K 5 /* V7 with 1K blocks (4.1, 2.9) */ | ||
176 | #define BSD_FS_V8 6 /* Eighth Edition, 4K blocks */ | ||
177 | #define BSD_FS_BSDFFS 7 /* 4.2BSD fast file system */ | ||
178 | #define BSD_FS_BSDLFS 9 /* 4.4BSD log-structured file system */ | ||
179 | #define BSD_FS_OTHER 10 /* in use, but unknown/unsupported */ | ||
180 | #define BSD_FS_HPFS 11 /* OS/2 high-performance file system */ | ||
181 | #define BSD_FS_ISO9660 12 /* ISO-9660 filesystem (cdrom) */ | ||
182 | #define BSD_FS_ISOFS BSD_FS_ISO9660 | ||
183 | #define BSD_FS_BOOT 13 /* partition contains bootstrap */ | ||
184 | #define BSD_FS_ADOS 14 /* AmigaDOS fast file system */ | ||
185 | #define BSD_FS_HFS 15 /* Macintosh HFS */ | ||
186 | #define BSD_FS_ADVFS 16 /* Digital Unix AdvFS */ | ||
187 | |||
188 | /* this is annoying, but it's also the way it is :-( */ | ||
189 | #ifdef __alpha__ | ||
190 | #define BSD_FS_EXT2 8 /* ext2 file system */ | ||
191 | #else | ||
192 | #define BSD_FS_MSDOS 8 /* MS-DOS file system */ | ||
193 | #endif | ||
194 | |||
195 | static const struct systypes xbsd_fstypes[] = { | ||
196 | { "\x00" "unused" }, /* BSD_FS_UNUSED */ | ||
197 | { "\x01" "swap" }, /* BSD_FS_SWAP */ | ||
198 | { "\x02" "Version 6" }, /* BSD_FS_V6 */ | ||
199 | { "\x03" "Version 7" }, /* BSD_FS_V7 */ | ||
200 | { "\x04" "System V" }, /* BSD_FS_SYSV */ | ||
201 | { "\x05" "4.1BSD" }, /* BSD_FS_V71K */ | ||
202 | { "\x06" "Eighth Edition" }, /* BSD_FS_V8 */ | ||
203 | { "\x07" "4.2BSD" }, /* BSD_FS_BSDFFS */ | ||
204 | #ifdef __alpha__ | ||
205 | { "\x08" "ext2" }, /* BSD_FS_EXT2 */ | ||
206 | #else | ||
207 | { "\x08" "MS-DOS" }, /* BSD_FS_MSDOS */ | ||
208 | #endif | ||
209 | { "\x09" "4.4LFS" }, /* BSD_FS_BSDLFS */ | ||
210 | { "\x0a" "unknown" }, /* BSD_FS_OTHER */ | ||
211 | { "\x0b" "HPFS" }, /* BSD_FS_HPFS */ | ||
212 | { "\x0c" "ISO-9660" }, /* BSD_FS_ISO9660 */ | ||
213 | { "\x0d" "boot" }, /* BSD_FS_BOOT */ | ||
214 | { "\x0e" "ADOS" }, /* BSD_FS_ADOS */ | ||
215 | { "\x0f" "HFS" }, /* BSD_FS_HFS */ | ||
216 | { "\x10" "AdvFS" }, /* BSD_FS_ADVFS */ | ||
217 | { NULL } | ||
218 | }; | ||
219 | #define BSD_FSMAXTYPES (SIZE(xbsd_fstypes)-1) | ||
220 | |||
221 | |||
222 | /* | ||
223 | * flags shared by various drives: | ||
224 | */ | ||
225 | #define BSD_D_REMOVABLE 0x01 /* removable media */ | ||
226 | #define BSD_D_ECC 0x02 /* supports ECC */ | ||
227 | #define BSD_D_BADSECT 0x04 /* supports bad sector forw. */ | ||
228 | #define BSD_D_RAMDISK 0x08 /* disk emulator */ | ||
229 | #define BSD_D_CHAIN 0x10 /* can do back-back transfers */ | ||
230 | #define BSD_D_DOSPART 0x20 /* within MSDOS partition */ | ||
231 | |||
232 | /* | ||
233 | Changes: | ||
234 | 19990319 - Arnaldo Carvalho de Melo <acme@conectiva.com.br> - i18n/nls | ||
235 | |||
236 | 20000101 - David Huggins-Daines <dhuggins@linuxcare.com> - Better | ||
237 | support for OSF/1 disklabels on Alpha. | ||
238 | Also fixed unaligned accesses in alpha_bootblock_checksum() | ||
239 | */ | ||
240 | |||
241 | static int possibly_osf_label; | ||
242 | |||
243 | #define FREEBSD_PARTITION 0xa5 | ||
244 | #define NETBSD_PARTITION 0xa9 | ||
245 | |||
246 | static void xbsd_delete_part(void); | ||
247 | static void xbsd_new_part(void); | ||
248 | static void xbsd_write_disklabel(void); | ||
249 | static int xbsd_create_disklabel(void); | ||
250 | static void xbsd_edit_disklabel(void); | ||
251 | static void xbsd_write_bootstrap(void); | ||
252 | static void xbsd_change_fstype(void); | ||
253 | static int xbsd_get_part_index(int max); | ||
254 | static int xbsd_check_new_partition(int *i); | ||
255 | static void xbsd_list_types(void); | ||
256 | static u_short xbsd_dkcksum(struct xbsd_disklabel *lp); | ||
257 | static int xbsd_initlabel(struct partition *p, struct xbsd_disklabel *d); | ||
258 | static int xbsd_readlabel(struct partition *p, struct xbsd_disklabel *d); | ||
259 | static int xbsd_writelabel(struct partition *p, struct xbsd_disklabel *d); | ||
260 | |||
261 | #if defined (__alpha__) | ||
262 | static void alpha_bootblock_checksum(char *boot); | ||
263 | #endif | ||
264 | |||
265 | #if !defined (__alpha__) | ||
266 | static int xbsd_translate_fstype(int linux_type); | ||
267 | static void xbsd_link_part(void); | ||
268 | static struct partition *xbsd_part; | ||
269 | static int xbsd_part_index; | ||
270 | #endif | ||
271 | |||
272 | #if defined (__alpha__) | ||
273 | /* We access this through a uint64_t * when checksumming */ | ||
274 | static char disklabelbuffer[BSD_BBSIZE] ATTRIBUTE_ALIGNED(8); | ||
275 | #else | ||
276 | static char disklabelbuffer[BSD_BBSIZE]; | ||
277 | #endif | ||
278 | |||
279 | static struct xbsd_disklabel xbsd_dlabel; | ||
280 | |||
281 | #define bsd_cround(n) \ | ||
282 | (display_in_cyl_units ? ((n)/xbsd_dlabel.d_secpercyl) + 1 : (n)) | ||
283 | |||
284 | /* | ||
285 | * Test whether the whole disk has BSD disk label magic. | ||
286 | * | ||
287 | * Note: often reformatting with DOS-type label leaves the BSD magic, | ||
288 | * so this does not mean that there is a BSD disk label. | ||
289 | */ | ||
290 | static int | ||
291 | check_osf_label(void) | ||
292 | { | ||
293 | if (xbsd_readlabel(NULL, &xbsd_dlabel) == 0) | ||
294 | return 0; | ||
295 | return 1; | ||
296 | } | ||
297 | |||
298 | static int | ||
299 | btrydev(const char * dev) | ||
300 | { | ||
301 | if (xbsd_readlabel (NULL, &xbsd_dlabel) == 0) | ||
302 | return -1; | ||
303 | printf(_("\nBSD label for device: %s\n"), dev); | ||
304 | xbsd_print_disklabel (0); | ||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | static void | ||
309 | bmenu(void) | ||
310 | { | ||
311 | puts (_("Command action")); | ||
312 | puts (_("\td\tdelete a BSD partition")); | ||
313 | puts (_("\te\tedit drive data")); | ||
314 | puts (_("\ti\tinstall bootstrap")); | ||
315 | puts (_("\tl\tlist known filesystem types")); | ||
316 | puts (_("\tm\tprint this menu")); | ||
317 | puts (_("\tn\tadd a new BSD partition")); | ||
318 | puts (_("\tp\tprint BSD partition table")); | ||
319 | puts (_("\tq\tquit without saving changes")); | ||
320 | puts (_("\tr\treturn to main menu")); | ||
321 | puts (_("\ts\tshow complete disklabel")); | ||
322 | puts (_("\tt\tchange a partition's filesystem id")); | ||
323 | puts (_("\tu\tchange units (cylinders/sectors)")); | ||
324 | puts (_("\tw\twrite disklabel to disk")); | ||
325 | #if !defined (__alpha__) | ||
326 | puts (_("\tx\tlink BSD partition to non-BSD partition")); | ||
327 | #endif | ||
328 | } | ||
329 | |||
330 | #if !defined (__alpha__) | ||
331 | static int | ||
332 | hidden(int type) | ||
333 | { | ||
334 | return type ^ 0x10; | ||
335 | } | ||
336 | |||
337 | static int | ||
338 | is_bsd_partition_type(int type) | ||
339 | { | ||
340 | return (type == FREEBSD_PARTITION || | ||
341 | type == hidden(FREEBSD_PARTITION) || | ||
342 | type == NETBSD_PARTITION || | ||
343 | type == hidden(NETBSD_PARTITION)); | ||
344 | } | ||
345 | #endif | ||
346 | |||
347 | static void | ||
348 | bsd_select(void) | ||
349 | { | ||
350 | #if !defined (__alpha__) | ||
351 | int t, ss; | ||
352 | struct partition *p; | ||
353 | |||
354 | for (t = 0; t < 4; t++) { | ||
355 | p = get_part_table(t); | ||
356 | if (p && is_bsd_partition_type(p->sys_ind)) { | ||
357 | xbsd_part = p; | ||
358 | xbsd_part_index = t; | ||
359 | ss = get_start_sect(xbsd_part); | ||
360 | if (ss == 0) { | ||
361 | fprintf(stderr, _("Partition %s has invalid starting sector 0.\n"), | ||
362 | partname(disk_device, t+1, 0)); | ||
363 | return; | ||
364 | } | ||
365 | printf(_("Reading disklabel of %s at sector %d.\n"), | ||
366 | partname(disk_device, t+1, 0), ss + BSD_LABELSECTOR); | ||
367 | if (xbsd_readlabel(xbsd_part, &xbsd_dlabel) == 0) | ||
368 | if (xbsd_create_disklabel() == 0) | ||
369 | return; | ||
370 | break; | ||
371 | } | ||
372 | } | ||
373 | |||
374 | if (t == 4) { | ||
375 | printf(_("There is no *BSD partition on %s.\n"), disk_device); | ||
376 | return; | ||
377 | } | ||
378 | |||
379 | #elif defined (__alpha__) | ||
380 | |||
381 | if (xbsd_readlabel(NULL, &xbsd_dlabel) == 0) | ||
382 | if (xbsd_create_disklabel() == 0) | ||
383 | exit (EXIT_SUCCESS); | ||
384 | |||
385 | #endif | ||
386 | |||
387 | while (1) { | ||
388 | putchar('\n'); | ||
389 | switch (tolower(read_nonempty(_("BSD disklabel command (m for help): ")))) { | ||
390 | case 'd': | ||
391 | xbsd_delete_part(); | ||
392 | break; | ||
393 | case 'e': | ||
394 | xbsd_edit_disklabel(); | ||
395 | break; | ||
396 | case 'i': | ||
397 | xbsd_write_bootstrap(); | ||
398 | break; | ||
399 | case 'l': | ||
400 | xbsd_list_types(); | ||
401 | break; | ||
402 | case 'n': | ||
403 | xbsd_new_part(); | ||
404 | break; | ||
405 | case 'p': | ||
406 | xbsd_print_disklabel(0); | ||
407 | break; | ||
408 | case 'q': | ||
409 | close(fd); | ||
410 | exit(EXIT_SUCCESS); | ||
411 | case 'r': | ||
412 | return; | ||
413 | case 's': | ||
414 | xbsd_print_disklabel(1); | ||
415 | break; | ||
416 | case 't': | ||
417 | xbsd_change_fstype(); | ||
418 | break; | ||
419 | case 'u': | ||
420 | change_units(); | ||
421 | break; | ||
422 | case 'w': | ||
423 | xbsd_write_disklabel(); | ||
424 | break; | ||
425 | #if !defined (__alpha__) | ||
426 | case 'x': | ||
427 | xbsd_link_part(); | ||
428 | break; | ||
429 | #endif | ||
430 | default: | ||
431 | bmenu(); | ||
432 | break; | ||
433 | } | ||
434 | } | ||
435 | } | ||
436 | |||
437 | static void | ||
438 | xbsd_delete_part(void) | ||
439 | { | ||
440 | int i; | ||
441 | |||
442 | i = xbsd_get_part_index(xbsd_dlabel.d_npartitions); | ||
443 | xbsd_dlabel.d_partitions[i].p_size = 0; | ||
444 | xbsd_dlabel.d_partitions[i].p_offset = 0; | ||
445 | xbsd_dlabel.d_partitions[i].p_fstype = BSD_FS_UNUSED; | ||
446 | if (xbsd_dlabel.d_npartitions == i + 1) | ||
447 | while (xbsd_dlabel.d_partitions[xbsd_dlabel.d_npartitions-1].p_size == 0) | ||
448 | xbsd_dlabel.d_npartitions--; | ||
449 | } | ||
450 | |||
451 | static void | ||
452 | xbsd_new_part(void) | ||
453 | { | ||
454 | off_t begin, end; | ||
455 | char mesg[256]; | ||
456 | int i; | ||
457 | |||
458 | if (!xbsd_check_new_partition(&i)) | ||
459 | return; | ||
460 | |||
461 | #if !defined (__alpha__) && !defined (__powerpc__) && !defined (__hppa__) | ||
462 | begin = get_start_sect(xbsd_part); | ||
463 | end = begin + get_nr_sects(xbsd_part) - 1; | ||
464 | #else | ||
465 | begin = 0; | ||
466 | end = xbsd_dlabel.d_secperunit - 1; | ||
467 | #endif | ||
468 | |||
469 | snprintf(mesg, sizeof(mesg), _("First %s"), str_units(SINGULAR)); | ||
470 | begin = read_int(bsd_cround(begin), bsd_cround(begin), bsd_cround(end), | ||
471 | 0, mesg); | ||
472 | |||
473 | if (display_in_cyl_units) | ||
474 | begin = (begin - 1) * xbsd_dlabel.d_secpercyl; | ||
475 | |||
476 | snprintf(mesg, sizeof(mesg), _("Last %s or +size or +sizeM or +sizeK"), | ||
477 | str_units(SINGULAR)); | ||
478 | end = read_int(bsd_cround (begin), bsd_cround (end), bsd_cround (end), | ||
479 | bsd_cround (begin), mesg); | ||
480 | |||
481 | if (display_in_cyl_units) | ||
482 | end = end * xbsd_dlabel.d_secpercyl - 1; | ||
483 | |||
484 | xbsd_dlabel.d_partitions[i].p_size = end - begin + 1; | ||
485 | xbsd_dlabel.d_partitions[i].p_offset = begin; | ||
486 | xbsd_dlabel.d_partitions[i].p_fstype = BSD_FS_UNUSED; | ||
487 | } | ||
488 | |||
489 | static void | ||
490 | xbsd_print_disklabel(int show_all) | ||
491 | { | ||
492 | struct xbsd_disklabel *lp = &xbsd_dlabel; | ||
493 | struct xbsd_partition *pp; | ||
494 | int i, j; | ||
495 | |||
496 | if (show_all) { | ||
497 | #if defined (__alpha__) | ||
498 | printf("# %s:\n", disk_device); | ||
499 | #else | ||
500 | printf("# %s:\n", partname(disk_device, xbsd_part_index+1, 0)); | ||
501 | #endif | ||
502 | if ((unsigned) lp->d_type < BSD_DKMAXTYPES) | ||
503 | printf(_("type: %s\n"), xbsd_dktypenames[lp->d_type]); | ||
504 | else | ||
505 | printf(_("type: %d\n"), lp->d_type); | ||
506 | printf(_("disk: %.*s\n"), (int) sizeof(lp->d_typename), lp->d_typename); | ||
507 | printf(_("label: %.*s\n"), (int) sizeof(lp->d_packname), lp->d_packname); | ||
508 | printf(_("flags:")); | ||
509 | if (lp->d_flags & BSD_D_REMOVABLE) | ||
510 | printf(_(" removable")); | ||
511 | if (lp->d_flags & BSD_D_ECC) | ||
512 | printf(_(" ecc")); | ||
513 | if (lp->d_flags & BSD_D_BADSECT) | ||
514 | printf(_(" badsect")); | ||
515 | puts(""); | ||
516 | /* On various machines the fields of *lp are short/int/long */ | ||
517 | /* In order to avoid problems, we cast them all to long. */ | ||
518 | printf(_("bytes/sector: %ld\n"), (long) lp->d_secsize); | ||
519 | printf(_("sectors/track: %ld\n"), (long) lp->d_nsectors); | ||
520 | printf(_("tracks/cylinder: %ld\n"), (long) lp->d_ntracks); | ||
521 | printf(_("sectors/cylinder: %ld\n"), (long) lp->d_secpercyl); | ||
522 | printf(_("cylinders: %ld\n"), (long) lp->d_ncylinders); | ||
523 | printf(_("rpm: %d\n"), lp->d_rpm); | ||
524 | printf(_("interleave: %d\n"), lp->d_interleave); | ||
525 | printf(_("trackskew: %d\n"), lp->d_trackskew); | ||
526 | printf(_("cylinderskew: %d\n"), lp->d_cylskew); | ||
527 | printf(_("headswitch: %ld\t\t# milliseconds\n"), | ||
528 | (long) lp->d_headswitch); | ||
529 | printf(_("track-to-track seek: %ld\t# milliseconds\n"), | ||
530 | (long) lp->d_trkseek); | ||
531 | printf(_("drivedata: ")); | ||
532 | for (i = NDDATA - 1; i >= 0; i--) | ||
533 | if (lp->d_drivedata[i]) | ||
534 | break; | ||
535 | if (i < 0) | ||
536 | i = 0; | ||
537 | for (j = 0; j <= i; j++) | ||
538 | printf("%ld ", (long) lp->d_drivedata[j]); | ||
539 | } | ||
540 | printf(_("\n%d partitions:\n"), lp->d_npartitions); | ||
541 | printf(_("# start end size fstype [fsize bsize cpg]\n")); | ||
542 | pp = lp->d_partitions; | ||
543 | for (i = 0; i < lp->d_npartitions; i++, pp++) { | ||
544 | if (pp->p_size) { | ||
545 | if (display_in_cyl_units && lp->d_secpercyl) { | ||
546 | printf(" %c: %8ld%c %8ld%c %8ld%c ", | ||
547 | 'a' + i, | ||
548 | (long) pp->p_offset / lp->d_secpercyl + 1, | ||
549 | (pp->p_offset % lp->d_secpercyl) ? '*' : ' ', | ||
550 | (long) (pp->p_offset + pp->p_size + lp->d_secpercyl - 1) / lp->d_secpercyl, | ||
551 | ((pp->p_offset + pp->p_size) % lp->d_secpercyl) ? '*' : ' ', | ||
552 | (long) pp->p_size / lp->d_secpercyl, | ||
553 | (pp->p_size % lp->d_secpercyl) ? '*' : ' ' | ||
554 | ); | ||
555 | } else { | ||
556 | printf(" %c: %8ld %8ld %8ld ", | ||
557 | 'a' + i, | ||
558 | (long) pp->p_offset, | ||
559 | (long) pp->p_offset + pp->p_size - 1, | ||
560 | (long) pp->p_size | ||
561 | ); | ||
562 | } | ||
563 | |||
564 | if ((unsigned) pp->p_fstype < BSD_FSMAXTYPES) | ||
565 | printf("%8.8s", xbsd_fstypes[pp->p_fstype].name); | ||
566 | else | ||
567 | printf("%8x", pp->p_fstype); | ||
568 | |||
569 | switch (pp->p_fstype) { | ||
570 | case BSD_FS_UNUSED: | ||
571 | printf(" %5ld %5ld %5.5s ", | ||
572 | (long) pp->p_fsize, (long) pp->p_fsize * pp->p_frag, ""); | ||
573 | break; | ||
574 | case BSD_FS_BSDFFS: | ||
575 | printf(" %5ld %5ld %5d ", | ||
576 | (long) pp->p_fsize, (long) pp->p_fsize * pp->p_frag, pp->p_cpg); | ||
577 | break; | ||
578 | default: | ||
579 | printf("%22.22s", ""); | ||
580 | break; | ||
581 | } | ||
582 | puts(""); | ||
583 | } | ||
584 | } | ||
585 | } | ||
586 | |||
587 | static void | ||
588 | xbsd_write_disklabel(void) | ||
589 | { | ||
590 | #if defined (__alpha__) | ||
591 | printf(_("Writing disklabel to %s.\n"), disk_device); | ||
592 | xbsd_writelabel(NULL, &xbsd_dlabel); | ||
593 | #else | ||
594 | printf(_("Writing disklabel to %s.\n"), | ||
595 | partname(disk_device, xbsd_part_index + 1, 0)); | ||
596 | xbsd_writelabel(xbsd_part, &xbsd_dlabel); | ||
597 | #endif | ||
598 | reread_partition_table(0); /* no exit yet */ | ||
599 | } | ||
600 | |||
601 | static int | ||
602 | xbsd_create_disklabel(void) | ||
603 | { | ||
604 | char c; | ||
605 | |||
606 | #if defined (__alpha__) | ||
607 | fprintf(stderr, _("%s contains no disklabel.\n"), disk_device); | ||
608 | #else | ||
609 | fprintf(stderr, _("%s contains no disklabel.\n"), | ||
610 | partname(disk_device, xbsd_part_index + 1, 0)); | ||
611 | #endif | ||
612 | |||
613 | while (1) { | ||
614 | c = read_nonempty(_("Do you want to create a disklabel? (y/n) ")); | ||
615 | if (c == 'y' || c == 'Y') { | ||
616 | if (xbsd_initlabel( | ||
617 | #if defined (__alpha__) || defined (__powerpc__) || defined (__hppa__) || \ | ||
618 | defined (__s390__) || defined (__s390x__) | ||
619 | NULL, &xbsd_dlabel | ||
620 | #else | ||
621 | xbsd_part, &xbsd_dlabel/* not used, xbsd_part_index*/ | ||
622 | #endif | ||
623 | ) == 1) { | ||
624 | xbsd_print_disklabel (1); | ||
625 | return 1; | ||
626 | } else | ||
627 | return 0; | ||
628 | } else if (c == 'n') | ||
629 | return 0; | ||
630 | } | ||
631 | } | ||
632 | |||
633 | static int | ||
634 | edit_int(int def, char *mesg) | ||
635 | { | ||
636 | do { | ||
637 | fputs(mesg, stdout); | ||
638 | printf(" (%d): ", def); | ||
639 | if (!read_line()) | ||
640 | return def; | ||
641 | } while (!isdigit(*line_ptr)); | ||
642 | return atoi(line_ptr); | ||
643 | } | ||
644 | |||
645 | static void | ||
646 | xbsd_edit_disklabel(void) | ||
647 | { | ||
648 | struct xbsd_disklabel *d; | ||
649 | |||
650 | d = &xbsd_dlabel; | ||
651 | |||
652 | #if defined (__alpha__) || defined (__ia64__) | ||
653 | d->d_secsize = (u_long) edit_int((u_long) d->d_secsize ,_("bytes/sector")); | ||
654 | d->d_nsectors = (u_long) edit_int((u_long) d->d_nsectors ,_("sectors/track")); | ||
655 | d->d_ntracks = (u_long) edit_int((u_long) d->d_ntracks ,_("tracks/cylinder")); | ||
656 | d->d_ncylinders = (u_long) edit_int((u_long) d->d_ncylinders ,_("cylinders")); | ||
657 | #endif | ||
658 | |||
659 | /* d->d_secpercyl can be != d->d_nsectors * d->d_ntracks */ | ||
660 | while (1) { | ||
661 | d->d_secpercyl = (u_long) edit_int((u_long) d->d_nsectors * d->d_ntracks, | ||
662 | _("sectors/cylinder")); | ||
663 | if (d->d_secpercyl <= d->d_nsectors * d->d_ntracks) | ||
664 | break; | ||
665 | |||
666 | printf(_("Must be <= sectors/track * tracks/cylinder (default).\n")); | ||
667 | } | ||
668 | d->d_rpm = (u_short) edit_int((u_short) d->d_rpm ,_("rpm")); | ||
669 | d->d_interleave = (u_short) edit_int((u_short) d->d_interleave,_("interleave")); | ||
670 | d->d_trackskew = (u_short) edit_int((u_short) d->d_trackskew ,_("trackskew")); | ||
671 | d->d_cylskew = (u_short) edit_int((u_short) d->d_cylskew ,_("cylinderskew")); | ||
672 | d->d_headswitch = (u_long) edit_int((u_long) d->d_headswitch ,_("headswitch")); | ||
673 | d->d_trkseek = (u_long) edit_int((u_long) d->d_trkseek ,_("track-to-track seek")); | ||
674 | |||
675 | d->d_secperunit = d->d_secpercyl * d->d_ncylinders; | ||
676 | } | ||
677 | |||
678 | static int | ||
679 | xbsd_get_bootstrap (char *path, void *ptr, int size) | ||
680 | { | ||
681 | int fdb; | ||
682 | |||
683 | if ((fdb = open (path, O_RDONLY)) < 0) { | ||
684 | perror(path); | ||
685 | return 0; | ||
686 | } | ||
687 | if (read(fdb, ptr, size) < 0) { | ||
688 | perror(path); | ||
689 | close(fdb); | ||
690 | return 0; | ||
691 | } | ||
692 | printf(" ... %s\n", path); | ||
693 | close(fdb); | ||
694 | return 1; | ||
695 | } | ||
696 | |||
697 | static void | ||
698 | sync_disks(void) | ||
699 | { | ||
700 | printf(_("\nSyncing disks.\n")); | ||
701 | sync(); | ||
702 | sleep(4); /* What? */ | ||
703 | } | ||
704 | |||
705 | static void | ||
706 | xbsd_write_bootstrap(void) | ||
707 | { | ||
708 | char *bootdir = BSD_LINUX_BOOTDIR; | ||
709 | char path[MAXPATHLEN]; | ||
710 | char *dkbasename; | ||
711 | struct xbsd_disklabel dl; | ||
712 | char *d, *p, *e; | ||
713 | int sector; | ||
714 | |||
715 | if (xbsd_dlabel.d_type == BSD_DTYPE_SCSI) | ||
716 | dkbasename = "sd"; | ||
717 | else | ||
718 | dkbasename = "wd"; | ||
719 | |||
720 | printf(_("Bootstrap: %sboot -> boot%s (%s): "), | ||
721 | dkbasename, dkbasename, dkbasename); | ||
722 | if (read_line()) { | ||
723 | line_ptr[strlen(line_ptr)-1] = '\0'; | ||
724 | dkbasename = line_ptr; | ||
725 | } | ||
726 | snprintf(path, sizeof(path), "%s/%sboot", bootdir, dkbasename); | ||
727 | if (!xbsd_get_bootstrap(path, disklabelbuffer, (int) xbsd_dlabel.d_secsize)) | ||
728 | return; | ||
729 | |||
730 | /* We need a backup of the disklabel (xbsd_dlabel might have changed). */ | ||
731 | d = &disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE]; | ||
732 | memmove(&dl, d, sizeof(struct xbsd_disklabel)); | ||
733 | |||
734 | /* The disklabel will be overwritten by 0's from bootxx anyway */ | ||
735 | memset(d, 0, sizeof(struct xbsd_disklabel)); | ||
736 | |||
737 | snprintf(path, sizeof(path), "%s/boot%s", bootdir, dkbasename); | ||
738 | if (!xbsd_get_bootstrap(path, &disklabelbuffer[xbsd_dlabel.d_secsize], | ||
739 | (int) xbsd_dlabel.d_bbsize - xbsd_dlabel.d_secsize)) | ||
740 | return; | ||
741 | |||
742 | e = d + sizeof(struct xbsd_disklabel); | ||
743 | for (p = d; p < e; p++) | ||
744 | if (*p) { | ||
745 | fprintf(stderr, _("Bootstrap overlaps with disk label!\n")); | ||
746 | exit(EXIT_FAILURE); | ||
747 | } | ||
748 | |||
749 | memmove(d, &dl, sizeof(struct xbsd_disklabel)); | ||
750 | |||
751 | #if defined (__powerpc__) || defined (__hppa__) | ||
752 | sector = 0; | ||
753 | #elif defined (__alpha__) | ||
754 | sector = 0; | ||
755 | alpha_bootblock_checksum(disklabelbuffer); | ||
756 | #else | ||
757 | sector = get_start_sect(xbsd_part); | ||
758 | #endif | ||
759 | |||
760 | if (lseek(fd, sector * SECTOR_SIZE, SEEK_SET) == -1) | ||
761 | fdisk_fatal(unable_to_seek); | ||
762 | if (BSD_BBSIZE != write(fd, disklabelbuffer, BSD_BBSIZE)) | ||
763 | fdisk_fatal(unable_to_write); | ||
764 | |||
765 | #if defined (__alpha__) | ||
766 | printf(_("Bootstrap installed on %s.\n"), disk_device); | ||
767 | #else | ||
768 | printf(_("Bootstrap installed on %s.\n"), | ||
769 | partname (disk_device, xbsd_part_index+1, 0)); | ||
770 | #endif | ||
771 | |||
772 | sync_disks(); | ||
773 | } | ||
774 | |||
775 | static void | ||
776 | xbsd_change_fstype(void) | ||
777 | { | ||
778 | int i; | ||
779 | |||
780 | i = xbsd_get_part_index(xbsd_dlabel.d_npartitions); | ||
781 | xbsd_dlabel.d_partitions[i].p_fstype = read_hex(xbsd_fstypes); | ||
782 | } | ||
783 | |||
784 | static int | ||
785 | xbsd_get_part_index(int max) | ||
786 | { | ||
787 | char prompt[256]; | ||
788 | char l; | ||
789 | |||
790 | snprintf(prompt, sizeof(prompt), _("Partition (a-%c): "), 'a' + max - 1); | ||
791 | do | ||
792 | l = tolower(read_nonempty(prompt)); | ||
793 | while (l < 'a' || l > 'a' + max - 1); | ||
794 | return l - 'a'; | ||
795 | } | ||
796 | |||
797 | static int | ||
798 | xbsd_check_new_partition(int *i) | ||
799 | { | ||
800 | /* room for more? various BSD flavours have different maxima */ | ||
801 | if (xbsd_dlabel.d_npartitions == BSD_MAXPARTITIONS) { | ||
802 | int t; | ||
803 | |||
804 | for (t = 0; t < BSD_MAXPARTITIONS; t++) | ||
805 | if (xbsd_dlabel.d_partitions[t].p_size == 0) | ||
806 | break; | ||
807 | |||
808 | if (t == BSD_MAXPARTITIONS) { | ||
809 | fprintf(stderr, _("The maximum number of partitions " | ||
810 | "has been created\n")); | ||
811 | return 0; | ||
812 | } | ||
813 | } | ||
814 | |||
815 | *i = xbsd_get_part_index (BSD_MAXPARTITIONS); | ||
816 | |||
817 | if (*i >= xbsd_dlabel.d_npartitions) | ||
818 | xbsd_dlabel.d_npartitions = (*i) + 1; | ||
819 | |||
820 | if (xbsd_dlabel.d_partitions[*i].p_size != 0) { | ||
821 | fprintf(stderr, _("This partition already exists.\n")); | ||
822 | return 0; | ||
823 | } | ||
824 | |||
825 | return 1; | ||
826 | } | ||
827 | |||
828 | static void | ||
829 | xbsd_list_types(void) | ||
830 | { | ||
831 | list_types(xbsd_fstypes); | ||
832 | } | ||
833 | |||
834 | static u_short | ||
835 | xbsd_dkcksum(struct xbsd_disklabel *lp) | ||
836 | { | ||
837 | u_short *start, *end; | ||
838 | u_short sum = 0; | ||
839 | |||
840 | start = (u_short *) lp; | ||
841 | end = (u_short *) &lp->d_partitions[lp->d_npartitions]; | ||
842 | while (start < end) | ||
843 | sum ^= *start++; | ||
844 | return sum; | ||
845 | } | ||
846 | |||
847 | static int | ||
848 | xbsd_initlabel(struct partition *p, struct xbsd_disklabel *d) | ||
849 | { | ||
850 | struct xbsd_partition *pp; | ||
851 | |||
852 | get_geometry(); | ||
853 | memset(d, 0, sizeof(struct xbsd_disklabel)); | ||
854 | |||
855 | d->d_magic = BSD_DISKMAGIC; | ||
856 | |||
857 | if (strncmp(disk_device, "/dev/sd", 7) == 0) | ||
858 | d->d_type = BSD_DTYPE_SCSI; | ||
859 | else | ||
860 | d->d_type = BSD_DTYPE_ST506; | ||
861 | |||
862 | #if !defined (__alpha__) | ||
863 | d->d_flags = BSD_D_DOSPART; | ||
864 | #else | ||
865 | d->d_flags = 0; | ||
866 | #endif | ||
867 | d->d_secsize = SECTOR_SIZE; /* bytes/sector */ | ||
868 | d->d_nsectors = sectors; /* sectors/track */ | ||
869 | d->d_ntracks = heads; /* tracks/cylinder (heads) */ | ||
870 | d->d_ncylinders = cylinders; | ||
871 | d->d_secpercyl = sectors * heads;/* sectors/cylinder */ | ||
872 | if (d->d_secpercyl == 0) | ||
873 | d->d_secpercyl = 1; /* avoid segfaults */ | ||
874 | d->d_secperunit = d->d_secpercyl * d->d_ncylinders; | ||
875 | |||
876 | d->d_rpm = 3600; | ||
877 | d->d_interleave = 1; | ||
878 | d->d_trackskew = 0; | ||
879 | d->d_cylskew = 0; | ||
880 | d->d_headswitch = 0; | ||
881 | d->d_trkseek = 0; | ||
882 | |||
883 | d->d_magic2 = BSD_DISKMAGIC; | ||
884 | d->d_bbsize = BSD_BBSIZE; | ||
885 | d->d_sbsize = BSD_SBSIZE; | ||
886 | |||
887 | #if !defined (__alpha__) | ||
888 | d->d_npartitions = 4; | ||
889 | pp = &d->d_partitions[2]; /* Partition C should be | ||
890 | the NetBSD partition */ | ||
891 | pp->p_offset = get_start_sect(p); | ||
892 | pp->p_size = get_nr_sects(p); | ||
893 | pp->p_fstype = BSD_FS_UNUSED; | ||
894 | pp = &d->d_partitions[3]; /* Partition D should be | ||
895 | the whole disk */ | ||
896 | pp->p_offset = 0; | ||
897 | pp->p_size = d->d_secperunit; | ||
898 | pp->p_fstype = BSD_FS_UNUSED; | ||
899 | #elif defined (__alpha__) | ||
900 | d->d_npartitions = 3; | ||
901 | pp = &d->d_partitions[2]; /* Partition C should be | ||
902 | the whole disk */ | ||
903 | pp->p_offset = 0; | ||
904 | pp->p_size = d->d_secperunit; | ||
905 | pp->p_fstype = BSD_FS_UNUSED; | ||
906 | #endif | ||
907 | |||
908 | return 1; | ||
909 | } | ||
910 | |||
911 | /* | ||
912 | * Read a xbsd_disklabel from sector 0 or from the starting sector of p. | ||
913 | * If it has the right magic, return 1. | ||
914 | */ | ||
915 | static int | ||
916 | xbsd_readlabel (struct partition *p, struct xbsd_disklabel *d) | ||
917 | { | ||
918 | int t, sector; | ||
919 | |||
920 | /* p is used only to get the starting sector */ | ||
921 | #if !defined (__alpha__) | ||
922 | sector = (p ? get_start_sect(p) : 0); | ||
923 | #elif defined (__alpha__) | ||
924 | sector = 0; | ||
925 | #endif | ||
926 | |||
927 | if (lseek(fd, sector * SECTOR_SIZE, SEEK_SET) == -1) | ||
928 | fdisk_fatal(unable_to_seek); | ||
929 | if (BSD_BBSIZE != read(fd, disklabelbuffer, BSD_BBSIZE)) | ||
930 | fdisk_fatal(unable_to_read); | ||
931 | |||
932 | memmove(d, &disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE + BSD_LABELOFFSET], | ||
933 | sizeof(struct xbsd_disklabel)); | ||
934 | |||
935 | if (d->d_magic != BSD_DISKMAGIC || d->d_magic2 != BSD_DISKMAGIC) | ||
936 | return 0; | ||
937 | |||
938 | for (t = d->d_npartitions; t < BSD_MAXPARTITIONS; t++) { | ||
939 | d->d_partitions[t].p_size = 0; | ||
940 | d->d_partitions[t].p_offset = 0; | ||
941 | d->d_partitions[t].p_fstype = BSD_FS_UNUSED; | ||
942 | } | ||
943 | |||
944 | if (d->d_npartitions > BSD_MAXPARTITIONS) | ||
945 | fprintf(stderr, _("Warning: too many partitions " | ||
946 | "(%d, maximum is %d).\n"), | ||
947 | d->d_npartitions, BSD_MAXPARTITIONS); | ||
948 | return 1; | ||
949 | } | ||
950 | |||
951 | static int | ||
952 | xbsd_writelabel (struct partition *p, struct xbsd_disklabel *d) | ||
953 | { | ||
954 | unsigned int sector; | ||
955 | |||
956 | #if !defined (__alpha__) && !defined (__powerpc__) && !defined (__hppa__) | ||
957 | sector = get_start_sect(p) + BSD_LABELSECTOR; | ||
958 | #else | ||
959 | sector = BSD_LABELSECTOR; | ||
960 | #endif | ||
961 | |||
962 | d->d_checksum = 0; | ||
963 | d->d_checksum = xbsd_dkcksum (d); | ||
964 | |||
965 | /* This is necessary if we want to write the bootstrap later, | ||
966 | otherwise we'd write the old disklabel with the bootstrap. | ||
967 | */ | ||
968 | memmove(&disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE + BSD_LABELOFFSET], | ||
969 | d, sizeof(struct xbsd_disklabel)); | ||
970 | |||
971 | #if defined (__alpha__) && BSD_LABELSECTOR == 0 | ||
972 | alpha_bootblock_checksum(disklabelbuffer); | ||
973 | if (lseek(fd, 0, SEEK_SET) == -1) | ||
974 | fdisk_fatal(unable_to_seek); | ||
975 | if (BSD_BBSIZE != write(fd, disklabelbuffer, BSD_BBSIZE)) | ||
976 | fdisk_fatal(unable_to_write); | ||
977 | #else | ||
978 | if (lseek(fd, sector * SECTOR_SIZE + BSD_LABELOFFSET, SEEK_SET) == -1) | ||
979 | fdisk_fatal(unable_to_seek); | ||
980 | if (sizeof(struct xbsd_disklabel) != write(fd, d, sizeof(struct xbsd_disklabel))) | ||
981 | fdisk_fatal(unable_to_write); | ||
982 | #endif | ||
983 | sync_disks(); | ||
984 | return 1; | ||
985 | } | ||
986 | |||
987 | |||
988 | #if !defined (__alpha__) | ||
989 | static int | ||
990 | xbsd_translate_fstype(int linux_type) | ||
991 | { | ||
992 | switch (linux_type) { | ||
993 | case 0x01: /* DOS 12-bit FAT */ | ||
994 | case 0x04: /* DOS 16-bit <32M */ | ||
995 | case 0x06: /* DOS 16-bit >=32M */ | ||
996 | case 0xe1: /* DOS access */ | ||
997 | case 0xe3: /* DOS R/O */ | ||
998 | case 0xf2: /* DOS secondary */ | ||
999 | return BSD_FS_MSDOS; | ||
1000 | case 0x07: /* OS/2 HPFS */ | ||
1001 | return BSD_FS_HPFS; | ||
1002 | default: | ||
1003 | return BSD_FS_OTHER; | ||
1004 | } | ||
1005 | } | ||
1006 | |||
1007 | static void | ||
1008 | xbsd_link_part(void) | ||
1009 | { | ||
1010 | int k, i; | ||
1011 | struct partition *p; | ||
1012 | |||
1013 | k = get_partition(1, partitions); | ||
1014 | |||
1015 | if (!xbsd_check_new_partition(&i)) | ||
1016 | return; | ||
1017 | |||
1018 | p = get_part_table(k); | ||
1019 | |||
1020 | xbsd_dlabel.d_partitions[i].p_size = get_nr_sects(p); | ||
1021 | xbsd_dlabel.d_partitions[i].p_offset = get_start_sect(p); | ||
1022 | xbsd_dlabel.d_partitions[i].p_fstype = xbsd_translate_fstype(p->sys_ind); | ||
1023 | } | ||
1024 | #endif | ||
1025 | |||
1026 | #if defined (__alpha__) | ||
1027 | |||
1028 | #if !defined(__GLIBC__) | ||
1029 | typedef unsigned long long uint64_t; | ||
1030 | #endif | ||
1031 | |||
1032 | static void | ||
1033 | alpha_bootblock_checksum(char *boot) | ||
1034 | { | ||
1035 | uint64_t *dp, sum; | ||
1036 | int i; | ||
1037 | |||
1038 | dp = (uint64_t *)boot; | ||
1039 | sum = 0; | ||
1040 | for (i = 0; i < 63; i++) | ||
1041 | sum += dp[i]; | ||
1042 | dp[63] = sum; | ||
1043 | } | ||
1044 | #endif /* __alpha__ */ | ||
1045 | |||
1046 | #endif /* OSF_LABEL */ | ||
diff --git a/util-linux/fdisk_sgi.c b/util-linux/fdisk_sgi.c new file mode 100644 index 000000000..548a70bdc --- /dev/null +++ b/util-linux/fdisk_sgi.c | |||
@@ -0,0 +1,885 @@ | |||
1 | #ifdef CONFIG_FEATURE_SGI_LABEL | ||
2 | |||
3 | /* | ||
4 | * Copyright (C) Andreas Neuper, Sep 1998. | ||
5 | * This file may be modified and redistributed under | ||
6 | * the terms of the GNU Public License. | ||
7 | */ | ||
8 | |||
9 | struct device_parameter { /* 48 bytes */ | ||
10 | unsigned char skew; | ||
11 | unsigned char gap1; | ||
12 | unsigned char gap2; | ||
13 | unsigned char sparecyl; | ||
14 | unsigned short pcylcount; | ||
15 | unsigned short head_vol0; | ||
16 | unsigned short ntrks; /* tracks in cyl 0 or vol 0 */ | ||
17 | unsigned char cmd_tag_queue_depth; | ||
18 | unsigned char unused0; | ||
19 | unsigned short unused1; | ||
20 | unsigned short nsect; /* sectors/tracks in cyl 0 or vol 0 */ | ||
21 | unsigned short bytes; | ||
22 | unsigned short ilfact; | ||
23 | unsigned int flags; /* controller flags */ | ||
24 | unsigned int datarate; | ||
25 | unsigned int retries_on_error; | ||
26 | unsigned int ms_per_word; | ||
27 | unsigned short xylogics_gap1; | ||
28 | unsigned short xylogics_syncdelay; | ||
29 | unsigned short xylogics_readdelay; | ||
30 | unsigned short xylogics_gap2; | ||
31 | unsigned short xylogics_readgate; | ||
32 | unsigned short xylogics_writecont; | ||
33 | }; | ||
34 | |||
35 | /* | ||
36 | * controller flags | ||
37 | */ | ||
38 | #define SECTOR_SLIP 0x01 | ||
39 | #define SECTOR_FWD 0x02 | ||
40 | #define TRACK_FWD 0x04 | ||
41 | #define TRACK_MULTIVOL 0x08 | ||
42 | #define IGNORE_ERRORS 0x10 | ||
43 | #define RESEEK 0x20 | ||
44 | #define ENABLE_CMDTAGQ 0x40 | ||
45 | |||
46 | typedef struct { | ||
47 | unsigned int magic; /* expect SGI_LABEL_MAGIC */ | ||
48 | unsigned short boot_part; /* active boot partition */ | ||
49 | unsigned short swap_part; /* active swap partition */ | ||
50 | unsigned char boot_file[16]; /* name of the bootfile */ | ||
51 | struct device_parameter devparam; /* 1 * 48 bytes */ | ||
52 | struct volume_directory { /* 15 * 16 bytes */ | ||
53 | unsigned char vol_file_name[8]; /* a character array */ | ||
54 | unsigned int vol_file_start; /* number of logical block */ | ||
55 | unsigned int vol_file_size; /* number of bytes */ | ||
56 | } directory[15]; | ||
57 | struct sgi_partinfo { /* 16 * 12 bytes */ | ||
58 | unsigned int num_sectors; /* number of blocks */ | ||
59 | unsigned int start_sector; /* must be cylinder aligned */ | ||
60 | unsigned int id; | ||
61 | } partitions[16]; | ||
62 | unsigned int csum; | ||
63 | unsigned int fillbytes; | ||
64 | } sgi_partition; | ||
65 | |||
66 | typedef struct { | ||
67 | unsigned int magic; /* looks like a magic number */ | ||
68 | unsigned int a2; | ||
69 | unsigned int a3; | ||
70 | unsigned int a4; | ||
71 | unsigned int b1; | ||
72 | unsigned short b2; | ||
73 | unsigned short b3; | ||
74 | unsigned int c[16]; | ||
75 | unsigned short d[3]; | ||
76 | unsigned char scsi_string[50]; | ||
77 | unsigned char serial[137]; | ||
78 | unsigned short check1816; | ||
79 | unsigned char installer[225]; | ||
80 | } sgiinfo; | ||
81 | |||
82 | #define SGI_LABEL_MAGIC 0x0be5a941 | ||
83 | #define SGI_LABEL_MAGIC_SWAPPED 0x41a9e50b | ||
84 | #define SGI_INFO_MAGIC 0x00072959 | ||
85 | #define SGI_INFO_MAGIC_SWAPPED 0x59290700 | ||
86 | |||
87 | #define SGI_SSWAP16(x) (sgi_other_endian ? __swap16(x) : (uint16_t)(x)) | ||
88 | #define SGI_SSWAP32(x) (sgi_other_endian ? __swap32(x) : (uint32_t)(x)) | ||
89 | |||
90 | #define sgilabel ((sgi_partition *)MBRbuffer) | ||
91 | #define sgiparam (sgilabel->devparam) | ||
92 | |||
93 | /* | ||
94 | * | ||
95 | * fdisksgilabel.c | ||
96 | * | ||
97 | * Copyright (C) Andreas Neuper, Sep 1998. | ||
98 | * This file may be modified and redistributed under | ||
99 | * the terms of the GNU Public License. | ||
100 | * | ||
101 | * Sat Mar 20 EST 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br> | ||
102 | * Internationalization | ||
103 | */ | ||
104 | |||
105 | |||
106 | static int sgi_other_endian; | ||
107 | static int debug; | ||
108 | static short sgi_volumes = 1; | ||
109 | |||
110 | /* | ||
111 | * only dealing with free blocks here | ||
112 | */ | ||
113 | |||
114 | typedef struct { | ||
115 | unsigned int first; | ||
116 | unsigned int last; | ||
117 | } freeblocks; | ||
118 | static freeblocks freelist[17]; /* 16 partitions can produce 17 vacant slots */ | ||
119 | |||
120 | static void | ||
121 | setfreelist(int i, unsigned int f, unsigned int l) | ||
122 | { | ||
123 | freelist[i].first = f; | ||
124 | freelist[i].last = l; | ||
125 | } | ||
126 | |||
127 | static void | ||
128 | add2freelist(unsigned int f, unsigned int l) | ||
129 | { | ||
130 | int i; | ||
131 | for (i = 0; i < 17 ; i++) | ||
132 | if (freelist[i].last == 0) | ||
133 | break; | ||
134 | setfreelist(i, f, l); | ||
135 | } | ||
136 | |||
137 | static void | ||
138 | clearfreelist(void) | ||
139 | { | ||
140 | int i; | ||
141 | |||
142 | for (i = 0; i < 17 ; i++) | ||
143 | setfreelist(i, 0, 0); | ||
144 | } | ||
145 | |||
146 | static unsigned int | ||
147 | isinfreelist(unsigned int b) | ||
148 | { | ||
149 | int i; | ||
150 | |||
151 | for (i = 0; i < 17 ; i++) | ||
152 | if (freelist[i].first <= b && freelist[i].last >= b) | ||
153 | return freelist[i].last; | ||
154 | return 0; | ||
155 | } | ||
156 | /* return last vacant block of this stride (never 0). */ | ||
157 | /* the '>=' is not quite correct, but simplifies the code */ | ||
158 | /* | ||
159 | * end of free blocks section | ||
160 | */ | ||
161 | |||
162 | static const struct systypes sgi_sys_types[] = { | ||
163 | /* SGI_VOLHDR */ { "\x00" "SGI volhdr" }, | ||
164 | /* 0x01 */ { "\x01" "SGI trkrepl" }, | ||
165 | /* 0x02 */ { "\x02" "SGI secrepl" }, | ||
166 | /* SGI_SWAP */ { "\x03" "SGI raw" }, | ||
167 | /* 0x04 */ { "\x04" "SGI bsd" }, | ||
168 | /* 0x05 */ { "\x05" "SGI sysv" }, | ||
169 | /* SGI_ENTIRE_DISK */ { "\x06" "SGI volume" }, | ||
170 | /* SGI_EFS */ { "\x07" "SGI efs" }, | ||
171 | /* 0x08 */ { "\x08" "SGI lvol" }, | ||
172 | /* 0x09 */ { "\x09" "SGI rlvol" }, | ||
173 | /* SGI_XFS */ { "\x0a" "SGI xfs" }, | ||
174 | /* SGI_XFSLOG */ { "\x0b" "SGI xfslog" }, | ||
175 | /* SGI_XLV */ { "\x0c" "SGI xlv" }, | ||
176 | /* SGI_XVM */ { "\x0d" "SGI xvm" }, | ||
177 | /* LINUX_SWAP */ { "\x82" "Linux swap" }, | ||
178 | /* LINUX_NATIVE */ { "\x83" "Linux native" }, | ||
179 | /* LINUX_LVM */ { "\x8d" "Linux LVM" }, | ||
180 | /* LINUX_RAID */ { "\xfd" "Linux RAID" }, | ||
181 | { NULL } | ||
182 | }; | ||
183 | |||
184 | |||
185 | static int | ||
186 | sgi_get_nsect(void) | ||
187 | { | ||
188 | return SGI_SSWAP16(sgilabel->devparam.nsect); | ||
189 | } | ||
190 | |||
191 | static int | ||
192 | sgi_get_ntrks(void) | ||
193 | { | ||
194 | return SGI_SSWAP16(sgilabel->devparam.ntrks); | ||
195 | } | ||
196 | |||
197 | static unsigned int | ||
198 | two_s_complement_32bit_sum(unsigned int* base, int size /* in bytes */) | ||
199 | { | ||
200 | int i = 0; | ||
201 | unsigned int sum = 0; | ||
202 | |||
203 | size /= sizeof(unsigned int); | ||
204 | for (i = 0; i < size; i++) | ||
205 | sum -= SGI_SSWAP32(base[i]); | ||
206 | return sum; | ||
207 | } | ||
208 | |||
209 | static int | ||
210 | check_sgi_label(void) | ||
211 | { | ||
212 | if (sizeof(sgilabel) > 512) { | ||
213 | fprintf(stderr, | ||
214 | _("According to MIPS Computer Systems, Inc the " | ||
215 | "Label must not contain more than 512 bytes\n")); | ||
216 | exit(1); | ||
217 | } | ||
218 | |||
219 | if (sgilabel->magic != SGI_LABEL_MAGIC | ||
220 | && sgilabel->magic != SGI_LABEL_MAGIC_SWAPPED) { | ||
221 | current_label_type = label_dos; | ||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | sgi_other_endian = (sgilabel->magic == SGI_LABEL_MAGIC_SWAPPED); | ||
226 | /* | ||
227 | * test for correct checksum | ||
228 | */ | ||
229 | if (two_s_complement_32bit_sum((unsigned int*)sgilabel, | ||
230 | sizeof(*sgilabel))) { | ||
231 | fprintf(stderr, | ||
232 | _("Detected sgi disklabel with wrong checksum.\n")); | ||
233 | } | ||
234 | update_units(); | ||
235 | current_label_type = label_sgi; | ||
236 | partitions = 16; | ||
237 | sgi_volumes = 15; | ||
238 | return 1; | ||
239 | } | ||
240 | |||
241 | static unsigned int | ||
242 | sgi_get_start_sector(int i) | ||
243 | { | ||
244 | return SGI_SSWAP32(sgilabel->partitions[i].start_sector); | ||
245 | } | ||
246 | |||
247 | static unsigned int | ||
248 | sgi_get_num_sectors(int i) | ||
249 | { | ||
250 | return SGI_SSWAP32(sgilabel->partitions[i].num_sectors); | ||
251 | } | ||
252 | |||
253 | static int | ||
254 | sgi_get_sysid(int i) | ||
255 | { | ||
256 | return SGI_SSWAP32(sgilabel->partitions[i].id); | ||
257 | } | ||
258 | |||
259 | static int | ||
260 | sgi_get_bootpartition(void) | ||
261 | { | ||
262 | return SGI_SSWAP16(sgilabel->boot_part); | ||
263 | } | ||
264 | |||
265 | static int | ||
266 | sgi_get_swappartition(void) | ||
267 | { | ||
268 | return SGI_SSWAP16(sgilabel->swap_part); | ||
269 | } | ||
270 | |||
271 | static void | ||
272 | sgi_list_table(int xtra) | ||
273 | { | ||
274 | int i, w, wd; | ||
275 | int kpi = 0; /* kernel partition ID */ | ||
276 | |||
277 | if(xtra) { | ||
278 | printf(_("\nDisk %s (SGI disk label): %d heads, %d sectors\n" | ||
279 | "%d cylinders, %d physical cylinders\n" | ||
280 | "%d extra sects/cyl, interleave %d:1\n" | ||
281 | "%s\n" | ||
282 | "Units = %s of %d * 512 bytes\n\n"), | ||
283 | disk_device, heads, sectors, cylinders, | ||
284 | SGI_SSWAP16(sgiparam.pcylcount), | ||
285 | SGI_SSWAP16(sgiparam.sparecyl), | ||
286 | SGI_SSWAP16(sgiparam.ilfact), | ||
287 | (char *)sgilabel, | ||
288 | str_units(PLURAL), units_per_sector); | ||
289 | } else { | ||
290 | printf( _("\nDisk %s (SGI disk label): " | ||
291 | "%d heads, %d sectors, %d cylinders\n" | ||
292 | "Units = %s of %d * 512 bytes\n\n"), | ||
293 | disk_device, heads, sectors, cylinders, | ||
294 | str_units(PLURAL), units_per_sector ); | ||
295 | } | ||
296 | |||
297 | w = strlen(disk_device); | ||
298 | wd = strlen(_("Device")); | ||
299 | if (w < wd) | ||
300 | w = wd; | ||
301 | |||
302 | printf(_("----- partitions -----\n" | ||
303 | "Pt# %*s Info Start End Sectors Id System\n"), | ||
304 | w + 2, _("Device")); | ||
305 | for (i = 0 ; i < partitions; i++) { | ||
306 | if( sgi_get_num_sectors(i) || debug ) { | ||
307 | uint32_t start = sgi_get_start_sector(i); | ||
308 | uint32_t len = sgi_get_num_sectors(i); | ||
309 | kpi++; /* only count nonempty partitions */ | ||
310 | printf( | ||
311 | "%2d: %s %4s %9ld %9ld %9ld %2x %s\n", | ||
312 | /* fdisk part number */ i+1, | ||
313 | /* device */ partname(disk_device, kpi, w+3), | ||
314 | /* flags */ (sgi_get_swappartition() == i) ? "swap" : | ||
315 | /* flags */ (sgi_get_bootpartition() == i) ? "boot" : " ", | ||
316 | /* start */ (long) scround(start), | ||
317 | /* end */ (long) scround(start+len)-1, | ||
318 | /* no odd flag on end */(long) len, | ||
319 | /* type id */ sgi_get_sysid(i), | ||
320 | /* type name */ partition_type(sgi_get_sysid(i))); | ||
321 | } | ||
322 | } | ||
323 | printf(_("----- Bootinfo -----\nBootfile: %s\n" | ||
324 | "----- Directory Entries -----\n"), | ||
325 | sgilabel->boot_file); | ||
326 | for (i = 0 ; i < sgi_volumes; i++) { | ||
327 | if (sgilabel->directory[i].vol_file_size) { | ||
328 | uint32_t start = SGI_SSWAP32(sgilabel->directory[i].vol_file_start); | ||
329 | uint32_t len = SGI_SSWAP32(sgilabel->directory[i].vol_file_size); | ||
330 | unsigned char *name = sgilabel->directory[i].vol_file_name; | ||
331 | |||
332 | printf(_("%2d: %-10s sector%5u size%8u\n"), | ||
333 | i, (char*)name, (unsigned int) start, (unsigned int) len); | ||
334 | } | ||
335 | } | ||
336 | } | ||
337 | |||
338 | static void | ||
339 | sgi_set_bootpartition(int i) | ||
340 | { | ||
341 | sgilabel->boot_part = SGI_SSWAP16(((short)i)); | ||
342 | } | ||
343 | |||
344 | static unsigned int | ||
345 | sgi_get_lastblock(void) | ||
346 | { | ||
347 | return heads * sectors * cylinders; | ||
348 | } | ||
349 | |||
350 | static void | ||
351 | sgi_set_swappartition(int i) | ||
352 | { | ||
353 | sgilabel->swap_part = SGI_SSWAP16(((short)i)); | ||
354 | } | ||
355 | |||
356 | static int | ||
357 | sgi_check_bootfile(const char* aFile) | ||
358 | { | ||
359 | if (strlen(aFile) < 3) /* "/a\n" is minimum */ { | ||
360 | printf(_("\nInvalid Bootfile!\n" | ||
361 | "\tThe bootfile must be an absolute non-zero pathname,\n" | ||
362 | "\te.g. \"/unix\" or \"/unix.save\".\n")); | ||
363 | return 0; | ||
364 | } else { | ||
365 | if (strlen(aFile) > 16) { | ||
366 | printf(_("\n\tName of Bootfile too long: " | ||
367 | "16 bytes maximum.\n")); | ||
368 | return 0; | ||
369 | } else { | ||
370 | if (aFile[0] != '/') { | ||
371 | printf(_("\n\tBootfile must have a " | ||
372 | "fully qualified pathname.\n")); | ||
373 | return 0; | ||
374 | } | ||
375 | } | ||
376 | } | ||
377 | if (strncmp(aFile, (char*)sgilabel->boot_file, 16)) { | ||
378 | printf(_("\n\tBe aware, that the bootfile is not checked for existence.\n\t" | ||
379 | "SGI's default is \"/unix\" and for backup \"/unix.save\".\n")); | ||
380 | /* filename is correct and did change */ | ||
381 | return 1; | ||
382 | } | ||
383 | return 0; /* filename did not change */ | ||
384 | } | ||
385 | |||
386 | static const char * | ||
387 | sgi_get_bootfile(void) | ||
388 | { | ||
389 | return (char*)sgilabel->boot_file; | ||
390 | } | ||
391 | |||
392 | static void | ||
393 | sgi_set_bootfile(const char* aFile) | ||
394 | { | ||
395 | int i = 0; | ||
396 | |||
397 | if (sgi_check_bootfile(aFile)) { | ||
398 | while (i < 16) { | ||
399 | if ((aFile[i] != '\n') /* in principle caught again by next line */ | ||
400 | && (strlen(aFile) > i)) | ||
401 | sgilabel->boot_file[i] = aFile[i]; | ||
402 | else | ||
403 | sgilabel->boot_file[i] = 0; | ||
404 | i++; | ||
405 | } | ||
406 | printf(_("\n\tBootfile is changed to \"%s\".\n"), sgilabel->boot_file); | ||
407 | } | ||
408 | } | ||
409 | |||
410 | static void | ||
411 | create_sgiinfo(void) | ||
412 | { | ||
413 | /* I keep SGI's habit to write the sgilabel to the second block */ | ||
414 | sgilabel->directory[0].vol_file_start = SGI_SSWAP32(2); | ||
415 | sgilabel->directory[0].vol_file_size = SGI_SSWAP32(sizeof(sgiinfo)); | ||
416 | strncpy((char*)sgilabel->directory[0].vol_file_name, "sgilabel", 8); | ||
417 | } | ||
418 | |||
419 | static sgiinfo *fill_sgiinfo(void); | ||
420 | |||
421 | static void | ||
422 | sgi_write_table(void) | ||
423 | { | ||
424 | sgilabel->csum = 0; | ||
425 | sgilabel->csum = SGI_SSWAP32(two_s_complement_32bit_sum( | ||
426 | (unsigned int*)sgilabel, sizeof(*sgilabel))); | ||
427 | assert(two_s_complement_32bit_sum( | ||
428 | (unsigned int*)sgilabel, sizeof(*sgilabel)) == 0); | ||
429 | |||
430 | if (lseek(fd, 0, SEEK_SET) < 0) | ||
431 | fdisk_fatal(unable_to_seek); | ||
432 | if (write(fd, sgilabel, SECTOR_SIZE) != SECTOR_SIZE) | ||
433 | fdisk_fatal(unable_to_write); | ||
434 | if (!strncmp((char*)sgilabel->directory[0].vol_file_name, "sgilabel", 8)) { | ||
435 | /* | ||
436 | * keep this habit of first writing the "sgilabel". | ||
437 | * I never tested whether it works without (AN 981002). | ||
438 | */ | ||
439 | sgiinfo *info = fill_sgiinfo(); | ||
440 | int infostartblock = SGI_SSWAP32(sgilabel->directory[0].vol_file_start); | ||
441 | if (lseek(fd, infostartblock*SECTOR_SIZE, SEEK_SET) < 0) | ||
442 | fdisk_fatal(unable_to_seek); | ||
443 | if (write(fd, info, SECTOR_SIZE) != SECTOR_SIZE) | ||
444 | fdisk_fatal(unable_to_write); | ||
445 | free(info); | ||
446 | } | ||
447 | } | ||
448 | |||
449 | static int | ||
450 | compare_start(int *x, int *y) | ||
451 | { | ||
452 | /* | ||
453 | * sort according to start sectors | ||
454 | * and prefers largest partition: | ||
455 | * entry zero is entire disk entry | ||
456 | */ | ||
457 | unsigned int i = *x; | ||
458 | unsigned int j = *y; | ||
459 | unsigned int a = sgi_get_start_sector(i); | ||
460 | unsigned int b = sgi_get_start_sector(j); | ||
461 | unsigned int c = sgi_get_num_sectors(i); | ||
462 | unsigned int d = sgi_get_num_sectors(j); | ||
463 | |||
464 | if (a == b) | ||
465 | return (d > c) ? 1 : (d == c) ? 0 : -1; | ||
466 | return (a > b) ? 1 : -1; | ||
467 | } | ||
468 | |||
469 | |||
470 | static int | ||
471 | verify_sgi(int verbose) | ||
472 | { | ||
473 | int Index[16]; /* list of valid partitions */ | ||
474 | int sortcount = 0; /* number of used partitions, i.e. non-zero lengths */ | ||
475 | int entire = 0, i = 0; | ||
476 | unsigned int start = 0; | ||
477 | long long gap = 0; /* count unused blocks */ | ||
478 | unsigned int lastblock = sgi_get_lastblock(); | ||
479 | |||
480 | clearfreelist(); | ||
481 | for (i = 0; i < 16; i++) { | ||
482 | if (sgi_get_num_sectors(i) != 0) { | ||
483 | Index[sortcount++] = i; | ||
484 | if (sgi_get_sysid(i) == SGI_ENTIRE_DISK) { | ||
485 | if (entire++ == 1) { | ||
486 | if (verbose) | ||
487 | printf(_("More than one entire disk entry present.\n")); | ||
488 | } | ||
489 | } | ||
490 | } | ||
491 | } | ||
492 | if (sortcount == 0) { | ||
493 | if (verbose) | ||
494 | printf(_("No partitions defined\n")); | ||
495 | return (lastblock > 0) ? 1 : (lastblock == 0) ? 0 : -1; | ||
496 | } | ||
497 | qsort(Index, sortcount, sizeof(Index[0]), (void*)compare_start); | ||
498 | if (sgi_get_sysid(Index[0]) == SGI_ENTIRE_DISK) { | ||
499 | if ((Index[0] != 10) && verbose) | ||
500 | printf(_("IRIX likes when Partition 11 covers the entire disk.\n")); | ||
501 | if ((sgi_get_start_sector(Index[0]) != 0) && verbose) | ||
502 | printf(_("The entire disk partition should start " | ||
503 | "at block 0,\n" | ||
504 | "not at diskblock %d.\n"), | ||
505 | sgi_get_start_sector(Index[0])); | ||
506 | if (debug) /* I do not understand how some disks fulfil it */ | ||
507 | if ((sgi_get_num_sectors(Index[0]) != lastblock) && verbose) | ||
508 | printf(_("The entire disk partition is only %d diskblock large,\n" | ||
509 | "but the disk is %d diskblocks long.\n"), | ||
510 | sgi_get_num_sectors(Index[0]), lastblock); | ||
511 | lastblock = sgi_get_num_sectors(Index[0]); | ||
512 | } else { | ||
513 | if (verbose) | ||
514 | printf(_("One Partition (#11) should cover the entire disk.\n")); | ||
515 | if (debug > 2) | ||
516 | printf("sysid=%d\tpartition=%d\n", | ||
517 | sgi_get_sysid(Index[0]), Index[0]+1); | ||
518 | } | ||
519 | for (i = 1, start = 0; i < sortcount; i++) { | ||
520 | int cylsize = sgi_get_nsect() * sgi_get_ntrks(); | ||
521 | |||
522 | if ((sgi_get_start_sector(Index[i]) % cylsize) != 0) { | ||
523 | if (debug) /* I do not understand how some disks fulfil it */ | ||
524 | if (verbose) | ||
525 | printf(_("Partition %d does not start on cylinder boundary.\n"), | ||
526 | Index[i]+1); | ||
527 | } | ||
528 | if (sgi_get_num_sectors(Index[i]) % cylsize != 0) { | ||
529 | if (debug) /* I do not understand how some disks fulfil it */ | ||
530 | if (verbose) | ||
531 | printf(_("Partition %d does not end on cylinder boundary.\n"), | ||
532 | Index[i]+1); | ||
533 | } | ||
534 | /* We cannot handle several "entire disk" entries. */ | ||
535 | if (sgi_get_sysid(Index[i]) == SGI_ENTIRE_DISK) continue; | ||
536 | if (start > sgi_get_start_sector(Index[i])) { | ||
537 | if (verbose) | ||
538 | printf(_("The Partition %d and %d overlap by %d sectors.\n"), | ||
539 | Index[i-1]+1, Index[i]+1, | ||
540 | start - sgi_get_start_sector(Index[i])); | ||
541 | if (gap > 0) gap = -gap; | ||
542 | if (gap == 0) gap = -1; | ||
543 | } | ||
544 | if (start < sgi_get_start_sector(Index[i])) { | ||
545 | if (verbose) | ||
546 | printf(_("Unused gap of %8u sectors - sectors %8u-%u\n"), | ||
547 | sgi_get_start_sector(Index[i]) - start, | ||
548 | start, sgi_get_start_sector(Index[i])-1); | ||
549 | gap += sgi_get_start_sector(Index[i]) - start; | ||
550 | add2freelist(start, sgi_get_start_sector(Index[i])); | ||
551 | } | ||
552 | start = sgi_get_start_sector(Index[i]) | ||
553 | + sgi_get_num_sectors(Index[i]); | ||
554 | if (debug > 1) { | ||
555 | if (verbose) | ||
556 | printf("%2d:%12d\t%12d\t%12d\n", Index[i], | ||
557 | sgi_get_start_sector(Index[i]), | ||
558 | sgi_get_num_sectors(Index[i]), | ||
559 | sgi_get_sysid(Index[i])); | ||
560 | } | ||
561 | } | ||
562 | if (start < lastblock) { | ||
563 | if (verbose) | ||
564 | printf(_("Unused gap of %8u sectors - sectors %8u-%u\n"), | ||
565 | lastblock - start, start, lastblock-1); | ||
566 | gap += lastblock - start; | ||
567 | add2freelist(start, lastblock); | ||
568 | } | ||
569 | /* | ||
570 | * Done with arithmetics | ||
571 | * Go for details now | ||
572 | */ | ||
573 | if (verbose) { | ||
574 | if (!sgi_get_num_sectors(sgi_get_bootpartition())) { | ||
575 | printf(_("\nThe boot partition does not exist.\n")); | ||
576 | } | ||
577 | if (!sgi_get_num_sectors(sgi_get_swappartition())) { | ||
578 | printf(_("\nThe swap partition does not exist.\n")); | ||
579 | } else { | ||
580 | if ((sgi_get_sysid(sgi_get_swappartition()) != SGI_SWAP) | ||
581 | && (sgi_get_sysid(sgi_get_swappartition()) != LINUX_SWAP)) | ||
582 | printf(_("\nThe swap partition has no swap type.\n")); | ||
583 | } | ||
584 | if (sgi_check_bootfile("/unix")) | ||
585 | printf(_("\tYou have chosen an unusual boot file name.\n")); | ||
586 | } | ||
587 | return (gap > 0) ? 1 : (gap == 0) ? 0 : -1; | ||
588 | } | ||
589 | |||
590 | static int | ||
591 | sgi_gaps(void) | ||
592 | { | ||
593 | /* | ||
594 | * returned value is: | ||
595 | * = 0 : disk is properly filled to the rim | ||
596 | * < 0 : there is an overlap | ||
597 | * > 0 : there is still some vacant space | ||
598 | */ | ||
599 | return verify_sgi(0); | ||
600 | } | ||
601 | |||
602 | static void | ||
603 | sgi_change_sysid(int i, int sys) | ||
604 | { | ||
605 | if( sgi_get_num_sectors(i) == 0 ) { /* caught already before, ... */ | ||
606 | printf(_("Sorry You may change the Tag of non-empty partitions.\n")); | ||
607 | return; | ||
608 | } | ||
609 | if (((sys != SGI_ENTIRE_DISK) && (sys != SGI_VOLHDR)) | ||
610 | && (sgi_get_start_sector(i) < 1) ) { | ||
611 | read_maybe_empty( | ||
612 | _("It is highly recommended that the partition at offset 0\n" | ||
613 | "is of type \"SGI volhdr\", the IRIX system will rely on it to\n" | ||
614 | "retrieve from its directory standalone tools like sash and fx.\n" | ||
615 | "Only the \"SGI volume\" entire disk section may violate this.\n" | ||
616 | "Type YES if you are sure about tagging this partition differently.\n")); | ||
617 | if (strcmp(line_ptr, _("YES\n"))) | ||
618 | return; | ||
619 | } | ||
620 | sgilabel->partitions[i].id = SGI_SSWAP32(sys); | ||
621 | } | ||
622 | |||
623 | /* returns partition index of first entry marked as entire disk */ | ||
624 | static int | ||
625 | sgi_entire(void) | ||
626 | { | ||
627 | int i; | ||
628 | |||
629 | for (i = 0; i < 16; i++) | ||
630 | if (sgi_get_sysid(i) == SGI_VOLUME) | ||
631 | return i; | ||
632 | return -1; | ||
633 | } | ||
634 | |||
635 | static void | ||
636 | sgi_set_partition(int i, unsigned int start, unsigned int length, int sys) | ||
637 | { | ||
638 | sgilabel->partitions[i].id = SGI_SSWAP32(sys); | ||
639 | sgilabel->partitions[i].num_sectors = SGI_SSWAP32(length); | ||
640 | sgilabel->partitions[i].start_sector = SGI_SSWAP32(start); | ||
641 | set_changed(i); | ||
642 | if (sgi_gaps() < 0) /* rebuild freelist */ | ||
643 | printf(_("Do You know, You got a partition overlap on the disk?\n")); | ||
644 | } | ||
645 | |||
646 | static void | ||
647 | sgi_set_entire(void) | ||
648 | { | ||
649 | int n; | ||
650 | |||
651 | for (n = 10; n < partitions; n++) { | ||
652 | if(!sgi_get_num_sectors(n) ) { | ||
653 | sgi_set_partition(n, 0, sgi_get_lastblock(), SGI_VOLUME); | ||
654 | break; | ||
655 | } | ||
656 | } | ||
657 | } | ||
658 | |||
659 | static void | ||
660 | sgi_set_volhdr(void) | ||
661 | { | ||
662 | int n; | ||
663 | |||
664 | for (n = 8; n < partitions; n++) { | ||
665 | if (!sgi_get_num_sectors(n)) { | ||
666 | /* | ||
667 | * 5 cylinders is an arbitrary value I like | ||
668 | * IRIX 5.3 stored files in the volume header | ||
669 | * (like sash, symmon, fx, ide) with ca. 3200 | ||
670 | * sectors. | ||
671 | */ | ||
672 | if (heads * sectors * 5 < sgi_get_lastblock()) | ||
673 | sgi_set_partition(n, 0, heads * sectors * 5, SGI_VOLHDR); | ||
674 | break; | ||
675 | } | ||
676 | } | ||
677 | } | ||
678 | |||
679 | static void | ||
680 | sgi_delete_partition(int i) | ||
681 | { | ||
682 | sgi_set_partition(i, 0, 0, 0); | ||
683 | } | ||
684 | |||
685 | static void | ||
686 | sgi_add_partition(int n, int sys) | ||
687 | { | ||
688 | char mesg[256]; | ||
689 | unsigned int first = 0, last = 0; | ||
690 | |||
691 | if (n == 10) { | ||
692 | sys = SGI_VOLUME; | ||
693 | } else if (n == 8) { | ||
694 | sys = 0; | ||
695 | } | ||
696 | if(sgi_get_num_sectors(n)) { | ||
697 | printf(_("Partition %d is already defined. Delete " | ||
698 | "it before re-adding it.\n"), n + 1); | ||
699 | return; | ||
700 | } | ||
701 | if ((sgi_entire() == -1) && (sys != SGI_VOLUME)) { | ||
702 | printf(_("Attempting to generate entire disk entry automatically.\n")); | ||
703 | sgi_set_entire(); | ||
704 | sgi_set_volhdr(); | ||
705 | } | ||
706 | if ((sgi_gaps() == 0) && (sys != SGI_VOLUME)) { | ||
707 | printf(_("The entire disk is already covered with partitions.\n")); | ||
708 | return; | ||
709 | } | ||
710 | if (sgi_gaps() < 0) { | ||
711 | printf(_("You got a partition overlap on the disk. Fix it first!\n")); | ||
712 | return; | ||
713 | } | ||
714 | snprintf(mesg, sizeof(mesg), _("First %s"), str_units(SINGULAR)); | ||
715 | while (1) { | ||
716 | if(sys == SGI_VOLUME) { | ||
717 | last = sgi_get_lastblock(); | ||
718 | first = read_int(0, 0, last-1, 0, mesg); | ||
719 | if (first != 0) { | ||
720 | printf(_("It is highly recommended that eleventh partition\n" | ||
721 | "covers the entire disk and is of type 'SGI volume'\n")); | ||
722 | } | ||
723 | } else { | ||
724 | first = freelist[0].first; | ||
725 | last = freelist[0].last; | ||
726 | first = read_int(scround(first), scround(first), scround(last)-1, | ||
727 | 0, mesg); | ||
728 | } | ||
729 | if (display_in_cyl_units) | ||
730 | first *= units_per_sector; | ||
731 | else | ||
732 | first = first; /* align to cylinder if you know how ... */ | ||
733 | if(!last ) | ||
734 | last = isinfreelist(first); | ||
735 | if(last == 0) { | ||
736 | printf(_("You will get a partition overlap on the disk. " | ||
737 | "Fix it first!\n")); | ||
738 | } else | ||
739 | break; | ||
740 | } | ||
741 | snprintf(mesg, sizeof(mesg), _(" Last %s"), str_units(SINGULAR)); | ||
742 | last = read_int(scround(first), scround(last)-1, scround(last)-1, | ||
743 | scround(first), mesg)+1; | ||
744 | if (display_in_cyl_units) | ||
745 | last *= units_per_sector; | ||
746 | else | ||
747 | last = last; /* align to cylinder if You know how ... */ | ||
748 | if ( (sys == SGI_VOLUME) && (first != 0 || last != sgi_get_lastblock() ) ) | ||
749 | printf(_("It is highly recommended that eleventh partition\n" | ||
750 | "covers the entire disk and is of type 'SGI volume'\n")); | ||
751 | sgi_set_partition(n, first, last-first, sys); | ||
752 | } | ||
753 | |||
754 | #ifdef CONFIG_FEATURE_FDISK_ADVANCED | ||
755 | static void | ||
756 | create_sgilabel(void) | ||
757 | { | ||
758 | struct hd_geometry geometry; | ||
759 | struct { | ||
760 | unsigned int start; | ||
761 | unsigned int nsect; | ||
762 | int sysid; | ||
763 | } old[4]; | ||
764 | int i = 0; | ||
765 | long longsectors; /* the number of sectors on the device */ | ||
766 | int res; /* the result from the ioctl */ | ||
767 | int sec_fac; /* the sector factor */ | ||
768 | |||
769 | sec_fac = sector_size / 512; /* determine the sector factor */ | ||
770 | |||
771 | fprintf( stderr, | ||
772 | _("Building a new SGI disklabel. Changes will remain in memory only,\n" | ||
773 | "until you decide to write them. After that, of course, the previous\n" | ||
774 | "content will be unrecoverably lost.\n\n")); | ||
775 | |||
776 | sgi_other_endian = (BYTE_ORDER == LITTLE_ENDIAN); | ||
777 | res = ioctl(fd, BLKGETSIZE, &longsectors); | ||
778 | if (!ioctl(fd, HDIO_GETGEO, &geometry)) { | ||
779 | heads = geometry.heads; | ||
780 | sectors = geometry.sectors; | ||
781 | if (res == 0) { | ||
782 | /* the get device size ioctl was successful */ | ||
783 | cylinders = longsectors / (heads * sectors); | ||
784 | cylinders /= sec_fac; | ||
785 | } else { | ||
786 | /* otherwise print error and use truncated version */ | ||
787 | cylinders = geometry.cylinders; | ||
788 | fprintf(stderr, | ||
789 | _("Warning: BLKGETSIZE ioctl failed on %s. " | ||
790 | "Using geometry cylinder value of %d.\n" | ||
791 | "This value may be truncated for devices" | ||
792 | " > 33.8 GB.\n"), disk_device, cylinders); | ||
793 | } | ||
794 | } | ||
795 | for (i = 0; i < 4; i++) { | ||
796 | old[i].sysid = 0; | ||
797 | if (valid_part_table_flag(MBRbuffer)) { | ||
798 | if(get_part_table(i)->sys_ind) { | ||
799 | old[i].sysid = get_part_table(i)->sys_ind; | ||
800 | old[i].start = get_start_sect(get_part_table(i)); | ||
801 | old[i].nsect = get_nr_sects(get_part_table(i)); | ||
802 | printf(_("Trying to keep parameters of partition %d.\n"), i); | ||
803 | if (debug) | ||
804 | printf(_("ID=%02x\tSTART=%d\tLENGTH=%d\n"), | ||
805 | old[i].sysid, old[i].start, old[i].nsect); | ||
806 | } | ||
807 | } | ||
808 | } | ||
809 | |||
810 | memset(MBRbuffer, 0, sizeof(MBRbuffer)); | ||
811 | sgilabel->magic = SGI_SSWAP32(SGI_LABEL_MAGIC); | ||
812 | sgilabel->boot_part = SGI_SSWAP16(0); | ||
813 | sgilabel->swap_part = SGI_SSWAP16(1); | ||
814 | |||
815 | /* sizeof(sgilabel->boot_file) = 16 > 6 */ | ||
816 | memset(sgilabel->boot_file, 0, 16); | ||
817 | strcpy((char*)sgilabel->boot_file, "/unix"); | ||
818 | |||
819 | sgilabel->devparam.skew = (0); | ||
820 | sgilabel->devparam.gap1 = (0); | ||
821 | sgilabel->devparam.gap2 = (0); | ||
822 | sgilabel->devparam.sparecyl = (0); | ||
823 | sgilabel->devparam.pcylcount = SGI_SSWAP16(geometry.cylinders); | ||
824 | sgilabel->devparam.head_vol0 = SGI_SSWAP16(0); | ||
825 | sgilabel->devparam.ntrks = SGI_SSWAP16(geometry.heads); | ||
826 | /* tracks/cylinder (heads) */ | ||
827 | sgilabel->devparam.cmd_tag_queue_depth = (0); | ||
828 | sgilabel->devparam.unused0 = (0); | ||
829 | sgilabel->devparam.unused1 = SGI_SSWAP16(0); | ||
830 | sgilabel->devparam.nsect = SGI_SSWAP16(geometry.sectors); | ||
831 | /* sectors/track */ | ||
832 | sgilabel->devparam.bytes = SGI_SSWAP16(512); | ||
833 | sgilabel->devparam.ilfact = SGI_SSWAP16(1); | ||
834 | sgilabel->devparam.flags = SGI_SSWAP32(TRACK_FWD| | ||
835 | IGNORE_ERRORS|RESEEK); | ||
836 | sgilabel->devparam.datarate = SGI_SSWAP32(0); | ||
837 | sgilabel->devparam.retries_on_error = SGI_SSWAP32(1); | ||
838 | sgilabel->devparam.ms_per_word = SGI_SSWAP32(0); | ||
839 | sgilabel->devparam.xylogics_gap1 = SGI_SSWAP16(0); | ||
840 | sgilabel->devparam.xylogics_syncdelay = SGI_SSWAP16(0); | ||
841 | sgilabel->devparam.xylogics_readdelay = SGI_SSWAP16(0); | ||
842 | sgilabel->devparam.xylogics_gap2 = SGI_SSWAP16(0); | ||
843 | sgilabel->devparam.xylogics_readgate = SGI_SSWAP16(0); | ||
844 | sgilabel->devparam.xylogics_writecont = SGI_SSWAP16(0); | ||
845 | memset( &(sgilabel->directory), 0, sizeof(struct volume_directory)*15 ); | ||
846 | memset( &(sgilabel->partitions), 0, sizeof(struct sgi_partinfo)*16 ); | ||
847 | current_label_type = label_sgi; | ||
848 | partitions = 16; | ||
849 | sgi_volumes = 15; | ||
850 | sgi_set_entire(); | ||
851 | sgi_set_volhdr(); | ||
852 | for (i = 0; i < 4; i++) { | ||
853 | if(old[i].sysid) { | ||
854 | sgi_set_partition(i, old[i].start, old[i].nsect, old[i].sysid); | ||
855 | } | ||
856 | } | ||
857 | } | ||
858 | |||
859 | static void | ||
860 | sgi_set_xcyl(void) | ||
861 | { | ||
862 | /* do nothing in the beginning */ | ||
863 | } | ||
864 | #endif /* CONFIG_FEATURE_FDISK_ADVANCED */ | ||
865 | |||
866 | /* _____________________________________________________________ | ||
867 | */ | ||
868 | |||
869 | static sgiinfo * | ||
870 | fill_sgiinfo(void) | ||
871 | { | ||
872 | sgiinfo *info = calloc(1, sizeof(sgiinfo)); | ||
873 | |||
874 | info->magic = SGI_SSWAP32(SGI_INFO_MAGIC); | ||
875 | info->b1 = SGI_SSWAP32(-1); | ||
876 | info->b2 = SGI_SSWAP16(-1); | ||
877 | info->b3 = SGI_SSWAP16(1); | ||
878 | /* You may want to replace this string !!!!!!! */ | ||
879 | strcpy( (char*)info->scsi_string, "IBM OEM 0662S12 3 30" ); | ||
880 | strcpy( (char*)info->serial, "0000" ); | ||
881 | info->check1816 = SGI_SSWAP16(18*256 +16 ); | ||
882 | strcpy( (char*)info->installer, "Sfx version 5.3, Oct 18, 1994" ); | ||
883 | return info; | ||
884 | } | ||
885 | #endif /* SGI_LABEL */ | ||
diff --git a/util-linux/fdisk_sun.c b/util-linux/fdisk_sun.c new file mode 100644 index 000000000..1e8f2e525 --- /dev/null +++ b/util-linux/fdisk_sun.c | |||
@@ -0,0 +1,729 @@ | |||
1 | #ifdef CONFIG_FEATURE_SUN_LABEL | ||
2 | |||
3 | #define SUN_LABEL_MAGIC 0xDABE | ||
4 | #define SUN_LABEL_MAGIC_SWAPPED 0xBEDA | ||
5 | #define SUN_SSWAP16(x) (sun_other_endian ? __swap16(x) : (uint16_t)(x)) | ||
6 | #define SUN_SSWAP32(x) (sun_other_endian ? __swap32(x) : (uint32_t)(x)) | ||
7 | |||
8 | /* Copied from linux/major.h */ | ||
9 | #define FLOPPY_MAJOR 2 | ||
10 | |||
11 | #define SCSI_IOCTL_GET_IDLUN 0x5382 | ||
12 | |||
13 | /* | ||
14 | * fdisksunlabel.c | ||
15 | * | ||
16 | * I think this is mostly, or entirely, due to | ||
17 | * Jakub Jelinek (jj@sunsite.mff.cuni.cz), July 1996 | ||
18 | * | ||
19 | * Merged with fdisk for other architectures, aeb, June 1998. | ||
20 | * | ||
21 | * Sat Mar 20 EST 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br> | ||
22 | * Internationalization | ||
23 | */ | ||
24 | |||
25 | |||
26 | static int sun_other_endian; | ||
27 | static int scsi_disk; | ||
28 | static int floppy; | ||
29 | |||
30 | #ifndef IDE0_MAJOR | ||
31 | #define IDE0_MAJOR 3 | ||
32 | #endif | ||
33 | #ifndef IDE1_MAJOR | ||
34 | #define IDE1_MAJOR 22 | ||
35 | #endif | ||
36 | |||
37 | static void | ||
38 | guess_device_type(void) | ||
39 | { | ||
40 | struct stat bootstat; | ||
41 | |||
42 | if (fstat(fd, &bootstat) < 0) { | ||
43 | scsi_disk = 0; | ||
44 | floppy = 0; | ||
45 | } else if (S_ISBLK(bootstat.st_mode) | ||
46 | && (major(bootstat.st_rdev) == IDE0_MAJOR || | ||
47 | major(bootstat.st_rdev) == IDE1_MAJOR)) { | ||
48 | scsi_disk = 0; | ||
49 | floppy = 0; | ||
50 | } else if (S_ISBLK(bootstat.st_mode) | ||
51 | && major(bootstat.st_rdev) == FLOPPY_MAJOR) { | ||
52 | scsi_disk = 0; | ||
53 | floppy = 1; | ||
54 | } else { | ||
55 | scsi_disk = 1; | ||
56 | floppy = 0; | ||
57 | } | ||
58 | } | ||
59 | |||
60 | static const struct systypes sun_sys_types[] = { | ||
61 | { "\x00" "Empty" }, /* 0 */ | ||
62 | { "\x01" "Boot" }, /* 1 */ | ||
63 | { "\x02" "SunOS root" }, /* 2 */ | ||
64 | { "\x03" "SunOS swap" }, /* SUNOS_SWAP */ | ||
65 | { "\x04" "SunOS usr" }, /* 4 */ | ||
66 | { "\x05" "Whole disk" }, /* SUN_WHOLE_DISK */ | ||
67 | { "\x06" "SunOS stand" }, /* 6 */ | ||
68 | { "\x07" "SunOS var" }, /* 7 */ | ||
69 | { "\x08" "SunOS home" }, /* 8 */ | ||
70 | { "\x82" "Linux swap" }, /* LINUX_SWAP */ | ||
71 | { "\x83" "Linux native" }, /* LINUX_NATIVE */ | ||
72 | { "\x8e" "Linux LVM" }, /* 0x8e */ | ||
73 | /* New (2.2.x) raid partition with autodetect using persistent superblock */ | ||
74 | { "\xfd" "Linux raid autodetect" }, /* 0xfd */ | ||
75 | { NULL } | ||
76 | }; | ||
77 | |||
78 | |||
79 | static void | ||
80 | set_sun_partition(int i, uint start, uint stop, int sysid) | ||
81 | { | ||
82 | sunlabel->infos[i].id = sysid; | ||
83 | sunlabel->partitions[i].start_cylinder = | ||
84 | SUN_SSWAP32(start / (heads * sectors)); | ||
85 | sunlabel->partitions[i].num_sectors = | ||
86 | SUN_SSWAP32(stop - start); | ||
87 | set_changed(i); | ||
88 | } | ||
89 | |||
90 | static int | ||
91 | check_sun_label(void) | ||
92 | { | ||
93 | unsigned short *ush; | ||
94 | int csum; | ||
95 | |||
96 | if (sunlabel->magic != SUN_LABEL_MAGIC | ||
97 | && sunlabel->magic != SUN_LABEL_MAGIC_SWAPPED) { | ||
98 | current_label_type = label_dos; | ||
99 | sun_other_endian = 0; | ||
100 | return 0; | ||
101 | } | ||
102 | sun_other_endian = (sunlabel->magic == SUN_LABEL_MAGIC_SWAPPED); | ||
103 | ush = ((unsigned short *) (sunlabel + 1)) - 1; | ||
104 | for (csum = 0; ush >= (unsigned short *)sunlabel;) csum ^= *ush--; | ||
105 | if (csum) { | ||
106 | fprintf(stderr,_("Detected sun disklabel with wrong checksum.\n" | ||
107 | "Probably you'll have to set all the values,\n" | ||
108 | "e.g. heads, sectors, cylinders and partitions\n" | ||
109 | "or force a fresh label (s command in main menu)\n")); | ||
110 | } else { | ||
111 | heads = SUN_SSWAP16(sunlabel->ntrks); | ||
112 | cylinders = SUN_SSWAP16(sunlabel->ncyl); | ||
113 | sectors = SUN_SSWAP16(sunlabel->nsect); | ||
114 | } | ||
115 | update_units(); | ||
116 | current_label_type = label_sun; | ||
117 | partitions = 8; | ||
118 | return 1; | ||
119 | } | ||
120 | |||
121 | static const struct sun_predefined_drives { | ||
122 | const char *vendor; | ||
123 | const char *model; | ||
124 | unsigned short sparecyl; | ||
125 | unsigned short ncyl; | ||
126 | unsigned short nacyl; | ||
127 | unsigned short pcylcount; | ||
128 | unsigned short ntrks; | ||
129 | unsigned short nsect; | ||
130 | unsigned short rspeed; | ||
131 | } sun_drives[] = { | ||
132 | { "Quantum","ProDrive 80S",1,832,2,834,6,34,3662}, | ||
133 | { "Quantum","ProDrive 105S",1,974,2,1019,6,35,3662}, | ||
134 | { "CDC","Wren IV 94171-344",3,1545,2,1549,9,46,3600}, | ||
135 | { "IBM","DPES-31080",0,4901,2,4903,4,108,5400}, | ||
136 | { "IBM","DORS-32160",0,1015,2,1017,67,62,5400}, | ||
137 | { "IBM","DNES-318350",0,11199,2,11474,10,320,7200}, | ||
138 | { "SEAGATE","ST34371",0,3880,2,3882,16,135,7228}, | ||
139 | { "","SUN0104",1,974,2,1019,6,35,3662}, | ||
140 | { "","SUN0207",4,1254,2,1272,9,36,3600}, | ||
141 | { "","SUN0327",3,1545,2,1549,9,46,3600}, | ||
142 | { "","SUN0340",0,1538,2,1544,6,72,4200}, | ||
143 | { "","SUN0424",2,1151,2,2500,9,80,4400}, | ||
144 | { "","SUN0535",0,1866,2,2500,7,80,5400}, | ||
145 | { "","SUN0669",5,1614,2,1632,15,54,3600}, | ||
146 | { "","SUN1.0G",5,1703,2,1931,15,80,3597}, | ||
147 | { "","SUN1.05",0,2036,2,2038,14,72,5400}, | ||
148 | { "","SUN1.3G",6,1965,2,3500,17,80,5400}, | ||
149 | { "","SUN2.1G",0,2733,2,3500,19,80,5400}, | ||
150 | { "IOMEGA","Jaz",0,1019,2,1021,64,32,5394}, | ||
151 | }; | ||
152 | |||
153 | static const struct sun_predefined_drives * | ||
154 | sun_autoconfigure_scsi(void) | ||
155 | { | ||
156 | const struct sun_predefined_drives *p = NULL; | ||
157 | |||
158 | #ifdef SCSI_IOCTL_GET_IDLUN | ||
159 | unsigned int id[2]; | ||
160 | char buffer[2048]; | ||
161 | char buffer2[2048]; | ||
162 | FILE *pfd; | ||
163 | char *vendor; | ||
164 | char *model; | ||
165 | char *q; | ||
166 | int i; | ||
167 | |||
168 | if (ioctl(fd, SCSI_IOCTL_GET_IDLUN, &id)) | ||
169 | return NULL; | ||
170 | |||
171 | sprintf(buffer, | ||
172 | "Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n", | ||
173 | /* This is very wrong (works only if you have one HBA), | ||
174 | but I haven't found a way how to get hostno | ||
175 | from the current kernel */ | ||
176 | 0, | ||
177 | (id[0]>>16) & 0xff, | ||
178 | id[0] & 0xff, | ||
179 | (id[0]>>8) & 0xff | ||
180 | ); | ||
181 | pfd = fopen("/proc/scsi/scsi", "r"); | ||
182 | if (!pfd) { | ||
183 | return NULL; | ||
184 | } | ||
185 | while (fgets(buffer2, 2048, pfd)) { | ||
186 | if (strcmp(buffer, buffer2)) | ||
187 | continue; | ||
188 | if (!fgets(buffer2, 2048, pfd)) | ||
189 | break; | ||
190 | q = strstr(buffer2, "Vendor: "); | ||
191 | if (!q) | ||
192 | break; | ||
193 | q += 8; | ||
194 | vendor = q; | ||
195 | q = strstr(q, " "); | ||
196 | *q++ = '\0'; /* truncate vendor name */ | ||
197 | q = strstr(q, "Model: "); | ||
198 | if (!q) | ||
199 | break; | ||
200 | *q = '\0'; | ||
201 | q += 7; | ||
202 | model = q; | ||
203 | q = strstr(q, " Rev: "); | ||
204 | if (!q) | ||
205 | break; | ||
206 | *q = '\0'; | ||
207 | for (i = 0; i < SIZE(sun_drives); i++) { | ||
208 | if (*sun_drives[i].vendor && strcasecmp(sun_drives[i].vendor, vendor)) | ||
209 | continue; | ||
210 | if (!strstr(model, sun_drives[i].model)) | ||
211 | continue; | ||
212 | printf(_("Autoconfigure found a %s%s%s\n"), | ||
213 | sun_drives[i].vendor, | ||
214 | (*sun_drives[i].vendor) ? " " : "", | ||
215 | sun_drives[i].model); | ||
216 | p = sun_drives + i; | ||
217 | break; | ||
218 | } | ||
219 | break; | ||
220 | } | ||
221 | fclose(pfd); | ||
222 | #endif | ||
223 | return p; | ||
224 | } | ||
225 | |||
226 | static void | ||
227 | create_sunlabel(void) | ||
228 | { | ||
229 | struct hd_geometry geometry; | ||
230 | unsigned int ndiv; | ||
231 | int i; | ||
232 | unsigned char c; | ||
233 | const struct sun_predefined_drives *p = NULL; | ||
234 | |||
235 | fprintf(stderr, | ||
236 | _("Building a new sun disklabel. Changes will remain in memory only,\n" | ||
237 | "until you decide to write them. After that, of course, the previous\n" | ||
238 | "content won't be recoverable.\n\n")); | ||
239 | sun_other_endian = BB_LITTLE_ENDIAN; | ||
240 | memset(MBRbuffer, 0, sizeof(MBRbuffer)); | ||
241 | sunlabel->magic = SUN_SSWAP16(SUN_LABEL_MAGIC); | ||
242 | if (!floppy) { | ||
243 | puts(_("Drive type\n" | ||
244 | " ? auto configure\n" | ||
245 | " 0 custom (with hardware detected defaults)")); | ||
246 | for (i = 0; i < SIZE(sun_drives); i++) { | ||
247 | printf(" %c %s%s%s\n", | ||
248 | i + 'a', sun_drives[i].vendor, | ||
249 | (*sun_drives[i].vendor) ? " " : "", | ||
250 | sun_drives[i].model); | ||
251 | } | ||
252 | while (1) { | ||
253 | c = read_nonempty(_("Select type (? for auto, 0 for custom): ")); | ||
254 | if (c >= 'a' && c < 'a' + SIZE(sun_drives)) { | ||
255 | p = sun_drives + c - 'a'; | ||
256 | break; | ||
257 | } else if (c >= 'A' && c < 'A' + SIZE(sun_drives)) { | ||
258 | p = sun_drives + c - 'A'; | ||
259 | break; | ||
260 | } else if (c == '0') { | ||
261 | break; | ||
262 | } else if (c == '?' && scsi_disk) { | ||
263 | p = sun_autoconfigure_scsi(); | ||
264 | if (!p) | ||
265 | printf(_("Autoconfigure failed.\n")); | ||
266 | else | ||
267 | break; | ||
268 | } | ||
269 | } | ||
270 | } | ||
271 | if (!p || floppy) { | ||
272 | if (!ioctl(fd, HDIO_GETGEO, &geometry)) { | ||
273 | heads = geometry.heads; | ||
274 | sectors = geometry.sectors; | ||
275 | cylinders = geometry.cylinders; | ||
276 | } else { | ||
277 | heads = 0; | ||
278 | sectors = 0; | ||
279 | cylinders = 0; | ||
280 | } | ||
281 | if (floppy) { | ||
282 | sunlabel->nacyl = 0; | ||
283 | sunlabel->pcylcount = SUN_SSWAP16(cylinders); | ||
284 | sunlabel->rspeed = SUN_SSWAP16(300); | ||
285 | sunlabel->ilfact = SUN_SSWAP16(1); | ||
286 | sunlabel->sparecyl = 0; | ||
287 | } else { | ||
288 | heads = read_int(1,heads,1024,0,_("Heads")); | ||
289 | sectors = read_int(1,sectors,1024,0,_("Sectors/track")); | ||
290 | if (cylinders) | ||
291 | cylinders = read_int(1,cylinders-2,65535,0,_("Cylinders")); | ||
292 | else | ||
293 | cylinders = read_int(1,0,65535,0,_("Cylinders")); | ||
294 | sunlabel->nacyl = SUN_SSWAP16(read_int(0,2,65535,0, _("Alternate cylinders"))); | ||
295 | sunlabel->pcylcount = SUN_SSWAP16(read_int(0,cylinders+SUN_SSWAP16(sunlabel->nacyl), 65535,0, _("Physical cylinders"))); | ||
296 | sunlabel->rspeed = SUN_SSWAP16(read_int(1,5400,100000,0, _("Rotation speed (rpm)"))); | ||
297 | sunlabel->ilfact = SUN_SSWAP16(read_int(1,1,32,0, _("Interleave factor"))); | ||
298 | sunlabel->sparecyl = SUN_SSWAP16(read_int(0,0,sectors,0, _("Extra sectors per cylinder"))); | ||
299 | } | ||
300 | } else { | ||
301 | sunlabel->sparecyl = SUN_SSWAP16(p->sparecyl); | ||
302 | sunlabel->ncyl = SUN_SSWAP16(p->ncyl); | ||
303 | sunlabel->nacyl = SUN_SSWAP16(p->nacyl); | ||
304 | sunlabel->pcylcount = SUN_SSWAP16(p->pcylcount); | ||
305 | sunlabel->ntrks = SUN_SSWAP16(p->ntrks); | ||
306 | sunlabel->nsect = SUN_SSWAP16(p->nsect); | ||
307 | sunlabel->rspeed = SUN_SSWAP16(p->rspeed); | ||
308 | sunlabel->ilfact = SUN_SSWAP16(1); | ||
309 | cylinders = p->ncyl; | ||
310 | heads = p->ntrks; | ||
311 | sectors = p->nsect; | ||
312 | puts(_("You may change all the disk params from the x menu")); | ||
313 | } | ||
314 | |||
315 | snprintf((char *)(sunlabel->info), sizeof(sunlabel->info), | ||
316 | "%s%s%s cyl %d alt %d hd %d sec %d", | ||
317 | p ? p->vendor : "", (p && *p->vendor) ? " " : "", | ||
318 | p ? p->model : (floppy ? _("3,5\" floppy") : _("Linux custom")), | ||
319 | cylinders, SUN_SSWAP16(sunlabel->nacyl), heads, sectors); | ||
320 | |||
321 | sunlabel->ntrks = SUN_SSWAP16(heads); | ||
322 | sunlabel->nsect = SUN_SSWAP16(sectors); | ||
323 | sunlabel->ncyl = SUN_SSWAP16(cylinders); | ||
324 | if (floppy) | ||
325 | set_sun_partition(0, 0, cylinders * heads * sectors, LINUX_NATIVE); | ||
326 | else { | ||
327 | if (cylinders * heads * sectors >= 150 * 2048) { | ||
328 | ndiv = cylinders - (50 * 2048 / (heads * sectors)); /* 50M swap */ | ||
329 | } else | ||
330 | ndiv = cylinders * 2 / 3; | ||
331 | set_sun_partition(0, 0, ndiv * heads * sectors, LINUX_NATIVE); | ||
332 | set_sun_partition(1, ndiv * heads * sectors, cylinders * heads * sectors, LINUX_SWAP); | ||
333 | sunlabel->infos[1].flags |= 0x01; /* Not mountable */ | ||
334 | } | ||
335 | set_sun_partition(2, 0, cylinders * heads * sectors, SUN_WHOLE_DISK); | ||
336 | { | ||
337 | unsigned short *ush = (unsigned short *)sunlabel; | ||
338 | unsigned short csum = 0; | ||
339 | while (ush < (unsigned short *)(&sunlabel->csum)) | ||
340 | csum ^= *ush++; | ||
341 | sunlabel->csum = csum; | ||
342 | } | ||
343 | |||
344 | set_all_unchanged(); | ||
345 | set_changed(0); | ||
346 | get_boot(create_empty_sun); | ||
347 | } | ||
348 | |||
349 | static void | ||
350 | toggle_sunflags(int i, unsigned char mask) | ||
351 | { | ||
352 | if (sunlabel->infos[i].flags & mask) | ||
353 | sunlabel->infos[i].flags &= ~mask; | ||
354 | else | ||
355 | sunlabel->infos[i].flags |= mask; | ||
356 | set_changed(i); | ||
357 | } | ||
358 | |||
359 | static void | ||
360 | fetch_sun(uint *starts, uint *lens, uint *start, uint *stop) | ||
361 | { | ||
362 | int i, continuous = 1; | ||
363 | |||
364 | *start = 0; | ||
365 | *stop = cylinders * heads * sectors; | ||
366 | for (i = 0; i < partitions; i++) { | ||
367 | if (sunlabel->partitions[i].num_sectors | ||
368 | && sunlabel->infos[i].id | ||
369 | && sunlabel->infos[i].id != SUN_WHOLE_DISK) { | ||
370 | starts[i] = SUN_SSWAP32(sunlabel->partitions[i].start_cylinder) * heads * sectors; | ||
371 | lens[i] = SUN_SSWAP32(sunlabel->partitions[i].num_sectors); | ||
372 | if (continuous) { | ||
373 | if (starts[i] == *start) | ||
374 | *start += lens[i]; | ||
375 | else if (starts[i] + lens[i] >= *stop) | ||
376 | *stop = starts[i]; | ||
377 | else | ||
378 | continuous = 0; | ||
379 | /* There will be probably more gaps | ||
380 | than one, so lets check afterwards */ | ||
381 | } | ||
382 | } else { | ||
383 | starts[i] = 0; | ||
384 | lens[i] = 0; | ||
385 | } | ||
386 | } | ||
387 | } | ||
388 | |||
389 | static uint *verify_sun_starts; | ||
390 | |||
391 | static int | ||
392 | verify_sun_cmp(int *a, int *b) | ||
393 | { | ||
394 | if (*a == -1) return 1; | ||
395 | if (*b == -1) return -1; | ||
396 | if (verify_sun_starts[*a] > verify_sun_starts[*b]) return 1; | ||
397 | return -1; | ||
398 | } | ||
399 | |||
400 | static void | ||
401 | verify_sun(void) | ||
402 | { | ||
403 | uint starts[8], lens[8], start, stop; | ||
404 | int i,j,k,starto,endo; | ||
405 | int array[8]; | ||
406 | |||
407 | verify_sun_starts = starts; | ||
408 | fetch_sun(starts,lens,&start,&stop); | ||
409 | for (k = 0; k < 7; k++) { | ||
410 | for (i = 0; i < 8; i++) { | ||
411 | if (k && (lens[i] % (heads * sectors))) { | ||
412 | printf(_("Partition %d doesn't end on cylinder boundary\n"), i+1); | ||
413 | } | ||
414 | if (lens[i]) { | ||
415 | for (j = 0; j < i; j++) | ||
416 | if (lens[j]) { | ||
417 | if (starts[j] == starts[i]+lens[i]) { | ||
418 | starts[j] = starts[i]; lens[j] += lens[i]; | ||
419 | lens[i] = 0; | ||
420 | } else if (starts[i] == starts[j]+lens[j]){ | ||
421 | lens[j] += lens[i]; | ||
422 | lens[i] = 0; | ||
423 | } else if (!k) { | ||
424 | if (starts[i] < starts[j]+lens[j] | ||
425 | && starts[j] < starts[i]+lens[i]) { | ||
426 | starto = starts[i]; | ||
427 | if (starts[j] > starto) | ||
428 | starto = starts[j]; | ||
429 | endo = starts[i]+lens[i]; | ||
430 | if (starts[j]+lens[j] < endo) | ||
431 | endo = starts[j]+lens[j]; | ||
432 | printf(_("Partition %d overlaps with others in " | ||
433 | "sectors %d-%d\n"), i+1, starto, endo); | ||
434 | } | ||
435 | } | ||
436 | } | ||
437 | } | ||
438 | } | ||
439 | } | ||
440 | for (i = 0; i < 8; i++) { | ||
441 | if (lens[i]) | ||
442 | array[i] = i; | ||
443 | else | ||
444 | array[i] = -1; | ||
445 | } | ||
446 | qsort(array,SIZE(array),sizeof(array[0]), | ||
447 | (int (*)(const void *,const void *)) verify_sun_cmp); | ||
448 | if (array[0] == -1) { | ||
449 | printf(_("No partitions defined\n")); | ||
450 | return; | ||
451 | } | ||
452 | stop = cylinders * heads * sectors; | ||
453 | if (starts[array[0]]) | ||
454 | printf(_("Unused gap - sectors 0-%d\n"),starts[array[0]]); | ||
455 | for (i = 0; i < 7 && array[i+1] != -1; i++) { | ||
456 | printf(_("Unused gap - sectors %d-%d\n"),starts[array[i]]+lens[array[i]],starts[array[i+1]]); | ||
457 | } | ||
458 | start = starts[array[i]] + lens[array[i]]; | ||
459 | if (start < stop) | ||
460 | printf(_("Unused gap - sectors %d-%d\n"),start,stop); | ||
461 | } | ||
462 | |||
463 | static void | ||
464 | add_sun_partition(int n, int sys) | ||
465 | { | ||
466 | uint start, stop, stop2; | ||
467 | uint starts[8], lens[8]; | ||
468 | int whole_disk = 0; | ||
469 | |||
470 | char mesg[256]; | ||
471 | int i, first, last; | ||
472 | |||
473 | if (sunlabel->partitions[n].num_sectors && sunlabel->infos[n].id) { | ||
474 | printf(_("Partition %d is already defined. Delete " | ||
475 | "it before re-adding it.\n"), n + 1); | ||
476 | return; | ||
477 | } | ||
478 | |||
479 | fetch_sun(starts,lens,&start,&stop); | ||
480 | if (stop <= start) { | ||
481 | if (n == 2) | ||
482 | whole_disk = 1; | ||
483 | else { | ||
484 | printf(_("Other partitions already cover the whole disk.\nDelete " | ||
485 | "some/shrink them before retry.\n")); | ||
486 | return; | ||
487 | } | ||
488 | } | ||
489 | snprintf(mesg, sizeof(mesg), _("First %s"), str_units(SINGULAR)); | ||
490 | while (1) { | ||
491 | if (whole_disk) | ||
492 | first = read_int(0, 0, 0, 0, mesg); | ||
493 | else | ||
494 | first = read_int(scround(start), scround(stop)+1, | ||
495 | scround(stop), 0, mesg); | ||
496 | if (display_in_cyl_units) | ||
497 | first *= units_per_sector; | ||
498 | else | ||
499 | /* Starting sector has to be properly aligned */ | ||
500 | first = (first + heads * sectors - 1) / (heads * sectors); | ||
501 | if (n == 2 && first != 0) | ||
502 | printf("\ | ||
503 | It is highly recommended that the third partition covers the whole disk\n\ | ||
504 | and is of type `Whole disk'\n"); | ||
505 | /* ewt asks to add: "don't start a partition at cyl 0" | ||
506 | However, edmundo@rano.demon.co.uk writes: | ||
507 | "In addition to having a Sun partition table, to be able to | ||
508 | boot from the disc, the first partition, /dev/sdX1, must | ||
509 | start at cylinder 0. This means that /dev/sdX1 contains | ||
510 | the partition table and the boot block, as these are the | ||
511 | first two sectors of the disc. Therefore you must be | ||
512 | careful what you use /dev/sdX1 for. In particular, you must | ||
513 | not use a partition starting at cylinder 0 for Linux swap, | ||
514 | as that would overwrite the partition table and the boot | ||
515 | block. You may, however, use such a partition for a UFS | ||
516 | or EXT2 file system, as these file systems leave the first | ||
517 | 1024 bytes undisturbed. */ | ||
518 | /* On the other hand, one should not use partitions | ||
519 | starting at block 0 in an md, or the label will | ||
520 | be trashed. */ | ||
521 | for (i = 0; i < partitions; i++) | ||
522 | if (lens[i] && starts[i] <= first && starts[i] + lens[i] > first) | ||
523 | break; | ||
524 | if (i < partitions && !whole_disk) { | ||
525 | if (n == 2 && !first) { | ||
526 | whole_disk = 1; | ||
527 | break; | ||
528 | } | ||
529 | printf(_("Sector %d is already allocated\n"), first); | ||
530 | } else | ||
531 | break; | ||
532 | } | ||
533 | stop = cylinders * heads * sectors; | ||
534 | stop2 = stop; | ||
535 | for (i = 0; i < partitions; i++) { | ||
536 | if (starts[i] > first && starts[i] < stop) | ||
537 | stop = starts[i]; | ||
538 | } | ||
539 | snprintf(mesg, sizeof(mesg), | ||
540 | _("Last %s or +size or +sizeM or +sizeK"), | ||
541 | str_units(SINGULAR)); | ||
542 | if (whole_disk) | ||
543 | last = read_int(scround(stop2), scround(stop2), scround(stop2), | ||
544 | 0, mesg); | ||
545 | else if (n == 2 && !first) | ||
546 | last = read_int(scround(first), scround(stop2), scround(stop2), | ||
547 | scround(first), mesg); | ||
548 | else | ||
549 | last = read_int(scround(first), scround(stop), scround(stop), | ||
550 | scround(first), mesg); | ||
551 | if (display_in_cyl_units) | ||
552 | last *= units_per_sector; | ||
553 | if (n == 2 && !first) { | ||
554 | if (last >= stop2) { | ||
555 | whole_disk = 1; | ||
556 | last = stop2; | ||
557 | } else if (last > stop) { | ||
558 | printf(_("You haven't covered the whole disk with " | ||
559 | "the 3rd partition, but your value\n" | ||
560 | "%d %s covers some other partition. " | ||
561 | "Your entry has been changed\n" | ||
562 | "to %d %s\n"), | ||
563 | scround(last), str_units(SINGULAR), | ||
564 | scround(stop), str_units(SINGULAR)); | ||
565 | last = stop; | ||
566 | } | ||
567 | } else if (!whole_disk && last > stop) | ||
568 | last = stop; | ||
569 | |||
570 | if (whole_disk) | ||
571 | sys = SUN_WHOLE_DISK; | ||
572 | set_sun_partition(n, first, last, sys); | ||
573 | } | ||
574 | |||
575 | static void | ||
576 | sun_delete_partition(int i) | ||
577 | { | ||
578 | unsigned int nsec; | ||
579 | |||
580 | if (i == 2 | ||
581 | && sunlabel->infos[i].id == SUN_WHOLE_DISK | ||
582 | && !sunlabel->partitions[i].start_cylinder | ||
583 | && (nsec = SUN_SSWAP32(sunlabel->partitions[i].num_sectors)) == heads * sectors * cylinders) | ||
584 | printf(_("If you want to maintain SunOS/Solaris compatibility, " | ||
585 | "consider leaving this\n" | ||
586 | "partition as Whole disk (5), starting at 0, with %u " | ||
587 | "sectors\n"), nsec); | ||
588 | sunlabel->infos[i].id = 0; | ||
589 | sunlabel->partitions[i].num_sectors = 0; | ||
590 | } | ||
591 | |||
592 | static void | ||
593 | sun_change_sysid(int i, int sys) | ||
594 | { | ||
595 | if (sys == LINUX_SWAP && !sunlabel->partitions[i].start_cylinder) { | ||
596 | read_maybe_empty( | ||
597 | _("It is highly recommended that the partition at offset 0\n" | ||
598 | "is UFS, EXT2FS filesystem or SunOS swap. Putting Linux swap\n" | ||
599 | "there may destroy your partition table and bootblock.\n" | ||
600 | "Type YES if you're very sure you would like that partition\n" | ||
601 | "tagged with 82 (Linux swap): ")); | ||
602 | if (strcmp (line_ptr, _("YES\n"))) | ||
603 | return; | ||
604 | } | ||
605 | switch (sys) { | ||
606 | case SUNOS_SWAP: | ||
607 | case LINUX_SWAP: | ||
608 | /* swaps are not mountable by default */ | ||
609 | sunlabel->infos[i].flags |= 0x01; | ||
610 | break; | ||
611 | default: | ||
612 | /* assume other types are mountable; | ||
613 | user can change it anyway */ | ||
614 | sunlabel->infos[i].flags &= ~0x01; | ||
615 | break; | ||
616 | } | ||
617 | sunlabel->infos[i].id = sys; | ||
618 | } | ||
619 | |||
620 | static void | ||
621 | sun_list_table(int xtra) | ||
622 | { | ||
623 | int i, w; | ||
624 | |||
625 | w = strlen(disk_device); | ||
626 | if (xtra) | ||
627 | printf( | ||
628 | _("\nDisk %s (Sun disk label): %d heads, %d sectors, %d rpm\n" | ||
629 | "%d cylinders, %d alternate cylinders, %d physical cylinders\n" | ||
630 | "%d extra sects/cyl, interleave %d:1\n" | ||
631 | "%s\n" | ||
632 | "Units = %s of %d * 512 bytes\n\n"), | ||
633 | disk_device, heads, sectors, SUN_SSWAP16(sunlabel->rspeed), | ||
634 | cylinders, SUN_SSWAP16(sunlabel->nacyl), | ||
635 | SUN_SSWAP16(sunlabel->pcylcount), | ||
636 | SUN_SSWAP16(sunlabel->sparecyl), | ||
637 | SUN_SSWAP16(sunlabel->ilfact), | ||
638 | (char *)sunlabel, | ||
639 | str_units(PLURAL), units_per_sector); | ||
640 | else | ||
641 | printf( | ||
642 | _("\nDisk %s (Sun disk label): %d heads, %d sectors, %d cylinders\n" | ||
643 | "Units = %s of %d * 512 bytes\n\n"), | ||
644 | disk_device, heads, sectors, cylinders, | ||
645 | str_units(PLURAL), units_per_sector); | ||
646 | |||
647 | printf(_("%*s Flag Start End Blocks Id System\n"), | ||
648 | w + 1, _("Device")); | ||
649 | for (i = 0 ; i < partitions; i++) { | ||
650 | if (sunlabel->partitions[i].num_sectors) { | ||
651 | uint32_t start = SUN_SSWAP32(sunlabel->partitions[i].start_cylinder) * heads * sectors; | ||
652 | uint32_t len = SUN_SSWAP32(sunlabel->partitions[i].num_sectors); | ||
653 | printf("%s %c%c %9ld %9ld %9ld%c %2x %s\n", | ||
654 | partname(disk_device, i+1, w), /* device */ | ||
655 | (sunlabel->infos[i].flags & 0x01) ? 'u' : ' ', /* flags */ | ||
656 | (sunlabel->infos[i].flags & 0x10) ? 'r' : ' ', | ||
657 | (long) scround(start), /* start */ | ||
658 | (long) scround(start+len), /* end */ | ||
659 | (long) len / 2, len & 1 ? '+' : ' ', /* odd flag on end */ | ||
660 | sunlabel->infos[i].id, /* type id */ | ||
661 | partition_type(sunlabel->infos[i].id)); /* type name */ | ||
662 | } | ||
663 | } | ||
664 | } | ||
665 | |||
666 | #ifdef CONFIG_FEATURE_FDISK_ADVANCED | ||
667 | |||
668 | static void | ||
669 | sun_set_alt_cyl(void) | ||
670 | { | ||
671 | sunlabel->nacyl = | ||
672 | SUN_SSWAP16(read_int(0,SUN_SSWAP16(sunlabel->nacyl), 65535, 0, | ||
673 | _("Number of alternate cylinders"))); | ||
674 | } | ||
675 | |||
676 | static void | ||
677 | sun_set_ncyl(int cyl) | ||
678 | { | ||
679 | sunlabel->ncyl = SUN_SSWAP16(cyl); | ||
680 | } | ||
681 | |||
682 | static void | ||
683 | sun_set_xcyl(void) | ||
684 | { | ||
685 | sunlabel->sparecyl = | ||
686 | SUN_SSWAP16(read_int(0, SUN_SSWAP16(sunlabel->sparecyl), sectors, 0, | ||
687 | _("Extra sectors per cylinder"))); | ||
688 | } | ||
689 | |||
690 | static void | ||
691 | sun_set_ilfact(void) | ||
692 | { | ||
693 | sunlabel->ilfact = | ||
694 | SUN_SSWAP16(read_int(1, SUN_SSWAP16(sunlabel->ilfact), 32, 0, | ||
695 | _("Interleave factor"))); | ||
696 | } | ||
697 | |||
698 | static void | ||
699 | sun_set_rspeed(void) | ||
700 | { | ||
701 | sunlabel->rspeed = | ||
702 | SUN_SSWAP16(read_int(1, SUN_SSWAP16(sunlabel->rspeed), 100000, 0, | ||
703 | _("Rotation speed (rpm)"))); | ||
704 | } | ||
705 | |||
706 | static void | ||
707 | sun_set_pcylcount(void) | ||
708 | { | ||
709 | sunlabel->pcylcount = | ||
710 | SUN_SSWAP16(read_int(0, SUN_SSWAP16(sunlabel->pcylcount), 65535, 0, | ||
711 | _("Number of physical cylinders"))); | ||
712 | } | ||
713 | #endif /* CONFIG_FEATURE_FDISK_ADVANCED */ | ||
714 | |||
715 | static void | ||
716 | sun_write_table(void) | ||
717 | { | ||
718 | unsigned short *ush = (unsigned short *)sunlabel; | ||
719 | unsigned short csum = 0; | ||
720 | |||
721 | while (ush < (unsigned short *)(&sunlabel->csum)) | ||
722 | csum ^= *ush++; | ||
723 | sunlabel->csum = csum; | ||
724 | if (lseek(fd, 0, SEEK_SET) < 0) | ||
725 | fdisk_fatal(unable_to_seek); | ||
726 | if (write(fd, sunlabel, SECTOR_SIZE) != SECTOR_SIZE) | ||
727 | fdisk_fatal(unable_to_write); | ||
728 | } | ||
729 | #endif /* SUN_LABEL */ | ||
diff --git a/util-linux/freeramdisk.c b/util-linux/freeramdisk.c new file mode 100644 index 000000000..2293d3ee6 --- /dev/null +++ b/util-linux/freeramdisk.c | |||
@@ -0,0 +1,34 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * freeramdisk and fdflush implementations for busybox | ||
4 | * | ||
5 | * Copyright (C) 2000 and written by Emanuele Caratti <wiz@iol.it> | ||
6 | * Adjusted a bit by Erik Andersen <andersen@codepoet.org> | ||
7 | * Unified with fdflush by Tito Ragusa <farmatito@tiscali.it> | ||
8 | * | ||
9 | * Licensed under GPLv2, see file LICENSE in this tarball for details. | ||
10 | */ | ||
11 | |||
12 | #include "busybox.h" | ||
13 | |||
14 | /* From <linux/fd.h> */ | ||
15 | #define FDFLUSH _IO(2,0x4b) | ||
16 | |||
17 | int freeramdisk_main(int argc, char **argv) | ||
18 | { | ||
19 | int result; | ||
20 | int fd; | ||
21 | |||
22 | if (argc != 2) bb_show_usage(); | ||
23 | |||
24 | fd = xopen(argv[1], O_RDWR); | ||
25 | |||
26 | // Act like freeramdisk, fdflush, or both depending on configuration. | ||
27 | result = ioctl(fd, (ENABLE_FREERAMDISK && applet_name[1]=='r') | ||
28 | || !ENABLE_FDFLUSH ? BLKFLSBUF : FDFLUSH); | ||
29 | |||
30 | if (ENABLE_FEATURE_CLEAN_UP) close(fd); | ||
31 | |||
32 | if (result) bb_perror_msg_and_die("%s", argv[1]); | ||
33 | return EXIT_SUCCESS; | ||
34 | } | ||
diff --git a/util-linux/fsck_minix.c b/util-linux/fsck_minix.c new file mode 100644 index 000000000..9c831bd59 --- /dev/null +++ b/util-linux/fsck_minix.c | |||
@@ -0,0 +1,1417 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * fsck.c - a file system consistency checker for Linux. | ||
4 | * | ||
5 | * (C) 1991, 1992 Linus Torvalds. | ||
6 | * | ||
7 | * Licensed under GPLv2, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | /* | ||
11 | * 09.11.91 - made the first rudimentary functions | ||
12 | * | ||
13 | * 10.11.91 - updated, does checking, no repairs yet. | ||
14 | * Sent out to the mailing-list for testing. | ||
15 | * | ||
16 | * 14.11.91 - Testing seems to have gone well. Added some | ||
17 | * correction-code, and changed some functions. | ||
18 | * | ||
19 | * 15.11.91 - More correction code. Hopefully it notices most | ||
20 | * cases now, and tries to do something about them. | ||
21 | * | ||
22 | * 16.11.91 - More corrections (thanks to Mika Jalava). Most | ||
23 | * things seem to work now. Yeah, sure. | ||
24 | * | ||
25 | * | ||
26 | * 19.04.92 - Had to start over again from this old version, as a | ||
27 | * kernel bug ate my enhanced fsck in february. | ||
28 | * | ||
29 | * 28.02.93 - added support for different directory entry sizes.. | ||
30 | * | ||
31 | * Sat Mar 6 18:59:42 1993, faith@cs.unc.edu: Output namelen with | ||
32 | * super-block information | ||
33 | * | ||
34 | * Sat Oct 9 11:17:11 1993, faith@cs.unc.edu: make exit status conform | ||
35 | * to that required by fsutil | ||
36 | * | ||
37 | * Mon Jan 3 11:06:52 1994 - Dr. Wettstein (greg%wind.uucp@plains.nodak.edu) | ||
38 | * Added support for file system valid flag. Also | ||
39 | * added program_version variable and output of | ||
40 | * program name and version number when program | ||
41 | * is executed. | ||
42 | * | ||
43 | * 30.10.94 - added support for v2 filesystem | ||
44 | * (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de) | ||
45 | * | ||
46 | * 10.12.94 - added test to prevent checking of mounted fs adapted | ||
47 | * from Theodore Ts'o's (tytso@athena.mit.edu) e2fsck | ||
48 | * program. (Daniel Quinlan, quinlan@yggdrasil.com) | ||
49 | * | ||
50 | * 01.07.96 - Fixed the v2 fs stuff to use the right #defines and such | ||
51 | * for modern libcs (janl@math.uio.no, Nicolai Langfeldt) | ||
52 | * | ||
53 | * 02.07.96 - Added C bit fiddling routines from rmk@ecs.soton.ac.uk | ||
54 | * (Russell King). He made them for ARM. It would seem | ||
55 | * that the ARM is powerful enough to do this in C whereas | ||
56 | * i386 and m64k must use assembly to get it fast >:-) | ||
57 | * This should make minix fsck system-independent. | ||
58 | * (janl@math.uio.no, Nicolai Langfeldt) | ||
59 | * | ||
60 | * 04.11.96 - Added minor fixes from Andreas Schwab to avoid compiler | ||
61 | * warnings. Added mc68k bitops from | ||
62 | * Joerg Dorchain <dorchain@mpi-sb.mpg.de>. | ||
63 | * | ||
64 | * 06.11.96 - Added v2 code submitted by Joerg Dorchain, but written by | ||
65 | * Andreas Schwab. | ||
66 | * | ||
67 | * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org> | ||
68 | * - added Native Language Support | ||
69 | * | ||
70 | * | ||
71 | * I've had no time to add comments - hopefully the function names | ||
72 | * are comments enough. As with all file system checkers, this assumes | ||
73 | * the file system is quiescent - don't use it on a mounted device | ||
74 | * unless you can be sure nobody is writing to it (and remember that the | ||
75 | * kernel can write to it when it searches for files). | ||
76 | * | ||
77 | * Usage: fsck [-larvsm] device | ||
78 | * -l for a listing of all the filenames | ||
79 | * -a for automatic repairs (not implemented) | ||
80 | * -r for repairs (interactive) (not implemented) | ||
81 | * -v for verbose (tells how many files) | ||
82 | * -s for super-block info | ||
83 | * -m for minix-like "mode not cleared" warnings | ||
84 | * -f force filesystem check even if filesystem marked as valid | ||
85 | * | ||
86 | * The device may be a block device or a image of one, but this isn't | ||
87 | * enforced (but it's not much fun on a character device :-). | ||
88 | */ | ||
89 | |||
90 | #include "busybox.h" | ||
91 | #include <mntent.h> | ||
92 | |||
93 | /* | ||
94 | * This is the original minix inode layout on disk. | ||
95 | * Note the 8-bit gid and atime and ctime. | ||
96 | */ | ||
97 | struct minix_inode { | ||
98 | uint16_t i_mode; | ||
99 | uint16_t i_uid; | ||
100 | uint32_t i_size; | ||
101 | uint32_t i_time; | ||
102 | uint8_t i_gid; | ||
103 | uint8_t i_nlinks; | ||
104 | uint16_t i_zone[9]; | ||
105 | }; | ||
106 | |||
107 | /* | ||
108 | * The new minix inode has all the time entries, as well as | ||
109 | * long block numbers and a third indirect block (7+1+1+1 | ||
110 | * instead of 7+1+1). Also, some previously 8-bit values are | ||
111 | * now 16-bit. The inode is now 64 bytes instead of 32. | ||
112 | */ | ||
113 | struct minix2_inode { | ||
114 | uint16_t i_mode; | ||
115 | uint16_t i_nlinks; | ||
116 | uint16_t i_uid; | ||
117 | uint16_t i_gid; | ||
118 | uint32_t i_size; | ||
119 | uint32_t i_atime; | ||
120 | uint32_t i_mtime; | ||
121 | uint32_t i_ctime; | ||
122 | uint32_t i_zone[10]; | ||
123 | }; | ||
124 | |||
125 | enum { | ||
126 | MINIX_ROOT_INO = 1, | ||
127 | MINIX_LINK_MAX = 250, | ||
128 | MINIX2_LINK_MAX = 65530, | ||
129 | |||
130 | MINIX_I_MAP_SLOTS = 8, | ||
131 | MINIX_Z_MAP_SLOTS = 64, | ||
132 | MINIX_SUPER_MAGIC = 0x137F, /* original minix fs */ | ||
133 | MINIX_SUPER_MAGIC2 = 0x138F, /* minix fs, 30 char names */ | ||
134 | MINIX2_SUPER_MAGIC = 0x2468, /* minix V2 fs */ | ||
135 | MINIX2_SUPER_MAGIC2 = 0x2478, /* minix V2 fs, 30 char names */ | ||
136 | MINIX_VALID_FS = 0x0001, /* Clean fs. */ | ||
137 | MINIX_ERROR_FS = 0x0002, /* fs has errors. */ | ||
138 | |||
139 | MINIX_INODES_PER_BLOCK = ((BLOCK_SIZE)/(sizeof (struct minix_inode))), | ||
140 | MINIX2_INODES_PER_BLOCK = ((BLOCK_SIZE)/(sizeof (struct minix2_inode))), | ||
141 | |||
142 | MINIX_V1 = 0x0001, /* original minix fs */ | ||
143 | MINIX_V2 = 0x0002 /* minix V2 fs */ | ||
144 | }; | ||
145 | |||
146 | #define INODE_VERSION(inode) inode->i_sb->u.minix_sb.s_version | ||
147 | |||
148 | /* | ||
149 | * minix super-block data on disk | ||
150 | */ | ||
151 | struct minix_super_block { | ||
152 | uint16_t s_ninodes; | ||
153 | uint16_t s_nzones; | ||
154 | uint16_t s_imap_blocks; | ||
155 | uint16_t s_zmap_blocks; | ||
156 | uint16_t s_firstdatazone; | ||
157 | uint16_t s_log_zone_size; | ||
158 | uint32_t s_max_size; | ||
159 | uint16_t s_magic; | ||
160 | uint16_t s_state; | ||
161 | uint32_t s_zones; | ||
162 | }; | ||
163 | |||
164 | struct minix_dir_entry { | ||
165 | uint16_t inode; | ||
166 | char name[0]; | ||
167 | }; | ||
168 | |||
169 | |||
170 | #define NAME_MAX 255 /* # chars in a file name */ | ||
171 | |||
172 | #define MINIX_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix_inode))) | ||
173 | |||
174 | #ifndef BLKGETSIZE | ||
175 | #define BLKGETSIZE _IO(0x12,96) /* return device size */ | ||
176 | #endif | ||
177 | |||
178 | #ifndef __linux__ | ||
179 | #define volatile | ||
180 | #endif | ||
181 | |||
182 | enum { ROOT_INO = 1 }; | ||
183 | |||
184 | #define UPPER(size,n) ((size+((n)-1))/(n)) | ||
185 | #define INODE_SIZE (sizeof(struct minix_inode)) | ||
186 | #ifdef CONFIG_FEATURE_MINIX2 | ||
187 | #define INODE_SIZE2 (sizeof(struct minix2_inode)) | ||
188 | #define INODE_BLOCKS UPPER(INODES, (version2 ? MINIX2_INODES_PER_BLOCK \ | ||
189 | : MINIX_INODES_PER_BLOCK)) | ||
190 | #else | ||
191 | #define INODE_BLOCKS UPPER(INODES, (MINIX_INODES_PER_BLOCK)) | ||
192 | #endif | ||
193 | #define INODE_BUFFER_SIZE (INODE_BLOCKS * BLOCK_SIZE) | ||
194 | |||
195 | #define BITS_PER_BLOCK (BLOCK_SIZE<<3) | ||
196 | |||
197 | static char *program_version = "1.2 - 11/11/96"; | ||
198 | static char *device_name; | ||
199 | static int IN; | ||
200 | static int repair, automatic, verbose, list, show, warn_mode, force; | ||
201 | static int directory, regular, blockdev, chardev, links, symlinks, total; | ||
202 | |||
203 | static int changed; /* flags if the filesystem has been changed */ | ||
204 | static int errors_uncorrected; /* flag if some error was not corrected */ | ||
205 | static int dirsize = 16; | ||
206 | static int namelen = 14; | ||
207 | static struct termios termios; | ||
208 | static int termios_set; | ||
209 | |||
210 | static char *inode_buffer; | ||
211 | #define Inode (((struct minix_inode *) inode_buffer)-1) | ||
212 | #define Inode2 (((struct minix2_inode *) inode_buffer)-1) | ||
213 | static char super_block_buffer[BLOCK_SIZE]; | ||
214 | |||
215 | #define Super (*(struct minix_super_block *)super_block_buffer) | ||
216 | #define INODES ((unsigned long)Super.s_ninodes) | ||
217 | #ifdef CONFIG_FEATURE_MINIX2 | ||
218 | static int version2; | ||
219 | #define ZONES ((unsigned long)(version2 ? Super.s_zones : Super.s_nzones)) | ||
220 | #else | ||
221 | #define ZONES ((unsigned long)(Super.s_nzones)) | ||
222 | #endif | ||
223 | #define IMAPS ((unsigned long)Super.s_imap_blocks) | ||
224 | #define ZMAPS ((unsigned long)Super.s_zmap_blocks) | ||
225 | #define FIRSTZONE ((unsigned long)Super.s_firstdatazone) | ||
226 | #define ZONESIZE ((unsigned long)Super.s_log_zone_size) | ||
227 | #define MAXSIZE ((unsigned long)Super.s_max_size) | ||
228 | #define MAGIC (Super.s_magic) | ||
229 | #define NORM_FIRSTZONE (2+IMAPS+ZMAPS+INODE_BLOCKS) | ||
230 | |||
231 | static char *inode_map; | ||
232 | static char *zone_map; | ||
233 | |||
234 | static unsigned char *inode_count; | ||
235 | static unsigned char *zone_count; | ||
236 | |||
237 | static void recursive_check(unsigned int ino); | ||
238 | #ifdef CONFIG_FEATURE_MINIX2 | ||
239 | static void recursive_check2(unsigned int ino); | ||
240 | #endif | ||
241 | |||
242 | static int bit(char *a, unsigned int i) | ||
243 | { | ||
244 | return (a[i >> 3] & (1<<(i & 7))) != 0; | ||
245 | } | ||
246 | #define inode_in_use(x) (bit(inode_map,(x))) | ||
247 | #define zone_in_use(x) (bit(zone_map,(x)-FIRSTZONE+1)) | ||
248 | |||
249 | #define mark_inode(x) (setbit(inode_map,(x)),changed=1) | ||
250 | #define unmark_inode(x) (clrbit(inode_map,(x)),changed=1) | ||
251 | |||
252 | #define mark_zone(x) (setbit(zone_map,(x)-FIRSTZONE+1),changed=1) | ||
253 | #define unmark_zone(x) (clrbit(zone_map,(x)-FIRSTZONE+1),changed=1) | ||
254 | |||
255 | static void leave(int) ATTRIBUTE_NORETURN; | ||
256 | static void leave(int status) | ||
257 | { | ||
258 | if (termios_set) | ||
259 | tcsetattr(0, TCSANOW, &termios); | ||
260 | exit(status); | ||
261 | } | ||
262 | |||
263 | static void die(const char *str) | ||
264 | { | ||
265 | bb_error_msg("%s", str); | ||
266 | leave(8); | ||
267 | } | ||
268 | |||
269 | /* File-name data */ | ||
270 | enum { MAX_DEPTH = 32 }; | ||
271 | static int name_depth; | ||
272 | static char *current_name; | ||
273 | static char *name_component[MAX_DEPTH+1]; | ||
274 | |||
275 | /* Wed Feb 9 15:17:06 MST 2000 */ | ||
276 | /* dynamically allocate name_list (instead of making it static) */ | ||
277 | static void alloc_current_name(void) | ||
278 | { | ||
279 | current_name = xmalloc(MAX_DEPTH * (BUFSIZ + 1)); | ||
280 | current_name[0] = '/'; | ||
281 | current_name[1] = '\0'; | ||
282 | name_component[0] = ¤t_name[0]; | ||
283 | } | ||
284 | |||
285 | #ifdef CONFIG_FEATURE_CLEAN_UP | ||
286 | /* execute this atexit() to deallocate name_list[] */ | ||
287 | /* piptigger was here */ | ||
288 | static void free_current_name(void) | ||
289 | { | ||
290 | free(current_name); | ||
291 | } | ||
292 | #endif | ||
293 | |||
294 | static void push_filename(const char *name) | ||
295 | { | ||
296 | // /dir/dir/dir/file | ||
297 | // ^ ^ ^ | ||
298 | // [0] [1] [2] <-name_component[i] | ||
299 | if (name_depth < MAX_DEPTH) { | ||
300 | int len; | ||
301 | char *p = name_component[name_depth]; | ||
302 | *p++ = '/'; | ||
303 | len = sprintf(p, "%.*s", namelen, name); | ||
304 | name_component[name_depth + 1] = p + len; | ||
305 | } | ||
306 | name_depth++; | ||
307 | } | ||
308 | |||
309 | static void pop_filename(void) { | ||
310 | name_depth--; | ||
311 | if (name_depth < MAX_DEPTH) { | ||
312 | *name_component[name_depth] = '\0'; | ||
313 | if (!name_depth) { | ||
314 | current_name[0] = '/'; | ||
315 | current_name[1] = '\0'; | ||
316 | } | ||
317 | } | ||
318 | } | ||
319 | |||
320 | static int ask(const char *string, int def) | ||
321 | { | ||
322 | int c; | ||
323 | |||
324 | if (!repair) { | ||
325 | puts(""); | ||
326 | errors_uncorrected = 1; | ||
327 | return 0; | ||
328 | } | ||
329 | if (automatic) { | ||
330 | puts(""); | ||
331 | if (!def) | ||
332 | errors_uncorrected = 1; | ||
333 | return def; | ||
334 | } | ||
335 | printf(def ? "%s (y/n)? " : "%s (n/y)? ", string); | ||
336 | for (;;) { | ||
337 | fflush(stdout); | ||
338 | if ((c = getchar()) == EOF) { | ||
339 | if (!def) | ||
340 | errors_uncorrected = 1; | ||
341 | return def; | ||
342 | } | ||
343 | c = toupper(c); | ||
344 | if (c == 'Y') { | ||
345 | def = 1; | ||
346 | break; | ||
347 | } else if (c == 'N') { | ||
348 | def = 0; | ||
349 | break; | ||
350 | } else if (c == ' ' || c == '\n') | ||
351 | break; | ||
352 | } | ||
353 | if (def) | ||
354 | printf("y\n"); | ||
355 | else { | ||
356 | printf("n\n"); | ||
357 | errors_uncorrected = 1; | ||
358 | } | ||
359 | return def; | ||
360 | } | ||
361 | |||
362 | /* | ||
363 | * Make certain that we aren't checking a filesystem that is on a | ||
364 | * mounted partition. Code adapted from e2fsck, Copyright (C) 1993, | ||
365 | * 1994 Theodore Ts'o. Also licensed under GPL. | ||
366 | */ | ||
367 | static void check_mount(void) | ||
368 | { | ||
369 | FILE *f; | ||
370 | struct mntent *mnt; | ||
371 | int cont; | ||
372 | int fd; | ||
373 | |||
374 | if ((f = setmntent(MOUNTED, "r")) == NULL) | ||
375 | return; | ||
376 | while ((mnt = getmntent(f)) != NULL) | ||
377 | if (strcmp(device_name, mnt->mnt_fsname) == 0) | ||
378 | break; | ||
379 | endmntent(f); | ||
380 | if (!mnt) | ||
381 | return; | ||
382 | |||
383 | /* | ||
384 | * If the root is mounted read-only, then /etc/mtab is | ||
385 | * probably not correct; so we won't issue a warning based on | ||
386 | * it. | ||
387 | */ | ||
388 | fd = open(MOUNTED, O_RDWR); | ||
389 | if (fd < 0 && errno == EROFS) | ||
390 | return; | ||
391 | else | ||
392 | close(fd); | ||
393 | |||
394 | printf("%s is mounted. ", device_name); | ||
395 | cont = 0; | ||
396 | if (isatty(0) && isatty(1)) | ||
397 | cont = ask("Do you really want to continue", 0); | ||
398 | if (!cont) { | ||
399 | printf("Check aborted\n"); | ||
400 | exit(0); | ||
401 | } | ||
402 | return; | ||
403 | } | ||
404 | |||
405 | /* | ||
406 | * check_zone_nr checks to see that *nr is a valid zone nr. If it | ||
407 | * isn't, it will possibly be repaired. Check_zone_nr sets *corrected | ||
408 | * if an error was corrected, and returns the zone (0 for no zone | ||
409 | * or a bad zone-number). | ||
410 | */ | ||
411 | static int check_zone_nr2(uint32_t *nr, int *corrected) | ||
412 | { | ||
413 | const char *msg; | ||
414 | if (!*nr) | ||
415 | return 0; | ||
416 | if (*nr < FIRSTZONE) | ||
417 | msg = "< FIRSTZONE"; | ||
418 | else if (*nr >= ZONES) | ||
419 | msg = ">= ZONES"; | ||
420 | else | ||
421 | return *nr; | ||
422 | printf("Zone nr %s in file '%s'. ", msg, current_name); | ||
423 | if (ask("Remove block", 1)) { | ||
424 | *nr = 0; | ||
425 | *corrected = 1; | ||
426 | } | ||
427 | return 0; | ||
428 | } | ||
429 | |||
430 | static int check_zone_nr(uint16_t *nr, int *corrected) | ||
431 | { | ||
432 | uint32_t nr32 = *nr; | ||
433 | int r = check_zone_nr2(&nr32, corrected); | ||
434 | *nr = (uint16_t)nr32; | ||
435 | return r; | ||
436 | } | ||
437 | |||
438 | /* | ||
439 | * read-block reads block nr into the buffer at addr. | ||
440 | */ | ||
441 | static void read_block(unsigned int nr, char *addr) | ||
442 | { | ||
443 | if (!nr) { | ||
444 | memset(addr, 0, BLOCK_SIZE); | ||
445 | return; | ||
446 | } | ||
447 | if (BLOCK_SIZE * nr != lseek(IN, BLOCK_SIZE * nr, SEEK_SET)) { | ||
448 | printf("%s: cannot seek to block in file '%s'\n", | ||
449 | bb_msg_read_error, current_name); | ||
450 | errors_uncorrected = 1; | ||
451 | memset(addr, 0, BLOCK_SIZE); | ||
452 | } else if (BLOCK_SIZE != read(IN, addr, BLOCK_SIZE)) { | ||
453 | printf("%s: bad block in file '%s'\n", | ||
454 | bb_msg_read_error, current_name); | ||
455 | errors_uncorrected = 1; | ||
456 | memset(addr, 0, BLOCK_SIZE); | ||
457 | } | ||
458 | } | ||
459 | |||
460 | /* | ||
461 | * write_block writes block nr to disk. | ||
462 | */ | ||
463 | static void write_block(unsigned int nr, char *addr) | ||
464 | { | ||
465 | if (!nr) | ||
466 | return; | ||
467 | if (nr < FIRSTZONE || nr >= ZONES) { | ||
468 | printf("Internal error: trying to write bad block\n" | ||
469 | "Write request ignored\n"); | ||
470 | errors_uncorrected = 1; | ||
471 | return; | ||
472 | } | ||
473 | if (BLOCK_SIZE * nr != lseek(IN, BLOCK_SIZE * nr, SEEK_SET)) | ||
474 | die("Seek failed in write_block"); | ||
475 | if (BLOCK_SIZE != write(IN, addr, BLOCK_SIZE)) { | ||
476 | printf("%s: bad block in file '%s'\n", | ||
477 | bb_msg_write_error, current_name); | ||
478 | errors_uncorrected = 1; | ||
479 | } | ||
480 | } | ||
481 | |||
482 | /* | ||
483 | * map_block calculates the absolute block nr of a block in a file. | ||
484 | * It sets 'changed' if the inode has needed changing, and re-writes | ||
485 | * any indirect blocks with errors. | ||
486 | */ | ||
487 | static int map_block(struct minix_inode *inode, unsigned int blknr) | ||
488 | { | ||
489 | uint16_t ind[BLOCK_SIZE >> 1]; | ||
490 | uint16_t dind[BLOCK_SIZE >> 1]; | ||
491 | int blk_chg, block, result; | ||
492 | |||
493 | if (blknr < 7) | ||
494 | return check_zone_nr(inode->i_zone + blknr, &changed); | ||
495 | blknr -= 7; | ||
496 | if (blknr < 512) { | ||
497 | block = check_zone_nr(inode->i_zone + 7, &changed); | ||
498 | read_block(block, (char *) ind); | ||
499 | blk_chg = 0; | ||
500 | result = check_zone_nr(blknr + ind, &blk_chg); | ||
501 | if (blk_chg) | ||
502 | write_block(block, (char *) ind); | ||
503 | return result; | ||
504 | } | ||
505 | blknr -= 512; | ||
506 | block = check_zone_nr(inode->i_zone + 8, &changed); | ||
507 | read_block(block, (char *) dind); | ||
508 | blk_chg = 0; | ||
509 | result = check_zone_nr(dind + (blknr / 512), &blk_chg); | ||
510 | if (blk_chg) | ||
511 | write_block(block, (char *) dind); | ||
512 | block = result; | ||
513 | read_block(block, (char *) ind); | ||
514 | blk_chg = 0; | ||
515 | result = check_zone_nr(ind + (blknr % 512), &blk_chg); | ||
516 | if (blk_chg) | ||
517 | write_block(block, (char *) ind); | ||
518 | return result; | ||
519 | } | ||
520 | |||
521 | #ifdef CONFIG_FEATURE_MINIX2 | ||
522 | static int map_block2(struct minix2_inode *inode, unsigned int blknr) | ||
523 | { | ||
524 | uint32_t ind[BLOCK_SIZE >> 2]; | ||
525 | uint32_t dind[BLOCK_SIZE >> 2]; | ||
526 | uint32_t tind[BLOCK_SIZE >> 2]; | ||
527 | int blk_chg, block, result; | ||
528 | |||
529 | if (blknr < 7) | ||
530 | return check_zone_nr2(inode->i_zone + blknr, &changed); | ||
531 | blknr -= 7; | ||
532 | if (blknr < 256) { | ||
533 | block = check_zone_nr2(inode->i_zone + 7, &changed); | ||
534 | read_block(block, (char *) ind); | ||
535 | blk_chg = 0; | ||
536 | result = check_zone_nr2(blknr + ind, &blk_chg); | ||
537 | if (blk_chg) | ||
538 | write_block(block, (char *) ind); | ||
539 | return result; | ||
540 | } | ||
541 | blknr -= 256; | ||
542 | if (blknr >= 256 * 256) { | ||
543 | block = check_zone_nr2(inode->i_zone + 8, &changed); | ||
544 | read_block(block, (char *) dind); | ||
545 | blk_chg = 0; | ||
546 | result = check_zone_nr2(dind + blknr / 256, &blk_chg); | ||
547 | if (blk_chg) | ||
548 | write_block(block, (char *) dind); | ||
549 | block = result; | ||
550 | read_block(block, (char *) ind); | ||
551 | blk_chg = 0; | ||
552 | result = check_zone_nr2(ind + blknr % 256, &blk_chg); | ||
553 | if (blk_chg) | ||
554 | write_block(block, (char *) ind); | ||
555 | return result; | ||
556 | } | ||
557 | blknr -= 256 * 256; | ||
558 | block = check_zone_nr2(inode->i_zone + 9, &changed); | ||
559 | read_block(block, (char *) tind); | ||
560 | blk_chg = 0; | ||
561 | result = check_zone_nr2(tind + blknr / (256 * 256), &blk_chg); | ||
562 | if (blk_chg) | ||
563 | write_block(block, (char *) tind); | ||
564 | block = result; | ||
565 | read_block(block, (char *) dind); | ||
566 | blk_chg = 0; | ||
567 | result = check_zone_nr2(dind + (blknr / 256) % 256, &blk_chg); | ||
568 | if (blk_chg) | ||
569 | write_block(block, (char *) dind); | ||
570 | block = result; | ||
571 | read_block(block, (char *) ind); | ||
572 | blk_chg = 0; | ||
573 | result = check_zone_nr2(ind + blknr % 256, &blk_chg); | ||
574 | if (blk_chg) | ||
575 | write_block(block, (char *) ind); | ||
576 | return result; | ||
577 | } | ||
578 | #endif | ||
579 | |||
580 | static void write_super_block(void) | ||
581 | { | ||
582 | /* | ||
583 | * Set the state of the filesystem based on whether or not there | ||
584 | * are uncorrected errors. The filesystem valid flag is | ||
585 | * unconditionally set if we get this far. | ||
586 | */ | ||
587 | Super.s_state |= MINIX_VALID_FS; | ||
588 | if (errors_uncorrected) | ||
589 | Super.s_state |= MINIX_ERROR_FS; | ||
590 | else | ||
591 | Super.s_state &= ~MINIX_ERROR_FS; | ||
592 | |||
593 | if (BLOCK_SIZE != lseek(IN, BLOCK_SIZE, SEEK_SET)) | ||
594 | die("Seek failed in write_super_block"); | ||
595 | if (BLOCK_SIZE != write(IN, super_block_buffer, BLOCK_SIZE)) | ||
596 | die("Unable to write super-block"); | ||
597 | } | ||
598 | |||
599 | static void write_tables(void) | ||
600 | { | ||
601 | write_super_block(); | ||
602 | |||
603 | if (IMAPS * BLOCK_SIZE != write(IN, inode_map, IMAPS * BLOCK_SIZE)) | ||
604 | die("Unable to write inode map"); | ||
605 | if (ZMAPS * BLOCK_SIZE != write(IN, zone_map, ZMAPS * BLOCK_SIZE)) | ||
606 | die("Unable to write zone map"); | ||
607 | if (INODE_BUFFER_SIZE != write(IN, inode_buffer, INODE_BUFFER_SIZE)) | ||
608 | die("Unable to write inodes"); | ||
609 | } | ||
610 | |||
611 | static void get_dirsize(void) | ||
612 | { | ||
613 | int block; | ||
614 | char blk[BLOCK_SIZE]; | ||
615 | int size; | ||
616 | |||
617 | #ifdef CONFIG_FEATURE_MINIX2 | ||
618 | if (version2) | ||
619 | block = Inode2[ROOT_INO].i_zone[0]; | ||
620 | else | ||
621 | #endif | ||
622 | block = Inode[ROOT_INO].i_zone[0]; | ||
623 | read_block(block, blk); | ||
624 | for (size = 16; size < BLOCK_SIZE; size <<= 1) { | ||
625 | if (strcmp(blk + size + 2, "..") == 0) { | ||
626 | dirsize = size; | ||
627 | namelen = size - 2; | ||
628 | return; | ||
629 | } | ||
630 | } | ||
631 | /* use defaults */ | ||
632 | } | ||
633 | |||
634 | static void read_superblock(void) | ||
635 | { | ||
636 | if (BLOCK_SIZE != lseek(IN, BLOCK_SIZE, SEEK_SET)) | ||
637 | die("Seek failed"); | ||
638 | if (BLOCK_SIZE != read(IN, super_block_buffer, BLOCK_SIZE)) | ||
639 | die("Unable to read super block"); | ||
640 | /* already initialized to: | ||
641 | namelen = 14; | ||
642 | dirsize = 16; | ||
643 | version2 = 0; | ||
644 | */ | ||
645 | if (MAGIC == MINIX_SUPER_MAGIC) { | ||
646 | } else if (MAGIC == MINIX_SUPER_MAGIC2) { | ||
647 | namelen = 30; | ||
648 | dirsize = 32; | ||
649 | #ifdef CONFIG_FEATURE_MINIX2 | ||
650 | } else if (MAGIC == MINIX2_SUPER_MAGIC) { | ||
651 | version2 = 1; | ||
652 | } else if (MAGIC == MINIX2_SUPER_MAGIC2) { | ||
653 | namelen = 30; | ||
654 | dirsize = 32; | ||
655 | version2 = 1; | ||
656 | #endif | ||
657 | } else | ||
658 | die("Bad magic number in super-block"); | ||
659 | if (ZONESIZE != 0 || BLOCK_SIZE != 1024) | ||
660 | die("Only 1k blocks/zones supported"); | ||
661 | if (IMAPS * BLOCK_SIZE * 8 < INODES + 1) | ||
662 | die("Bad s_imap_blocks field in super-block"); | ||
663 | if (ZMAPS * BLOCK_SIZE * 8 < ZONES - FIRSTZONE + 1) | ||
664 | die("Bad s_zmap_blocks field in super-block"); | ||
665 | } | ||
666 | |||
667 | static void read_tables(void) | ||
668 | { | ||
669 | inode_map = xzalloc(IMAPS * BLOCK_SIZE); | ||
670 | zone_map = xzalloc(ZMAPS * BLOCK_SIZE); | ||
671 | inode_buffer = xmalloc(INODE_BUFFER_SIZE); | ||
672 | inode_count = xmalloc(INODES + 1); | ||
673 | zone_count = xmalloc(ZONES); | ||
674 | if (IMAPS * BLOCK_SIZE != read(IN, inode_map, IMAPS * BLOCK_SIZE)) | ||
675 | die("Unable to read inode map"); | ||
676 | if (ZMAPS * BLOCK_SIZE != read(IN, zone_map, ZMAPS * BLOCK_SIZE)) | ||
677 | die("Unable to read zone map"); | ||
678 | if (INODE_BUFFER_SIZE != read(IN, inode_buffer, INODE_BUFFER_SIZE)) | ||
679 | die("Unable to read inodes"); | ||
680 | if (NORM_FIRSTZONE != FIRSTZONE) { | ||
681 | printf("Warning: Firstzone!=Norm_firstzone\n"); | ||
682 | errors_uncorrected = 1; | ||
683 | } | ||
684 | get_dirsize(); | ||
685 | if (show) { | ||
686 | printf("%ld inodes\n" | ||
687 | "%ld blocks\n" | ||
688 | "Firstdatazone=%ld (%ld)\n" | ||
689 | "Zonesize=%d\n" | ||
690 | "Maxsize=%ld\n" | ||
691 | "Filesystem state=%d\n" | ||
692 | "namelen=%d\n\n", | ||
693 | INODES, | ||
694 | ZONES, | ||
695 | FIRSTZONE, NORM_FIRSTZONE, | ||
696 | BLOCK_SIZE << ZONESIZE, | ||
697 | MAXSIZE, | ||
698 | Super.s_state, | ||
699 | namelen); | ||
700 | } | ||
701 | } | ||
702 | |||
703 | static struct minix_inode *get_inode(unsigned int nr) | ||
704 | { | ||
705 | struct minix_inode *inode; | ||
706 | |||
707 | if (!nr || nr > INODES) | ||
708 | return NULL; | ||
709 | total++; | ||
710 | inode = Inode + nr; | ||
711 | if (!inode_count[nr]) { | ||
712 | if (!inode_in_use(nr)) { | ||
713 | printf("Inode %d is marked as 'unused', but it is used " | ||
714 | "for file '%s'\n", nr, current_name); | ||
715 | if (repair) { | ||
716 | if (ask("Mark as 'in use'", 1)) | ||
717 | mark_inode(nr); | ||
718 | } else { | ||
719 | errors_uncorrected = 1; | ||
720 | } | ||
721 | } | ||
722 | if (S_ISDIR(inode->i_mode)) | ||
723 | directory++; | ||
724 | else if (S_ISREG(inode->i_mode)) | ||
725 | regular++; | ||
726 | else if (S_ISCHR(inode->i_mode)) | ||
727 | chardev++; | ||
728 | else if (S_ISBLK(inode->i_mode)) | ||
729 | blockdev++; | ||
730 | else if (S_ISLNK(inode->i_mode)) | ||
731 | symlinks++; | ||
732 | else if (S_ISSOCK(inode->i_mode)); | ||
733 | else if (S_ISFIFO(inode->i_mode)); | ||
734 | else { | ||
735 | printf("%s has mode %05o\n", current_name, inode->i_mode); | ||
736 | } | ||
737 | |||
738 | } else | ||
739 | links++; | ||
740 | if (!++inode_count[nr]) { | ||
741 | printf("Warning: inode count too big\n"); | ||
742 | inode_count[nr]--; | ||
743 | errors_uncorrected = 1; | ||
744 | } | ||
745 | return inode; | ||
746 | } | ||
747 | |||
748 | #ifdef CONFIG_FEATURE_MINIX2 | ||
749 | static struct minix2_inode *get_inode2(unsigned int nr) | ||
750 | { | ||
751 | struct minix2_inode *inode; | ||
752 | |||
753 | if (!nr || nr > INODES) | ||
754 | return NULL; | ||
755 | total++; | ||
756 | inode = Inode2 + nr; | ||
757 | if (!inode_count[nr]) { | ||
758 | if (!inode_in_use(nr)) { | ||
759 | printf("Inode %d is marked as 'unused', but it is used " | ||
760 | "for file '%s'\n", nr, current_name); | ||
761 | if (repair) { | ||
762 | if (ask("Mark as 'in use'", 1)) | ||
763 | mark_inode(nr); | ||
764 | else | ||
765 | errors_uncorrected = 1; | ||
766 | } | ||
767 | } | ||
768 | if (S_ISDIR(inode->i_mode)) | ||
769 | directory++; | ||
770 | else if (S_ISREG(inode->i_mode)) | ||
771 | regular++; | ||
772 | else if (S_ISCHR(inode->i_mode)) | ||
773 | chardev++; | ||
774 | else if (S_ISBLK(inode->i_mode)) | ||
775 | blockdev++; | ||
776 | else if (S_ISLNK(inode->i_mode)) | ||
777 | symlinks++; | ||
778 | else if (S_ISSOCK(inode->i_mode)); | ||
779 | else if (S_ISFIFO(inode->i_mode)); | ||
780 | else { | ||
781 | printf("%s has mode %05o\n", current_name, inode->i_mode); | ||
782 | } | ||
783 | } else | ||
784 | links++; | ||
785 | if (!++inode_count[nr]) { | ||
786 | printf("Warning: inode count too big\n"); | ||
787 | inode_count[nr]--; | ||
788 | errors_uncorrected = 1; | ||
789 | } | ||
790 | return inode; | ||
791 | } | ||
792 | #endif | ||
793 | |||
794 | static void check_root(void) | ||
795 | { | ||
796 | struct minix_inode *inode = Inode + ROOT_INO; | ||
797 | |||
798 | if (!inode || !S_ISDIR(inode->i_mode)) | ||
799 | die("Root inode isn't a directory"); | ||
800 | } | ||
801 | |||
802 | #ifdef CONFIG_FEATURE_MINIX2 | ||
803 | static void check_root2(void) | ||
804 | { | ||
805 | struct minix2_inode *inode = Inode2 + ROOT_INO; | ||
806 | |||
807 | if (!inode || !S_ISDIR(inode->i_mode)) | ||
808 | die("Root inode isn't a directory"); | ||
809 | } | ||
810 | #endif | ||
811 | |||
812 | static int add_zone(uint16_t *znr, int *corrected) | ||
813 | { | ||
814 | int result; | ||
815 | int block; | ||
816 | |||
817 | result = 0; | ||
818 | block = check_zone_nr(znr, corrected); | ||
819 | if (!block) | ||
820 | return 0; | ||
821 | if (zone_count[block]) { | ||
822 | printf("Already used block is reused in file '%s'. ", | ||
823 | current_name); | ||
824 | if (ask("Clear", 1)) { | ||
825 | *znr = 0; | ||
826 | block = 0; | ||
827 | *corrected = 1; | ||
828 | return 0; | ||
829 | } | ||
830 | } | ||
831 | if (!zone_in_use(block)) { | ||
832 | printf("Block %d in file '%s' is marked as 'unused'. ", | ||
833 | block, current_name); | ||
834 | if (ask("Correct", 1)) | ||
835 | mark_zone(block); | ||
836 | } | ||
837 | if (!++zone_count[block]) | ||
838 | zone_count[block]--; | ||
839 | return block; | ||
840 | } | ||
841 | |||
842 | #ifdef CONFIG_FEATURE_MINIX2 | ||
843 | static int add_zone2(uint32_t *znr, int *corrected) | ||
844 | { | ||
845 | int result; | ||
846 | int block; | ||
847 | |||
848 | result = 0; | ||
849 | block = check_zone_nr2(znr, corrected); | ||
850 | if (!block) | ||
851 | return 0; | ||
852 | if (zone_count[block]) { | ||
853 | printf("Already used block is reused in file '%s'. ", | ||
854 | current_name); | ||
855 | if (ask("Clear", 1)) { | ||
856 | *znr = 0; | ||
857 | block = 0; | ||
858 | *corrected = 1; | ||
859 | return 0; | ||
860 | } | ||
861 | } | ||
862 | if (!zone_in_use(block)) { | ||
863 | printf("Block %d in file '%s' is marked as 'unused'. ", | ||
864 | block, current_name); | ||
865 | if (ask("Correct", 1)) | ||
866 | mark_zone(block); | ||
867 | } | ||
868 | if (!++zone_count[block]) | ||
869 | zone_count[block]--; | ||
870 | return block; | ||
871 | } | ||
872 | #endif | ||
873 | |||
874 | static void add_zone_ind(uint16_t *znr, int *corrected) | ||
875 | { | ||
876 | static char blk[BLOCK_SIZE]; | ||
877 | int i, chg_blk = 0; | ||
878 | int block; | ||
879 | |||
880 | block = add_zone(znr, corrected); | ||
881 | if (!block) | ||
882 | return; | ||
883 | read_block(block, blk); | ||
884 | for (i = 0; i < (BLOCK_SIZE >> 1); i++) | ||
885 | add_zone(i + (uint16_t *) blk, &chg_blk); | ||
886 | if (chg_blk) | ||
887 | write_block(block, blk); | ||
888 | } | ||
889 | |||
890 | #ifdef CONFIG_FEATURE_MINIX2 | ||
891 | static void add_zone_ind2(uint32_t *znr, int *corrected) | ||
892 | { | ||
893 | static char blk[BLOCK_SIZE]; | ||
894 | int i, chg_blk = 0; | ||
895 | int block; | ||
896 | |||
897 | block = add_zone2(znr, corrected); | ||
898 | if (!block) | ||
899 | return; | ||
900 | read_block(block, blk); | ||
901 | for (i = 0; i < BLOCK_SIZE >> 2; i++) | ||
902 | add_zone2(i + (uint32_t *) blk, &chg_blk); | ||
903 | if (chg_blk) | ||
904 | write_block(block, blk); | ||
905 | } | ||
906 | #endif | ||
907 | |||
908 | static void add_zone_dind(uint16_t *znr, int *corrected) | ||
909 | { | ||
910 | static char blk[BLOCK_SIZE]; | ||
911 | int i, blk_chg = 0; | ||
912 | int block; | ||
913 | |||
914 | block = add_zone(znr, corrected); | ||
915 | if (!block) | ||
916 | return; | ||
917 | read_block(block, blk); | ||
918 | for (i = 0; i < (BLOCK_SIZE >> 1); i++) | ||
919 | add_zone_ind(i + (uint16_t *) blk, &blk_chg); | ||
920 | if (blk_chg) | ||
921 | write_block(block, blk); | ||
922 | } | ||
923 | |||
924 | #ifdef CONFIG_FEATURE_MINIX2 | ||
925 | static void add_zone_dind2(uint32_t *znr, int *corrected) | ||
926 | { | ||
927 | static char blk[BLOCK_SIZE]; | ||
928 | int i, blk_chg = 0; | ||
929 | int block; | ||
930 | |||
931 | block = add_zone2(znr, corrected); | ||
932 | if (!block) | ||
933 | return; | ||
934 | read_block(block, blk); | ||
935 | for (i = 0; i < BLOCK_SIZE >> 2; i++) | ||
936 | add_zone_ind2(i + (uint32_t *) blk, &blk_chg); | ||
937 | if (blk_chg) | ||
938 | write_block(block, blk); | ||
939 | } | ||
940 | |||
941 | static void add_zone_tind2(uint32_t *znr, int *corrected) | ||
942 | { | ||
943 | static char blk[BLOCK_SIZE]; | ||
944 | int i, blk_chg = 0; | ||
945 | int block; | ||
946 | |||
947 | block = add_zone2(znr, corrected); | ||
948 | if (!block) | ||
949 | return; | ||
950 | read_block(block, blk); | ||
951 | for (i = 0; i < BLOCK_SIZE >> 2; i++) | ||
952 | add_zone_dind2(i + (uint32_t *) blk, &blk_chg); | ||
953 | if (blk_chg) | ||
954 | write_block(block, blk); | ||
955 | } | ||
956 | #endif | ||
957 | |||
958 | static void check_zones(unsigned int i) | ||
959 | { | ||
960 | struct minix_inode *inode; | ||
961 | |||
962 | if (!i || i > INODES) | ||
963 | return; | ||
964 | if (inode_count[i] > 1) /* have we counted this file already? */ | ||
965 | return; | ||
966 | inode = Inode + i; | ||
967 | if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) && | ||
968 | !S_ISLNK(inode->i_mode)) return; | ||
969 | for (i = 0; i < 7; i++) | ||
970 | add_zone(i + inode->i_zone, &changed); | ||
971 | add_zone_ind(7 + inode->i_zone, &changed); | ||
972 | add_zone_dind(8 + inode->i_zone, &changed); | ||
973 | } | ||
974 | |||
975 | #ifdef CONFIG_FEATURE_MINIX2 | ||
976 | static void check_zones2(unsigned int i) | ||
977 | { | ||
978 | struct minix2_inode *inode; | ||
979 | |||
980 | if (!i || i > INODES) | ||
981 | return; | ||
982 | if (inode_count[i] > 1) /* have we counted this file already? */ | ||
983 | return; | ||
984 | inode = Inode2 + i; | ||
985 | if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) | ||
986 | && !S_ISLNK(inode->i_mode)) | ||
987 | return; | ||
988 | for (i = 0; i < 7; i++) | ||
989 | add_zone2(i + inode->i_zone, &changed); | ||
990 | add_zone_ind2(7 + inode->i_zone, &changed); | ||
991 | add_zone_dind2(8 + inode->i_zone, &changed); | ||
992 | add_zone_tind2(9 + inode->i_zone, &changed); | ||
993 | } | ||
994 | #endif | ||
995 | |||
996 | static void check_file(struct minix_inode *dir, unsigned int offset) | ||
997 | { | ||
998 | static char blk[BLOCK_SIZE]; | ||
999 | struct minix_inode *inode; | ||
1000 | int ino; | ||
1001 | char *name; | ||
1002 | int block; | ||
1003 | |||
1004 | block = map_block(dir, offset / BLOCK_SIZE); | ||
1005 | read_block(block, blk); | ||
1006 | name = blk + (offset % BLOCK_SIZE) + 2; | ||
1007 | ino = *(uint16_t *) (name - 2); | ||
1008 | if (ino > INODES) { | ||
1009 | printf("%s contains a bad inode number for file '%.*s'. ", | ||
1010 | current_name, namelen, name); | ||
1011 | if (ask("Remove", 1)) { | ||
1012 | *(uint16_t *) (name - 2) = 0; | ||
1013 | write_block(block, blk); | ||
1014 | } | ||
1015 | ino = 0; | ||
1016 | } | ||
1017 | push_filename(name); | ||
1018 | inode = get_inode(ino); | ||
1019 | pop_filename(); | ||
1020 | if (!offset) { | ||
1021 | if (!inode || strcmp(".", name)) { | ||
1022 | printf("%s: bad directory: '.' isn't first\n", current_name); | ||
1023 | errors_uncorrected = 1; | ||
1024 | } else | ||
1025 | return; | ||
1026 | } | ||
1027 | if (offset == dirsize) { | ||
1028 | if (!inode || strcmp("..", name)) { | ||
1029 | printf("%s: bad directory: '..' isn't second\n", current_name); | ||
1030 | errors_uncorrected = 1; | ||
1031 | } else | ||
1032 | return; | ||
1033 | } | ||
1034 | if (!inode) | ||
1035 | return; | ||
1036 | push_filename(name); | ||
1037 | if (list) { | ||
1038 | if (verbose) | ||
1039 | printf("%6d %07o %3d ", ino, inode->i_mode, inode->i_nlinks); | ||
1040 | printf("%s%s\n", current_name, S_ISDIR(inode->i_mode) ? ":" : ""); | ||
1041 | } | ||
1042 | check_zones(ino); | ||
1043 | if (inode && S_ISDIR(inode->i_mode)) | ||
1044 | recursive_check(ino); | ||
1045 | pop_filename(); | ||
1046 | return; | ||
1047 | } | ||
1048 | |||
1049 | #ifdef CONFIG_FEATURE_MINIX2 | ||
1050 | static void check_file2(struct minix2_inode *dir, unsigned int offset) | ||
1051 | { | ||
1052 | static char blk[BLOCK_SIZE]; | ||
1053 | struct minix2_inode *inode; | ||
1054 | int ino; | ||
1055 | char *name; | ||
1056 | int block; | ||
1057 | |||
1058 | block = map_block2(dir, offset / BLOCK_SIZE); | ||
1059 | read_block(block, blk); | ||
1060 | name = blk + (offset % BLOCK_SIZE) + 2; | ||
1061 | ino = *(uint16_t *) (name - 2); | ||
1062 | if (ino > INODES) { | ||
1063 | printf("%s contains a bad inode number for file '%.*s'. ", | ||
1064 | current_name, namelen, name); | ||
1065 | if (ask("Remove", 1)) { | ||
1066 | *(uint16_t *) (name - 2) = 0; | ||
1067 | write_block(block, blk); | ||
1068 | } | ||
1069 | ino = 0; | ||
1070 | } | ||
1071 | push_filename(name); | ||
1072 | inode = get_inode2(ino); | ||
1073 | pop_filename(); | ||
1074 | if (!offset) { | ||
1075 | if (!inode || strcmp(".", name)) { | ||
1076 | printf("%s: bad directory: '.' isn't first\n", current_name); | ||
1077 | errors_uncorrected = 1; | ||
1078 | } else | ||
1079 | return; | ||
1080 | } | ||
1081 | if (offset == dirsize) { | ||
1082 | if (!inode || strcmp("..", name)) { | ||
1083 | printf("%s: bad directory: '..' isn't second\n", current_name); | ||
1084 | errors_uncorrected = 1; | ||
1085 | } else | ||
1086 | return; | ||
1087 | } | ||
1088 | if (!inode) | ||
1089 | return; | ||
1090 | push_filename(name); | ||
1091 | if (list) { | ||
1092 | if (verbose) | ||
1093 | printf("%6d %07o %3d ", ino, inode->i_mode, inode->i_nlinks); | ||
1094 | printf("%s%s\n", current_name, S_ISDIR(inode->i_mode) ? ":" : ""); | ||
1095 | } | ||
1096 | check_zones2(ino); | ||
1097 | if (inode && S_ISDIR(inode->i_mode)) | ||
1098 | recursive_check2(ino); | ||
1099 | pop_filename(); | ||
1100 | return; | ||
1101 | } | ||
1102 | #endif | ||
1103 | |||
1104 | static void recursive_check(unsigned int ino) | ||
1105 | { | ||
1106 | struct minix_inode *dir; | ||
1107 | unsigned int offset; | ||
1108 | |||
1109 | dir = Inode + ino; | ||
1110 | if (!S_ISDIR(dir->i_mode)) | ||
1111 | die("Internal error"); | ||
1112 | if (dir->i_size < 2 * dirsize) { | ||
1113 | printf("%s: bad directory: size<32", current_name); | ||
1114 | errors_uncorrected = 1; | ||
1115 | } | ||
1116 | for (offset = 0; offset < dir->i_size; offset += dirsize) | ||
1117 | check_file(dir, offset); | ||
1118 | } | ||
1119 | |||
1120 | #ifdef CONFIG_FEATURE_MINIX2 | ||
1121 | static void recursive_check2(unsigned int ino) | ||
1122 | { | ||
1123 | struct minix2_inode *dir; | ||
1124 | unsigned int offset; | ||
1125 | |||
1126 | dir = Inode2 + ino; | ||
1127 | if (!S_ISDIR(dir->i_mode)) | ||
1128 | die("Internal error"); | ||
1129 | if (dir->i_size < 2 * dirsize) { | ||
1130 | printf("%s: bad directory: size<32", current_name); | ||
1131 | errors_uncorrected = 1; | ||
1132 | } | ||
1133 | for (offset = 0; offset < dir->i_size; offset += dirsize) | ||
1134 | check_file2(dir, offset); | ||
1135 | } | ||
1136 | #endif | ||
1137 | |||
1138 | static int bad_zone(int i) | ||
1139 | { | ||
1140 | char buffer[1024]; | ||
1141 | |||
1142 | if (BLOCK_SIZE * i != lseek(IN, BLOCK_SIZE * i, SEEK_SET)) | ||
1143 | die("Seek failed in bad_zone"); | ||
1144 | return (BLOCK_SIZE != read(IN, buffer, BLOCK_SIZE)); | ||
1145 | } | ||
1146 | |||
1147 | static void check_counts(void) | ||
1148 | { | ||
1149 | int i; | ||
1150 | |||
1151 | for (i = 1; i <= INODES; i++) { | ||
1152 | if (warn_mode && Inode[i].i_mode && !inode_in_use(i)) { | ||
1153 | printf("Inode %d has non-zero mode. ", i); | ||
1154 | if (ask("Clear", 1)) { | ||
1155 | Inode[i].i_mode = 0; | ||
1156 | changed = 1; | ||
1157 | } | ||
1158 | } | ||
1159 | if (!inode_count[i]) { | ||
1160 | if (!inode_in_use(i)) | ||
1161 | continue; | ||
1162 | printf("Unused inode %d is marked as 'used' in the bitmap. ", i); | ||
1163 | if (ask("Clear", 1)) | ||
1164 | unmark_inode(i); | ||
1165 | continue; | ||
1166 | } | ||
1167 | if (!inode_in_use(i)) { | ||
1168 | printf("Inode %d is used, but marked as 'unused' in the bitmap. ", i); | ||
1169 | if (ask("Set", 1)) | ||
1170 | mark_inode(i); | ||
1171 | } | ||
1172 | if (Inode[i].i_nlinks != inode_count[i]) { | ||
1173 | printf("Inode %d (mode=%07o), i_nlinks=%d, counted=%d. ", | ||
1174 | i, Inode[i].i_mode, Inode[i].i_nlinks, inode_count[i]); | ||
1175 | if (ask("Set i_nlinks to count", 1)) { | ||
1176 | Inode[i].i_nlinks = inode_count[i]; | ||
1177 | changed = 1; | ||
1178 | } | ||
1179 | } | ||
1180 | } | ||
1181 | for (i = FIRSTZONE; i < ZONES; i++) { | ||
1182 | if (zone_in_use(i) == zone_count[i]) | ||
1183 | continue; | ||
1184 | if (!zone_count[i]) { | ||
1185 | if (bad_zone(i)) | ||
1186 | continue; | ||
1187 | printf("Zone %d is marked 'in use', but no file uses it. ", i); | ||
1188 | if (ask("Unmark", 1)) | ||
1189 | unmark_zone(i); | ||
1190 | continue; | ||
1191 | } | ||
1192 | printf("Zone %d: %sin use, counted=%d\n", | ||
1193 | i, zone_in_use(i) ? "" : "not ", zone_count[i]); | ||
1194 | } | ||
1195 | } | ||
1196 | |||
1197 | #ifdef CONFIG_FEATURE_MINIX2 | ||
1198 | static void check_counts2(void) | ||
1199 | { | ||
1200 | int i; | ||
1201 | |||
1202 | for (i = 1; i <= INODES; i++) { | ||
1203 | if (warn_mode && Inode2[i].i_mode && !inode_in_use(i)) { | ||
1204 | printf("Inode %d has non-zero mode. ", i); | ||
1205 | if (ask("Clear", 1)) { | ||
1206 | Inode2[i].i_mode = 0; | ||
1207 | changed = 1; | ||
1208 | } | ||
1209 | } | ||
1210 | if (!inode_count[i]) { | ||
1211 | if (!inode_in_use(i)) | ||
1212 | continue; | ||
1213 | printf("Unused inode %d is marked as 'used' in the bitmap. ", i); | ||
1214 | if (ask("Clear", 1)) | ||
1215 | unmark_inode(i); | ||
1216 | continue; | ||
1217 | } | ||
1218 | if (!inode_in_use(i)) { | ||
1219 | printf("Inode %d is used, but marked as 'unused' in the bitmap. ", i); | ||
1220 | if (ask("Set", 1)) | ||
1221 | mark_inode(i); | ||
1222 | } | ||
1223 | if (Inode2[i].i_nlinks != inode_count[i]) { | ||
1224 | printf("Inode %d (mode=%07o), i_nlinks=%d, counted=%d. ", | ||
1225 | i, Inode2[i].i_mode, Inode2[i].i_nlinks, | ||
1226 | inode_count[i]); | ||
1227 | if (ask("Set i_nlinks to count", 1)) { | ||
1228 | Inode2[i].i_nlinks = inode_count[i]; | ||
1229 | changed = 1; | ||
1230 | } | ||
1231 | } | ||
1232 | } | ||
1233 | for (i = FIRSTZONE; i < ZONES; i++) { | ||
1234 | if (zone_in_use(i) == zone_count[i]) | ||
1235 | continue; | ||
1236 | if (!zone_count[i]) { | ||
1237 | if (bad_zone(i)) | ||
1238 | continue; | ||
1239 | printf("Zone %d is marked 'in use', but no file uses it. ", i); | ||
1240 | if (ask("Unmark", 1)) | ||
1241 | unmark_zone(i); | ||
1242 | continue; | ||
1243 | } | ||
1244 | printf("Zone %d: %sin use, counted=%d\n", | ||
1245 | i, zone_in_use(i) ? "" : "not ", zone_count[i]); | ||
1246 | } | ||
1247 | } | ||
1248 | #endif | ||
1249 | |||
1250 | static void check(void) | ||
1251 | { | ||
1252 | memset(inode_count, 0, (INODES + 1) * sizeof(*inode_count)); | ||
1253 | memset(zone_count, 0, ZONES * sizeof(*zone_count)); | ||
1254 | check_zones(ROOT_INO); | ||
1255 | recursive_check(ROOT_INO); | ||
1256 | check_counts(); | ||
1257 | } | ||
1258 | |||
1259 | #ifdef CONFIG_FEATURE_MINIX2 | ||
1260 | static void check2(void) | ||
1261 | { | ||
1262 | memset(inode_count, 0, (INODES + 1) * sizeof(*inode_count)); | ||
1263 | memset(zone_count, 0, ZONES * sizeof(*zone_count)); | ||
1264 | check_zones2(ROOT_INO); | ||
1265 | recursive_check2(ROOT_INO); | ||
1266 | check_counts2(); | ||
1267 | } | ||
1268 | #endif | ||
1269 | |||
1270 | int fsck_minix_main(int argc, char **argv) | ||
1271 | { | ||
1272 | struct termios tmp; | ||
1273 | int retcode = 0; | ||
1274 | |||
1275 | alloc_current_name(); | ||
1276 | #ifdef CONFIG_FEATURE_CLEAN_UP | ||
1277 | /* Don't bother to free memory. Exit does | ||
1278 | * that automagically, so we can save a few bytes */ | ||
1279 | atexit(free_current_name); | ||
1280 | #endif | ||
1281 | |||
1282 | if (INODE_SIZE * MINIX_INODES_PER_BLOCK != BLOCK_SIZE) | ||
1283 | die("Bad inode size"); | ||
1284 | #ifdef CONFIG_FEATURE_MINIX2 | ||
1285 | if (INODE_SIZE2 * MINIX2_INODES_PER_BLOCK != BLOCK_SIZE) | ||
1286 | die("Bad v2 inode size"); | ||
1287 | #endif | ||
1288 | while (argc-- > 1) { | ||
1289 | argv++; | ||
1290 | if (argv[0][0] != '-') { | ||
1291 | if (device_name) | ||
1292 | bb_show_usage(); | ||
1293 | else | ||
1294 | device_name = argv[0]; | ||
1295 | } else | ||
1296 | while (*++argv[0]) | ||
1297 | switch (argv[0][0]) { | ||
1298 | case 'l': | ||
1299 | list = 1; | ||
1300 | break; | ||
1301 | case 'a': | ||
1302 | automatic = 1; | ||
1303 | repair = 1; | ||
1304 | break; | ||
1305 | case 'r': | ||
1306 | automatic = 0; | ||
1307 | repair = 1; | ||
1308 | break; | ||
1309 | case 'v': | ||
1310 | verbose = 1; | ||
1311 | break; | ||
1312 | case 's': | ||
1313 | show = 1; | ||
1314 | break; | ||
1315 | case 'm': | ||
1316 | warn_mode = 1; | ||
1317 | break; | ||
1318 | case 'f': | ||
1319 | force = 1; | ||
1320 | break; | ||
1321 | default: | ||
1322 | bb_show_usage(); | ||
1323 | } | ||
1324 | } | ||
1325 | if (!device_name) | ||
1326 | bb_show_usage(); | ||
1327 | check_mount(); /* trying to check a mounted filesystem? */ | ||
1328 | if (repair && !automatic) { | ||
1329 | if (!isatty(0) || !isatty(1)) | ||
1330 | die("Need terminal for interactive repairs"); | ||
1331 | } | ||
1332 | IN = open(device_name, repair ? O_RDWR : O_RDONLY); | ||
1333 | if (IN < 0){ | ||
1334 | printf("Unable to open device '%s'\n", device_name); | ||
1335 | leave(8); | ||
1336 | } | ||
1337 | sync(); /* paranoia? */ | ||
1338 | read_superblock(); | ||
1339 | |||
1340 | /* | ||
1341 | * Determine whether or not we should continue with the checking. | ||
1342 | * This is based on the status of the filesystem valid and error | ||
1343 | * flags and whether or not the -f switch was specified on the | ||
1344 | * command line. | ||
1345 | */ | ||
1346 | printf("%s, %s\n", applet_name, program_version); | ||
1347 | if (!(Super.s_state & MINIX_ERROR_FS) && | ||
1348 | (Super.s_state & MINIX_VALID_FS) && !force) { | ||
1349 | if (repair) | ||
1350 | printf("%s is clean, check is skipped\n", device_name); | ||
1351 | return retcode; | ||
1352 | } else if (force) | ||
1353 | printf("Forcing filesystem check on %s\n", device_name); | ||
1354 | else if (repair) | ||
1355 | printf("Filesystem on %s is dirty, needs checking\n", | ||
1356 | device_name); | ||
1357 | |||
1358 | read_tables(); | ||
1359 | |||
1360 | if (repair && !automatic) { | ||
1361 | tcgetattr(0, &termios); | ||
1362 | tmp = termios; | ||
1363 | tmp.c_lflag &= ~(ICANON | ECHO); | ||
1364 | tcsetattr(0, TCSANOW, &tmp); | ||
1365 | termios_set = 1; | ||
1366 | } | ||
1367 | #ifdef CONFIG_FEATURE_MINIX2 | ||
1368 | if (version2) { | ||
1369 | check_root2(); | ||
1370 | check2(); | ||
1371 | } else | ||
1372 | #endif | ||
1373 | { | ||
1374 | check_root(); | ||
1375 | check(); | ||
1376 | } | ||
1377 | if (verbose) { | ||
1378 | int i, free_cnt; | ||
1379 | |||
1380 | for (i = 1, free_cnt = 0; i <= INODES; i++) | ||
1381 | if (!inode_in_use(i)) | ||
1382 | free_cnt++; | ||
1383 | printf("\n%6ld inodes used (%ld%%)\n", (INODES - free_cnt), | ||
1384 | 100 * (INODES - free_cnt) / INODES); | ||
1385 | for (i = FIRSTZONE, free_cnt = 0; i < ZONES; i++) | ||
1386 | if (!zone_in_use(i)) | ||
1387 | free_cnt++; | ||
1388 | printf("%6ld zones used (%ld%%)\n\n" | ||
1389 | "%6d regular files\n" | ||
1390 | "%6d directories\n" | ||
1391 | "%6d character device files\n" | ||
1392 | "%6d block device files\n" | ||
1393 | "%6d links\n" | ||
1394 | "%6d symbolic links\n" | ||
1395 | "------\n" | ||
1396 | "%6d files\n", | ||
1397 | (ZONES - free_cnt), 100 * (ZONES - free_cnt) / ZONES, | ||
1398 | regular, directory, chardev, blockdev, | ||
1399 | links - 2 * directory + 1, symlinks, | ||
1400 | total - 2 * directory + 1); | ||
1401 | } | ||
1402 | if (changed) { | ||
1403 | write_tables(); | ||
1404 | printf("FILE SYSTEM HAS BEEN CHANGED\n"); | ||
1405 | sync(); | ||
1406 | } else if (repair) | ||
1407 | write_super_block(); | ||
1408 | |||
1409 | if (repair && !automatic) | ||
1410 | tcsetattr(0, TCSANOW, &termios); | ||
1411 | |||
1412 | if (changed) | ||
1413 | retcode += 3; | ||
1414 | if (errors_uncorrected) | ||
1415 | retcode += 4; | ||
1416 | return retcode; | ||
1417 | } | ||
diff --git a/util-linux/getopt.c b/util-linux/getopt.c new file mode 100644 index 000000000..64f568aa9 --- /dev/null +++ b/util-linux/getopt.c | |||
@@ -0,0 +1,365 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * getopt.c - Enhanced implementation of BSD getopt(1) | ||
4 | * Copyright (c) 1997, 1998, 1999, 2000 Frodo Looijaard <frodol@dds.nl> | ||
5 | * | ||
6 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
7 | */ | ||
8 | |||
9 | /* | ||
10 | * Version 1.0-b4: Tue Sep 23 1997. First public release. | ||
11 | * Version 1.0: Wed Nov 19 1997. | ||
12 | * Bumped up the version number to 1.0 | ||
13 | * Fixed minor typo (CSH instead of TCSH) | ||
14 | * Version 1.0.1: Tue Jun 3 1998 | ||
15 | * Fixed sizeof instead of strlen bug | ||
16 | * Bumped up the version number to 1.0.1 | ||
17 | * Version 1.0.2: Thu Jun 11 1998 (not present) | ||
18 | * Fixed gcc-2.8.1 warnings | ||
19 | * Fixed --version/-V option (not present) | ||
20 | * Version 1.0.5: Tue Jun 22 1999 | ||
21 | * Make -u option work (not present) | ||
22 | * Version 1.0.6: Tue Jun 27 2000 | ||
23 | * No important changes | ||
24 | * Version 1.1.0: Tue Jun 30 2000 | ||
25 | * Added NLS support (partly written by Arkadiusz Mi<B6>kiewicz | ||
26 | * <misiek@misiek.eu.org>) | ||
27 | * Ported to Busybox - Alfred M. Szmidt <ams@trillian.itslinux.org> | ||
28 | * Removed --version/-V and --help/-h in | ||
29 | * Removed parse_error(), using bb_error_msg() from Busybox instead | ||
30 | * Replaced our_malloc with xmalloc and our_realloc with xrealloc | ||
31 | * | ||
32 | */ | ||
33 | |||
34 | #include "busybox.h" | ||
35 | #include <getopt.h> | ||
36 | |||
37 | /* NON_OPT is the code that is returned when a non-option is found in '+' | ||
38 | mode */ | ||
39 | enum { | ||
40 | NON_OPT = 1, | ||
41 | /* LONG_OPT is the code that is returned when a long option is found. */ | ||
42 | LONG_OPT = 2 | ||
43 | }; | ||
44 | |||
45 | /* The shells recognized. */ | ||
46 | typedef enum {BASH,TCSH} shell_t; | ||
47 | |||
48 | |||
49 | /* Some global variables that tells us how to parse. */ | ||
50 | static shell_t shell=BASH; /* The shell we generate output for. */ | ||
51 | static int quiet_errors; /* 0 is not quiet. */ | ||
52 | static int quiet_output; /* 0 is not quiet. */ | ||
53 | static int quote=1; /* 1 is do quote. */ | ||
54 | static int alternative; /* 0 is getopt_long, 1 is getopt_long_only */ | ||
55 | |||
56 | /* Function prototypes */ | ||
57 | static const char *normalize(const char *arg); | ||
58 | static int generate_output(char * argv[],int argc,const char *optstr, | ||
59 | const struct option *longopts); | ||
60 | static void add_long_options(char *options); | ||
61 | static void add_longopt(const char *name,int has_arg); | ||
62 | static void set_shell(const char *new_shell); | ||
63 | |||
64 | |||
65 | /* | ||
66 | * This function 'normalizes' a single argument: it puts single quotes around | ||
67 | * it and escapes other special characters. If quote is false, it just | ||
68 | * returns its argument. | ||
69 | * Bash only needs special treatment for single quotes; tcsh also recognizes | ||
70 | * exclamation marks within single quotes, and nukes whitespace. | ||
71 | * This function returns a pointer to a buffer that is overwritten by | ||
72 | * each call. | ||
73 | */ | ||
74 | const char *normalize(const char *arg) | ||
75 | { | ||
76 | static char *BUFFER=NULL; | ||
77 | const char *argptr=arg; | ||
78 | char *bufptr; | ||
79 | |||
80 | free(BUFFER); | ||
81 | |||
82 | if (!quote) { /* Just copy arg */ | ||
83 | BUFFER=xstrdup(arg); | ||
84 | return BUFFER; | ||
85 | } | ||
86 | |||
87 | /* Each character in arg may take up to four characters in the result: | ||
88 | For a quote we need a closing quote, a backslash, a quote and an | ||
89 | opening quote! We need also the global opening and closing quote, | ||
90 | and one extra character for '\0'. */ | ||
91 | BUFFER=xmalloc(strlen(arg)*4+3); | ||
92 | |||
93 | bufptr=BUFFER; | ||
94 | *bufptr++='\''; | ||
95 | |||
96 | while (*argptr) { | ||
97 | if (*argptr == '\'') { | ||
98 | /* Quote: replace it with: '\'' */ | ||
99 | *bufptr++='\''; | ||
100 | *bufptr++='\\'; | ||
101 | *bufptr++='\''; | ||
102 | *bufptr++='\''; | ||
103 | } else if (shell==TCSH && *argptr=='!') { | ||
104 | /* Exclamation mark: replace it with: \! */ | ||
105 | *bufptr++='\''; | ||
106 | *bufptr++='\\'; | ||
107 | *bufptr++='!'; | ||
108 | *bufptr++='\''; | ||
109 | } else if (shell==TCSH && *argptr=='\n') { | ||
110 | /* Newline: replace it with: \n */ | ||
111 | *bufptr++='\\'; | ||
112 | *bufptr++='n'; | ||
113 | } else if (shell==TCSH && isspace(*argptr)) { | ||
114 | /* Non-newline whitespace: replace it with \<ws> */ | ||
115 | *bufptr++='\''; | ||
116 | *bufptr++='\\'; | ||
117 | *bufptr++=*argptr; | ||
118 | *bufptr++='\''; | ||
119 | } else | ||
120 | /* Just copy */ | ||
121 | *bufptr++=*argptr; | ||
122 | argptr++; | ||
123 | } | ||
124 | *bufptr++='\''; | ||
125 | *bufptr++='\0'; | ||
126 | return BUFFER; | ||
127 | } | ||
128 | |||
129 | /* | ||
130 | * Generate the output. argv[0] is the program name (used for reporting errors). | ||
131 | * argv[1..] contains the options to be parsed. argc must be the number of | ||
132 | * elements in argv (ie. 1 if there are no options, only the program name), | ||
133 | * optstr must contain the short options, and longopts the long options. | ||
134 | * Other settings are found in global variables. | ||
135 | */ | ||
136 | int generate_output(char * argv[],int argc,const char *optstr, | ||
137 | const struct option *longopts) | ||
138 | { | ||
139 | int exit_code = 0; /* We assume everything will be OK */ | ||
140 | int opt; | ||
141 | int longindex; | ||
142 | const char *charptr; | ||
143 | |||
144 | if (quiet_errors) /* No error reporting from getopt(3) */ | ||
145 | opterr=0; | ||
146 | optind=0; /* Reset getopt(3) */ | ||
147 | |||
148 | while ((opt = (alternative? | ||
149 | getopt_long_only(argc,argv,optstr,longopts,&longindex): | ||
150 | getopt_long(argc,argv,optstr,longopts,&longindex))) | ||
151 | != EOF) | ||
152 | if (opt == '?' || opt == ':' ) | ||
153 | exit_code = 1; | ||
154 | else if (!quiet_output) { | ||
155 | if (opt == LONG_OPT) { | ||
156 | printf(" --%s",longopts[longindex].name); | ||
157 | if (longopts[longindex].has_arg) | ||
158 | printf(" %s", | ||
159 | normalize(optarg?optarg:"")); | ||
160 | } else if (opt == NON_OPT) | ||
161 | printf(" %s",normalize(optarg)); | ||
162 | else { | ||
163 | printf(" -%c",opt); | ||
164 | charptr = strchr(optstr,opt); | ||
165 | if (charptr != NULL && *++charptr == ':') | ||
166 | printf(" %s", | ||
167 | normalize(optarg?optarg:"")); | ||
168 | } | ||
169 | } | ||
170 | |||
171 | if (! quiet_output) { | ||
172 | printf(" --"); | ||
173 | while (optind < argc) | ||
174 | printf(" %s",normalize(argv[optind++])); | ||
175 | puts(""); | ||
176 | } | ||
177 | return exit_code; | ||
178 | } | ||
179 | |||
180 | static struct option *long_options; | ||
181 | static int long_options_length; /* Length of array */ | ||
182 | static int long_options_nr; /* Nr of used elements in array */ | ||
183 | enum { LONG_OPTIONS_INCR = 10 }; | ||
184 | #define init_longopt() add_longopt(NULL,0) | ||
185 | |||
186 | /* Register a long option. The contents of name is copied. */ | ||
187 | void add_longopt(const char *name, int has_arg) | ||
188 | { | ||
189 | if (!name) { /* init */ | ||
190 | free(long_options); | ||
191 | long_options=NULL; | ||
192 | long_options_length=0; | ||
193 | long_options_nr=0; | ||
194 | } | ||
195 | |||
196 | if (long_options_nr == long_options_length) { | ||
197 | long_options_length += LONG_OPTIONS_INCR; | ||
198 | long_options=xrealloc(long_options, | ||
199 | sizeof(struct option) * | ||
200 | long_options_length); | ||
201 | } | ||
202 | |||
203 | long_options[long_options_nr].name=NULL; | ||
204 | long_options[long_options_nr].has_arg=0; | ||
205 | long_options[long_options_nr].flag=NULL; | ||
206 | long_options[long_options_nr].val=0; | ||
207 | |||
208 | if (long_options_nr) { /* Not for init! */ | ||
209 | long_options[long_options_nr-1].has_arg=has_arg; | ||
210 | long_options[long_options_nr-1].flag=NULL; | ||
211 | long_options[long_options_nr-1].val=LONG_OPT; | ||
212 | long_options[long_options_nr-1].name=xstrdup(name); | ||
213 | } | ||
214 | long_options_nr++; | ||
215 | } | ||
216 | |||
217 | |||
218 | /* | ||
219 | * Register several long options. options is a string of long options, | ||
220 | * separated by commas or whitespace. | ||
221 | * This nukes options! | ||
222 | */ | ||
223 | void add_long_options(char *options) | ||
224 | { | ||
225 | int arg_opt, tlen; | ||
226 | char *tokptr=strtok(options,", \t\n"); | ||
227 | while (tokptr) { | ||
228 | arg_opt=no_argument; | ||
229 | tlen=strlen(tokptr); | ||
230 | if (tlen > 0) { | ||
231 | if (tokptr[tlen-1] == ':') { | ||
232 | if (tlen > 1 && tokptr[tlen-2] == ':') { | ||
233 | tokptr[tlen-2]='\0'; | ||
234 | tlen -= 2; | ||
235 | arg_opt=optional_argument; | ||
236 | } else { | ||
237 | tokptr[tlen-1]='\0'; | ||
238 | tlen -= 1; | ||
239 | arg_opt=required_argument; | ||
240 | } | ||
241 | if (tlen == 0) | ||
242 | bb_error_msg("empty long option after -l or --long argument"); | ||
243 | } | ||
244 | add_longopt(tokptr,arg_opt); | ||
245 | } | ||
246 | tokptr=strtok(NULL,", \t\n"); | ||
247 | } | ||
248 | } | ||
249 | |||
250 | void set_shell(const char *new_shell) | ||
251 | { | ||
252 | if (!strcmp(new_shell,"bash")) | ||
253 | shell=BASH; | ||
254 | else if (!strcmp(new_shell,"tcsh")) | ||
255 | shell=TCSH; | ||
256 | else if (!strcmp(new_shell,"sh")) | ||
257 | shell=BASH; | ||
258 | else if (!strcmp(new_shell,"csh")) | ||
259 | shell=TCSH; | ||
260 | else | ||
261 | bb_error_msg("unknown shell after -s or --shell argument"); | ||
262 | } | ||
263 | |||
264 | |||
265 | /* Exit codes: | ||
266 | * 0) No errors, successful operation. | ||
267 | * 1) getopt(3) returned an error. | ||
268 | * 2) A problem with parameter parsing for getopt(1). | ||
269 | * 3) Internal error, out of memory | ||
270 | * 4) Returned for -T | ||
271 | */ | ||
272 | |||
273 | static const struct option longopts[]= | ||
274 | { | ||
275 | {"options",required_argument,NULL,'o'}, | ||
276 | {"longoptions",required_argument,NULL,'l'}, | ||
277 | {"quiet",no_argument,NULL,'q'}, | ||
278 | {"quiet-output",no_argument,NULL,'Q'}, | ||
279 | {"shell",required_argument,NULL,'s'}, | ||
280 | {"test",no_argument,NULL,'T'}, | ||
281 | {"unquoted",no_argument,NULL,'u'}, | ||
282 | {"alternative",no_argument,NULL,'a'}, | ||
283 | {"name",required_argument,NULL,'n'}, | ||
284 | {NULL,0,NULL,0} | ||
285 | }; | ||
286 | |||
287 | /* Stop scanning as soon as a non-option argument is found! */ | ||
288 | static const char shortopts[]="+ao:l:n:qQs:Tu"; | ||
289 | |||
290 | |||
291 | int getopt_main(int argc, char *argv[]) | ||
292 | { | ||
293 | const char *optstr = NULL; | ||
294 | char *name = NULL; | ||
295 | int opt; | ||
296 | int compatible=0; | ||
297 | |||
298 | init_longopt(); | ||
299 | |||
300 | if (getenv("GETOPT_COMPATIBLE")) | ||
301 | compatible=1; | ||
302 | |||
303 | if (argc == 1) { | ||
304 | if (compatible) { | ||
305 | /* For some reason, the original getopt gave no error | ||
306 | when there were no arguments. */ | ||
307 | printf(" --\n"); | ||
308 | return 0; | ||
309 | } else | ||
310 | bb_error_msg_and_die("missing optstring argument"); | ||
311 | } | ||
312 | |||
313 | if (argv[1][0] != '-' || compatible) { | ||
314 | char *s; | ||
315 | |||
316 | quote=0; | ||
317 | s=xmalloc(strlen(argv[1])+1); | ||
318 | strcpy(s,argv[1]+strspn(argv[1],"-+")); | ||
319 | argv[1]=argv[0]; | ||
320 | return generate_output(argv+1,argc-1,s,long_options); | ||
321 | } | ||
322 | |||
323 | while ((opt = getopt_long(argc,argv,shortopts,longopts,NULL)) != EOF) | ||
324 | switch (opt) { | ||
325 | case 'a': | ||
326 | alternative=1; | ||
327 | break; | ||
328 | case 'o': | ||
329 | optstr = optarg; | ||
330 | break; | ||
331 | case 'l': | ||
332 | add_long_options(optarg); | ||
333 | break; | ||
334 | case 'n': | ||
335 | name = optarg; | ||
336 | break; | ||
337 | case 'q': | ||
338 | quiet_errors=1; | ||
339 | break; | ||
340 | case 'Q': | ||
341 | quiet_output=1; | ||
342 | break; | ||
343 | case 's': | ||
344 | set_shell(optarg); | ||
345 | break; | ||
346 | case 'T': | ||
347 | return 4; | ||
348 | case 'u': | ||
349 | quote=0; | ||
350 | break; | ||
351 | default: | ||
352 | bb_show_usage(); | ||
353 | } | ||
354 | |||
355 | if (!optstr) { | ||
356 | if (optind >= argc) | ||
357 | bb_error_msg_and_die("missing optstring argument"); | ||
358 | else optstr=argv[optind++]; | ||
359 | } | ||
360 | if (name) | ||
361 | argv[optind-1]=name; | ||
362 | else | ||
363 | argv[optind-1]=argv[0]; | ||
364 | return generate_output(argv+optind-1,argc-optind+1,optstr,long_options); | ||
365 | } | ||
diff --git a/util-linux/hexdump.c b/util-linux/hexdump.c new file mode 100644 index 000000000..5cb245feb --- /dev/null +++ b/util-linux/hexdump.c | |||
@@ -0,0 +1,101 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * hexdump implementation for busybox | ||
4 | * Based on code from util-linux v 2.11l | ||
5 | * | ||
6 | * Copyright (c) 1989 | ||
7 | * The Regents of the University of California. All rights reserved. | ||
8 | * | ||
9 | * Licensed under GPLv2 or later, see file License in this tarball for details. | ||
10 | */ | ||
11 | |||
12 | #include "busybox.h" | ||
13 | #include <getopt.h> | ||
14 | #include "dump.h" | ||
15 | |||
16 | static void bb_dump_addfile(char *name) | ||
17 | { | ||
18 | char *p; | ||
19 | FILE *fp; | ||
20 | char *buf; | ||
21 | |||
22 | fp = xfopen(name, "r"); | ||
23 | |||
24 | while ((buf = xmalloc_getline(fp)) != NULL) { | ||
25 | p = skip_whitespace(buf); | ||
26 | |||
27 | if (*p && (*p != '#')) { | ||
28 | bb_dump_add(p); | ||
29 | } | ||
30 | free(buf); | ||
31 | } | ||
32 | fclose(fp); | ||
33 | } | ||
34 | |||
35 | static const char * const add_strings[] = { | ||
36 | "\"%07.7_ax \" 16/1 \"%03o \" \"\\n\"", /* b */ | ||
37 | "\"%07.7_ax \" 16/1 \"%3_c \" \"\\n\"", /* c */ | ||
38 | "\"%07.7_ax \" 8/2 \" %05u \" \"\\n\"", /* d */ | ||
39 | "\"%07.7_ax \" 8/2 \" %06o \" \"\\n\"", /* o */ | ||
40 | "\"%07.7_ax \" 8/2 \" %04x \" \"\\n\"", /* x */ | ||
41 | }; | ||
42 | |||
43 | static const char add_first[] = "\"%07.7_Ax\n\""; | ||
44 | |||
45 | static const char hexdump_opts[] = "bcdoxCe:f:n:s:v"; | ||
46 | |||
47 | static const struct suffix_mult suffixes[] = { | ||
48 | {"b", 512 }, | ||
49 | {"k", 1024 }, | ||
50 | {"m", 1024*1024 }, | ||
51 | {NULL, 0 } | ||
52 | }; | ||
53 | |||
54 | int hexdump_main(int argc, char **argv) | ||
55 | { | ||
56 | const char *p; | ||
57 | int ch; | ||
58 | |||
59 | bb_dump_vflag = FIRST; | ||
60 | bb_dump_length = -1; | ||
61 | |||
62 | while ((ch = getopt(argc, argv, hexdump_opts)) > 0) { | ||
63 | p = strchr(hexdump_opts, ch); | ||
64 | if (!p) | ||
65 | bb_show_usage(); | ||
66 | if ((p - hexdump_opts) < 5) { | ||
67 | bb_dump_add(add_first); | ||
68 | bb_dump_add(add_strings[(int)(p - hexdump_opts)]); | ||
69 | } else if (ch == 'C') { | ||
70 | bb_dump_add("\"%08.8_Ax\n\""); | ||
71 | bb_dump_add("\"%08.8_ax \" 8/1 \"%02x \" \" \" 8/1 \"%02x \" "); | ||
72 | bb_dump_add("\" |\" 16/1 \"%_p\" \"|\\n\""); | ||
73 | } else { | ||
74 | /* Save a little bit of space below by omitting the 'else's. */ | ||
75 | if (ch == 'e') { | ||
76 | bb_dump_add(optarg); | ||
77 | } /* else */ | ||
78 | if (ch == 'f') { | ||
79 | bb_dump_addfile(optarg); | ||
80 | } /* else */ | ||
81 | if (ch == 'n') { | ||
82 | bb_dump_length = xatoi_u(optarg); | ||
83 | } /* else */ | ||
84 | if (ch == 's') { | ||
85 | bb_dump_skip = xatoul_range_sfx(optarg, 0, LONG_MAX, suffixes); | ||
86 | } /* else */ | ||
87 | if (ch == 'v') { | ||
88 | bb_dump_vflag = ALL; | ||
89 | } | ||
90 | } | ||
91 | } | ||
92 | |||
93 | if (!bb_dump_fshead) { | ||
94 | bb_dump_add(add_first); | ||
95 | bb_dump_add("\"%07.7_ax \" 8/2 \"%04x \" \"\\n\""); | ||
96 | } | ||
97 | |||
98 | argv += optind; | ||
99 | |||
100 | return bb_dump_dump(argv); | ||
101 | } | ||
diff --git a/util-linux/hwclock.c b/util-linux/hwclock.c new file mode 100644 index 000000000..8fcd8c99c --- /dev/null +++ b/util-linux/hwclock.c | |||
@@ -0,0 +1,213 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Mini hwclock implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 2002 Robert Griebl <griebl@gmx.de> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | |||
11 | #include <sys/ioctl.h> | ||
12 | #include <sys/utsname.h> | ||
13 | #include <getopt.h> | ||
14 | #include "busybox.h" | ||
15 | |||
16 | /* Copied from linux/rtc.h to eliminate the kernel dependency */ | ||
17 | struct linux_rtc_time { | ||
18 | int tm_sec; | ||
19 | int tm_min; | ||
20 | int tm_hour; | ||
21 | int tm_mday; | ||
22 | int tm_mon; | ||
23 | int tm_year; | ||
24 | int tm_wday; | ||
25 | int tm_yday; | ||
26 | int tm_isdst; | ||
27 | }; | ||
28 | |||
29 | #define RTC_SET_TIME _IOW('p', 0x0a, struct linux_rtc_time) /* Set RTC time */ | ||
30 | #define RTC_RD_TIME _IOR('p', 0x09, struct linux_rtc_time) /* Read RTC time */ | ||
31 | |||
32 | #if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS | ||
33 | # ifndef _GNU_SOURCE | ||
34 | # define _GNU_SOURCE | ||
35 | # endif | ||
36 | #endif | ||
37 | |||
38 | static int xopen_rtc(int flags) | ||
39 | { | ||
40 | int rtc; | ||
41 | rtc = open("/dev/rtc", flags); | ||
42 | if (rtc < 0) { | ||
43 | rtc = open("/dev/misc/rtc", flags); | ||
44 | if (rtc < 0) | ||
45 | bb_perror_msg_and_die("cannot access RTC"); | ||
46 | } | ||
47 | return rtc; | ||
48 | } | ||
49 | |||
50 | static time_t read_rtc(int utc) | ||
51 | { | ||
52 | struct tm tm; | ||
53 | char *oldtz = 0; | ||
54 | time_t t = 0; | ||
55 | int rtc = xopen_rtc(O_RDONLY); | ||
56 | |||
57 | memset(&tm, 0, sizeof(struct tm)); | ||
58 | if (ioctl(rtc, RTC_RD_TIME, &tm) < 0 ) | ||
59 | bb_perror_msg_and_die("cannot read time from RTC"); | ||
60 | tm.tm_isdst = -1; /* not known */ | ||
61 | |||
62 | close(rtc); | ||
63 | |||
64 | if (utc) { | ||
65 | oldtz = getenv("TZ"); | ||
66 | setenv("TZ", "UTC 0", 1); | ||
67 | tzset(); | ||
68 | } | ||
69 | |||
70 | t = mktime(&tm); | ||
71 | |||
72 | if (utc) { | ||
73 | if (oldtz) | ||
74 | setenv("TZ", oldtz, 1); | ||
75 | else | ||
76 | unsetenv("TZ"); | ||
77 | tzset(); | ||
78 | } | ||
79 | return t; | ||
80 | } | ||
81 | |||
82 | static void write_rtc(time_t t, int utc) | ||
83 | { | ||
84 | struct tm tm; | ||
85 | int rtc = xopen_rtc(O_WRONLY); | ||
86 | |||
87 | tm = *(utc ? gmtime(&t) : localtime(&t)); | ||
88 | tm.tm_isdst = 0; | ||
89 | |||
90 | if (ioctl(rtc, RTC_SET_TIME, &tm) < 0) | ||
91 | bb_perror_msg_and_die("cannot set the RTC time"); | ||
92 | |||
93 | close(rtc); | ||
94 | } | ||
95 | |||
96 | static int show_clock(int utc) | ||
97 | { | ||
98 | struct tm *ptm; | ||
99 | time_t t; | ||
100 | RESERVE_CONFIG_BUFFER(buffer, 64); | ||
101 | |||
102 | t = read_rtc(utc); | ||
103 | ptm = localtime(&t); /* Sets 'tzname[]' */ | ||
104 | |||
105 | safe_strncpy(buffer, ctime(&t), 64); | ||
106 | if (buffer[0]) | ||
107 | buffer[strlen(buffer) - 1] = 0; | ||
108 | |||
109 | //printf("%s %.6f seconds %s\n", buffer, 0.0, utc ? "" : (ptm->tm_isdst ? tzname[1] : tzname[0])); | ||
110 | printf( "%s %.6f seconds\n", buffer, 0.0); | ||
111 | RELEASE_CONFIG_BUFFER(buffer); | ||
112 | |||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | static int to_sys_clock(int utc) | ||
117 | { | ||
118 | struct timeval tv = { 0, 0 }; | ||
119 | const struct timezone tz = { timezone/60 - 60*daylight, 0 }; | ||
120 | |||
121 | tv.tv_sec = read_rtc(utc); | ||
122 | |||
123 | if (settimeofday(&tv, &tz)) | ||
124 | bb_perror_msg_and_die("settimeofday() failed"); | ||
125 | |||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | static int from_sys_clock(int utc) | ||
130 | { | ||
131 | struct timeval tv = { 0, 0 }; | ||
132 | struct timezone tz = { 0, 0 }; | ||
133 | |||
134 | if (gettimeofday(&tv, &tz)) | ||
135 | bb_perror_msg_and_die("gettimeofday() failed"); | ||
136 | |||
137 | write_rtc(tv.tv_sec, utc); | ||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | #ifdef CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS | ||
142 | # define ADJTIME_PATH "/var/lib/hwclock/adjtime" | ||
143 | #else | ||
144 | # define ADJTIME_PATH "/etc/adjtime" | ||
145 | #endif | ||
146 | static int check_utc(void) | ||
147 | { | ||
148 | int utc = 0; | ||
149 | FILE *f = fopen(ADJTIME_PATH, "r"); | ||
150 | |||
151 | if (f) { | ||
152 | RESERVE_CONFIG_BUFFER(buffer, 128); | ||
153 | |||
154 | while (fgets(buffer, sizeof(buffer), f)) { | ||
155 | int len = strlen(buffer); | ||
156 | |||
157 | while (len && isspace(buffer[len - 1])) | ||
158 | len--; | ||
159 | |||
160 | buffer[len] = 0; | ||
161 | |||
162 | if (strncmp(buffer, "UTC", 3) == 0 ) { | ||
163 | utc = 1; | ||
164 | break; | ||
165 | } | ||
166 | } | ||
167 | fclose(f); | ||
168 | RELEASE_CONFIG_BUFFER(buffer); | ||
169 | } | ||
170 | return utc; | ||
171 | } | ||
172 | |||
173 | #define HWCLOCK_OPT_LOCALTIME 0x01 | ||
174 | #define HWCLOCK_OPT_UTC 0x02 | ||
175 | #define HWCLOCK_OPT_SHOW 0x04 | ||
176 | #define HWCLOCK_OPT_HCTOSYS 0x08 | ||
177 | #define HWCLOCK_OPT_SYSTOHC 0x10 | ||
178 | |||
179 | int hwclock_main(int argc, char **argv ) | ||
180 | { | ||
181 | unsigned opt; | ||
182 | int utc; | ||
183 | |||
184 | #if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS | ||
185 | static const struct option hwclock_long_options[] = { | ||
186 | { "localtime", 0, 0, 'l' }, | ||
187 | { "utc", 0, 0, 'u' }, | ||
188 | { "show", 0, 0, 'r' }, | ||
189 | { "hctosys", 0, 0, 's' }, | ||
190 | { "systohc", 0, 0, 'w' }, | ||
191 | { 0, 0, 0, 0 } | ||
192 | }; | ||
193 | applet_long_options = hwclock_long_options; | ||
194 | #endif | ||
195 | opt_complementary = "?:r--ws:w--rs:s--wr:l--u:u--l"; | ||
196 | opt = getopt32(argc, argv, "lursw"); | ||
197 | |||
198 | /* If -u or -l wasn't given check if we are using utc */ | ||
199 | if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME)) | ||
200 | utc = opt & HWCLOCK_OPT_UTC; | ||
201 | else | ||
202 | utc = check_utc(); | ||
203 | |||
204 | if (opt & HWCLOCK_OPT_HCTOSYS) { | ||
205 | return to_sys_clock(utc); | ||
206 | } | ||
207 | else if (opt & HWCLOCK_OPT_SYSTOHC) { | ||
208 | return from_sys_clock(utc); | ||
209 | } else { | ||
210 | /* default HWCLOCK_OPT_SHOW */ | ||
211 | return show_clock(utc); | ||
212 | } | ||
213 | } | ||
diff --git a/util-linux/ipcrm.c b/util-linux/ipcrm.c new file mode 100644 index 000000000..507e58fe3 --- /dev/null +++ b/util-linux/ipcrm.c | |||
@@ -0,0 +1,217 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * ipcrm.c - utility to allow removal of IPC objects and data structures. | ||
4 | * | ||
5 | * 01 Sept 2004 - Rodney Radford <rradford@mindspring.com> | ||
6 | * Adapted for busybox from util-linux-2.12a. | ||
7 | * | ||
8 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
9 | */ | ||
10 | |||
11 | #include "busybox.h" | ||
12 | |||
13 | /* X/OPEN tells us to use <sys/{types,ipc,sem}.h> for semctl() */ | ||
14 | /* X/OPEN tells us to use <sys/{types,ipc,msg}.h> for msgctl() */ | ||
15 | #include <sys/ipc.h> | ||
16 | #include <sys/shm.h> | ||
17 | #include <sys/msg.h> | ||
18 | #include <sys/sem.h> | ||
19 | |||
20 | #if defined (__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED) | ||
21 | /* union semun is defined by including <sys/sem.h> */ | ||
22 | #else | ||
23 | /* according to X/OPEN we have to define it ourselves */ | ||
24 | union semun { | ||
25 | int val; | ||
26 | struct semid_ds *buf; | ||
27 | unsigned short int *array; | ||
28 | struct seminfo *__buf; | ||
29 | }; | ||
30 | #endif | ||
31 | |||
32 | #ifndef CONFIG_IPCRM_DROP_LEGACY | ||
33 | |||
34 | typedef enum type_id { | ||
35 | SHM, | ||
36 | SEM, | ||
37 | MSG | ||
38 | } type_id; | ||
39 | |||
40 | static int remove_ids(type_id type, int argc, char **argv) | ||
41 | { | ||
42 | unsigned long id; | ||
43 | int ret = 0; /* silence gcc */ | ||
44 | int nb_errors = 0; | ||
45 | union semun arg; | ||
46 | |||
47 | arg.val = 0; | ||
48 | |||
49 | while (argc) { | ||
50 | id = bb_strtoul(argv[0], NULL, 10); | ||
51 | if (errno || id > INT_MAX) { | ||
52 | bb_error_msg("invalid id: %s", argv[0]); | ||
53 | nb_errors++; | ||
54 | } else { | ||
55 | if (type == SEM) | ||
56 | ret = semctl(id, 0, IPC_RMID, arg); | ||
57 | else if (type == MSG) | ||
58 | ret = msgctl(id, IPC_RMID, NULL); | ||
59 | else if (type == SHM) | ||
60 | ret = shmctl(id, IPC_RMID, NULL); | ||
61 | |||
62 | if (ret) { | ||
63 | bb_perror_msg("cannot remove id %s", argv[0]); | ||
64 | nb_errors++; | ||
65 | } | ||
66 | } | ||
67 | argc--; | ||
68 | argv++; | ||
69 | } | ||
70 | |||
71 | return nb_errors; | ||
72 | } | ||
73 | #endif /* #ifndef CONFIG_IPCRM_DROP_LEGACY */ | ||
74 | |||
75 | |||
76 | int ipcrm_main(int argc, char **argv) | ||
77 | { | ||
78 | int c; | ||
79 | int error = 0; | ||
80 | |||
81 | /* if the command is executed without parameters, do nothing */ | ||
82 | if (argc == 1) | ||
83 | return 0; | ||
84 | #ifndef CONFIG_IPCRM_DROP_LEGACY | ||
85 | /* check to see if the command is being invoked in the old way if so | ||
86 | then run the old code. Valid commands are msg, shm, sem. */ | ||
87 | { | ||
88 | type_id what = 0; /* silence gcc */ | ||
89 | char w; | ||
90 | |||
91 | w=argv[1][0]; | ||
92 | if ( ((w == 'm' && argv[1][1] == 's' && argv[1][2] == 'g') | ||
93 | || (argv[1][0] == 's' | ||
94 | && ((w=argv[1][1]) == 'h' || w == 'e') | ||
95 | && argv[1][2] == 'm') | ||
96 | ) && argv[1][3] == '\0' | ||
97 | ) { | ||
98 | |||
99 | if (argc < 3) | ||
100 | bb_show_usage(); | ||
101 | |||
102 | if (w == 'h') | ||
103 | what = SHM; | ||
104 | else if (w == 'm') | ||
105 | what = MSG; | ||
106 | else if (w == 'e') | ||
107 | what = SEM; | ||
108 | |||
109 | if (remove_ids(what, argc-2, &argv[2])) | ||
110 | fflush_stdout_and_exit(1); | ||
111 | printf("resource(s) deleted\n"); | ||
112 | return 0; | ||
113 | } | ||
114 | } | ||
115 | #endif /* #ifndef CONFIG_IPCRM_DROP_LEGACY */ | ||
116 | |||
117 | /* process new syntax to conform with SYSV ipcrm */ | ||
118 | while ((c = getopt(argc, argv, "q:m:s:Q:M:S:h?")) != -1) { | ||
119 | int result; | ||
120 | int id = 0; | ||
121 | int iskey = (isupper)(c); | ||
122 | |||
123 | /* needed to delete semaphores */ | ||
124 | union semun arg; | ||
125 | |||
126 | arg.val = 0; | ||
127 | |||
128 | if ((c == '?') || (c == 'h')) { | ||
129 | bb_show_usage(); | ||
130 | } | ||
131 | |||
132 | /* we don't need case information any more */ | ||
133 | c = tolower(c); | ||
134 | |||
135 | /* make sure the option is in range: allowed are q, m, s */ | ||
136 | if (c != 'q' && c != 'm' && c != 's') { | ||
137 | bb_show_usage(); | ||
138 | } | ||
139 | |||
140 | if (iskey) { | ||
141 | /* keys are in hex or decimal */ | ||
142 | key_t key = xstrtoul(optarg, 0); | ||
143 | |||
144 | if (key == IPC_PRIVATE) { | ||
145 | error++; | ||
146 | bb_error_msg("illegal key (%s)", optarg); | ||
147 | continue; | ||
148 | } | ||
149 | |||
150 | /* convert key to id */ | ||
151 | id = ((c == 'q') ? msgget(key, 0) : | ||
152 | (c == 'm') ? shmget(key, 0, 0) : semget(key, 0, 0)); | ||
153 | |||
154 | if (id < 0) { | ||
155 | char *errmsg; | ||
156 | const char * const what = "key"; | ||
157 | |||
158 | error++; | ||
159 | switch (errno) { | ||
160 | case EACCES: | ||
161 | errmsg = "permission denied for"; | ||
162 | break; | ||
163 | case EIDRM: | ||
164 | errmsg = "already removed"; | ||
165 | break; | ||
166 | case ENOENT: | ||
167 | errmsg = "invalid"; | ||
168 | break; | ||
169 | default: | ||
170 | errmsg = "unknown error in"; | ||
171 | break; | ||
172 | } | ||
173 | bb_error_msg("%s %s (%s)", errmsg, what, optarg); | ||
174 | continue; | ||
175 | } | ||
176 | } else { | ||
177 | /* ids are in decimal */ | ||
178 | id = xatoul(optarg); | ||
179 | } | ||
180 | |||
181 | result = ((c == 'q') ? msgctl(id, IPC_RMID, NULL) : | ||
182 | (c == 'm') ? shmctl(id, IPC_RMID, NULL) : | ||
183 | semctl(id, 0, IPC_RMID, arg)); | ||
184 | |||
185 | if (result) { | ||
186 | char *errmsg; | ||
187 | const char * const what = iskey ? "key" : "id"; | ||
188 | |||
189 | error++; | ||
190 | switch (errno) { | ||
191 | case EACCES: | ||
192 | case EPERM: | ||
193 | errmsg = "permission denied for"; | ||
194 | break; | ||
195 | case EINVAL: | ||
196 | errmsg = "invalid"; | ||
197 | break; | ||
198 | case EIDRM: | ||
199 | errmsg = "already removed"; | ||
200 | break; | ||
201 | default: | ||
202 | errmsg = "unknown error in"; | ||
203 | break; | ||
204 | } | ||
205 | bb_error_msg("%s %s (%s)", errmsg, what, optarg); | ||
206 | continue; | ||
207 | } | ||
208 | } | ||
209 | |||
210 | /* print usage if we still have some arguments left over */ | ||
211 | if (optind != argc) { | ||
212 | bb_show_usage(); | ||
213 | } | ||
214 | |||
215 | /* exit value reflects the number of errors encountered */ | ||
216 | return error; | ||
217 | } | ||
diff --git a/util-linux/ipcs.c b/util-linux/ipcs.c new file mode 100644 index 000000000..b81d07d6d --- /dev/null +++ b/util-linux/ipcs.c | |||
@@ -0,0 +1,630 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * ipcs.c -- provides information on allocated ipc resources. | ||
4 | * | ||
5 | * 01 Sept 2004 - Rodney Radford <rradford@mindspring.com> | ||
6 | * Adapted for busybox from util-linux-2.12a. | ||
7 | * | ||
8 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
9 | */ | ||
10 | |||
11 | #include "busybox.h" | ||
12 | #include <errno.h> | ||
13 | #include <time.h> | ||
14 | #include <pwd.h> | ||
15 | #include <grp.h> | ||
16 | |||
17 | /* X/OPEN tells us to use <sys/{types,ipc,sem}.h> for semctl() */ | ||
18 | /* X/OPEN tells us to use <sys/{types,ipc,msg}.h> for msgctl() */ | ||
19 | /* X/OPEN tells us to use <sys/{types,ipc,shm}.h> for shmctl() */ | ||
20 | #include <sys/types.h> | ||
21 | #include <sys/ipc.h> | ||
22 | #include <sys/sem.h> | ||
23 | #include <sys/msg.h> | ||
24 | #include <sys/shm.h> | ||
25 | |||
26 | |||
27 | |||
28 | /*-------------------------------------------------------------------*/ | ||
29 | /* SHM_DEST and SHM_LOCKED are defined in kernel headers, | ||
30 | but inside #ifdef __KERNEL__ ... #endif */ | ||
31 | #ifndef SHM_DEST | ||
32 | /* shm_mode upper byte flags */ | ||
33 | #define SHM_DEST 01000 /* segment will be destroyed on last detach */ | ||
34 | #define SHM_LOCKED 02000 /* segment will not be swapped */ | ||
35 | #endif | ||
36 | |||
37 | /* For older kernels the same holds for the defines below */ | ||
38 | #ifndef MSG_STAT | ||
39 | #define MSG_STAT 11 | ||
40 | #define MSG_INFO 12 | ||
41 | #endif | ||
42 | |||
43 | #ifndef SHM_STAT | ||
44 | #define SHM_STAT 13 | ||
45 | #define SHM_INFO 14 | ||
46 | struct shm_info { | ||
47 | int used_ids; | ||
48 | ulong shm_tot; /* total allocated shm */ | ||
49 | ulong shm_rss; /* total resident shm */ | ||
50 | ulong shm_swp; /* total swapped shm */ | ||
51 | ulong swap_attempts; | ||
52 | ulong swap_successes; | ||
53 | }; | ||
54 | #endif | ||
55 | |||
56 | #ifndef SEM_STAT | ||
57 | #define SEM_STAT 18 | ||
58 | #define SEM_INFO 19 | ||
59 | #endif | ||
60 | |||
61 | /* Some versions of libc only define IPC_INFO when __USE_GNU is defined. */ | ||
62 | #ifndef IPC_INFO | ||
63 | #define IPC_INFO 3 | ||
64 | #endif | ||
65 | /*-------------------------------------------------------------------*/ | ||
66 | |||
67 | /* The last arg of semctl is a union semun, but where is it defined? | ||
68 | X/OPEN tells us to define it ourselves, but until recently | ||
69 | Linux include files would also define it. */ | ||
70 | #if defined (__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED) | ||
71 | /* union semun is defined by including <sys/sem.h> */ | ||
72 | #else | ||
73 | /* according to X/OPEN we have to define it ourselves */ | ||
74 | union semun { | ||
75 | int val; | ||
76 | struct semid_ds *buf; | ||
77 | unsigned short int *array; | ||
78 | struct seminfo *__buf; | ||
79 | }; | ||
80 | #endif | ||
81 | |||
82 | /* X/OPEN (Jan 1987) does not define fields key, seq in struct ipc_perm; | ||
83 | libc 4/5 does not mention struct ipc_term at all, but includes | ||
84 | <linux/ipc.h>, which defines a struct ipc_perm with such fields. | ||
85 | glibc-1.09 has no support for sysv ipc. | ||
86 | glibc 2 uses __key, __seq */ | ||
87 | #if defined (__GNU_LIBRARY__) && __GNU_LIBRARY__ > 1 | ||
88 | #define KEY __key | ||
89 | #else | ||
90 | #define KEY key | ||
91 | #endif | ||
92 | |||
93 | #define LIMITS 1 | ||
94 | #define STATUS 2 | ||
95 | #define CREATOR 3 | ||
96 | #define TIME 4 | ||
97 | #define PID 5 | ||
98 | |||
99 | static char format; | ||
100 | |||
101 | static void print_perms(int id, struct ipc_perm *ipcp) | ||
102 | { | ||
103 | struct passwd *pw; | ||
104 | struct group *gr; | ||
105 | |||
106 | printf("%-10d %-10o", id, ipcp->mode & 0777); | ||
107 | |||
108 | if ((pw = getpwuid(ipcp->cuid))) | ||
109 | printf(" %-10s", pw->pw_name); | ||
110 | else | ||
111 | printf(" %-10d", ipcp->cuid); | ||
112 | if ((gr = getgrgid(ipcp->cgid))) | ||
113 | printf(" %-10s", gr->gr_name); | ||
114 | else | ||
115 | printf(" %-10d", ipcp->cgid); | ||
116 | |||
117 | if ((pw = getpwuid(ipcp->uid))) | ||
118 | printf(" %-10s", pw->pw_name); | ||
119 | else | ||
120 | printf(" %-10d", ipcp->uid); | ||
121 | if ((gr = getgrgid(ipcp->gid))) | ||
122 | printf(" %-10s\n", gr->gr_name); | ||
123 | else | ||
124 | printf(" %-10d\n", ipcp->gid); | ||
125 | } | ||
126 | |||
127 | |||
128 | static void do_shm(void) | ||
129 | { | ||
130 | int maxid, shmid, id; | ||
131 | struct shmid_ds shmseg; | ||
132 | struct shm_info shm_info; | ||
133 | struct shminfo shminfo; | ||
134 | struct ipc_perm *ipcp = &shmseg.shm_perm; | ||
135 | struct passwd *pw; | ||
136 | |||
137 | maxid = shmctl(0, SHM_INFO, (struct shmid_ds *) (void *) &shm_info); | ||
138 | if (maxid < 0) { | ||
139 | printf("kernel not configured for %s\n", "shared memory"); | ||
140 | return; | ||
141 | } | ||
142 | |||
143 | switch (format) { | ||
144 | case LIMITS: | ||
145 | printf("------ Shared Memory %s --------\n", "Limits"); | ||
146 | if ((shmctl(0, IPC_INFO, (struct shmid_ds *) (void *) &shminfo)) < 0) | ||
147 | return; | ||
148 | /* glibc 2.1.3 and all earlier libc's have ints as fields | ||
149 | of struct shminfo; glibc 2.1.91 has unsigned long; ach */ | ||
150 | printf("max number of segments = %lu\n" | ||
151 | "max seg size (kbytes) = %lu\n" | ||
152 | "max total shared memory (pages) = %lu\n" | ||
153 | "min seg size (bytes) = %lu\n", | ||
154 | (unsigned long) shminfo.shmmni, | ||
155 | (unsigned long) (shminfo.shmmax >> 10), | ||
156 | (unsigned long) shminfo.shmall, | ||
157 | (unsigned long) shminfo.shmmin); | ||
158 | return; | ||
159 | |||
160 | case STATUS: | ||
161 | printf("------ Shared Memory %s --------\n", "Status"); | ||
162 | printf( "segments allocated %d\n" | ||
163 | "pages allocated %ld\n" | ||
164 | "pages resident %ld\n" | ||
165 | "pages swapped %ld\n" | ||
166 | "Swap performance: %ld attempts\t%ld successes\n", | ||
167 | shm_info.used_ids, | ||
168 | shm_info.shm_tot, | ||
169 | shm_info.shm_rss, | ||
170 | shm_info.shm_swp, | ||
171 | shm_info.swap_attempts, shm_info.swap_successes); | ||
172 | return; | ||
173 | |||
174 | case CREATOR: | ||
175 | printf("------ Shared Memory %s --------\n", "Segment Creators/Owners"); | ||
176 | printf( "%-10s %-10s %-10s %-10s %-10s %-10s\n", | ||
177 | "shmid", "perms", "cuid", "cgid", "uid", "gid"); | ||
178 | break; | ||
179 | |||
180 | case TIME: | ||
181 | printf("------ Shared Memory %s --------\n", "Attach/Detach/Change Times"); | ||
182 | printf( "%-10s %-10s %-20s %-20s %-20s\n", | ||
183 | "shmid", "owner", "attached", "detached", "changed"); | ||
184 | break; | ||
185 | |||
186 | case PID: | ||
187 | printf("------ Shared Memory %s --------\n", "Creator/Last-op"); | ||
188 | printf( "%-10s %-10s %-10s %-10s\n", | ||
189 | "shmid", "owner", "cpid", "lpid"); | ||
190 | break; | ||
191 | |||
192 | default: | ||
193 | printf("------ Shared Memory %s --------\n", "Segments"); | ||
194 | printf( "%-10s %-10s %-10s %-10s %-10s %-10s %-12s\n", | ||
195 | "key", "shmid", "owner", "perms", "bytes", "nattch", | ||
196 | "status"); | ||
197 | break; | ||
198 | } | ||
199 | |||
200 | for (id = 0; id <= maxid; id++) { | ||
201 | shmid = shmctl(id, SHM_STAT, &shmseg); | ||
202 | if (shmid < 0) | ||
203 | continue; | ||
204 | if (format == CREATOR) { | ||
205 | print_perms(shmid, ipcp); | ||
206 | continue; | ||
207 | } | ||
208 | pw = getpwuid(ipcp->uid); | ||
209 | switch (format) { | ||
210 | case TIME: | ||
211 | if (pw) | ||
212 | printf("%-10d %-10.10s", shmid, pw->pw_name); | ||
213 | else | ||
214 | printf("%-10d %-10d", shmid, ipcp->uid); | ||
215 | /* ctime uses static buffer: use separate calls */ | ||
216 | printf(" %-20.16s", shmseg.shm_atime | ||
217 | ? ctime(&shmseg.shm_atime) + 4 : "Not set"); | ||
218 | printf(" %-20.16s", shmseg.shm_dtime | ||
219 | ? ctime(&shmseg.shm_dtime) + 4 : "Not set"); | ||
220 | printf(" %-20.16s\n", shmseg.shm_ctime | ||
221 | ? ctime(&shmseg.shm_ctime) + 4 : "Not set"); | ||
222 | break; | ||
223 | case PID: | ||
224 | if (pw) | ||
225 | printf("%-10d %-10.10s", shmid, pw->pw_name); | ||
226 | else | ||
227 | printf("%-10d %-10d", shmid, ipcp->uid); | ||
228 | printf(" %-10d %-10d\n", shmseg.shm_cpid, shmseg.shm_lpid); | ||
229 | break; | ||
230 | |||
231 | default: | ||
232 | printf("0x%08x ", ipcp->KEY); | ||
233 | if (pw) | ||
234 | printf("%-10d %-10.10s", shmid, pw->pw_name); | ||
235 | else | ||
236 | printf("%-10d %-10d", shmid, ipcp->uid); | ||
237 | printf(" %-10o %-10lu %-10ld %-6s %-6s\n", ipcp->mode & 0777, | ||
238 | /* | ||
239 | * earlier: int, Austin has size_t | ||
240 | */ | ||
241 | (unsigned long) shmseg.shm_segsz, | ||
242 | /* | ||
243 | * glibc-2.1.3 and earlier has unsigned short; | ||
244 | * Austin has shmatt_t | ||
245 | */ | ||
246 | (long) shmseg.shm_nattch, | ||
247 | ipcp->mode & SHM_DEST ? "dest" : " ", | ||
248 | ipcp->mode & SHM_LOCKED ? "locked" : " "); | ||
249 | break; | ||
250 | } | ||
251 | } | ||
252 | } | ||
253 | |||
254 | |||
255 | static void do_sem(void) | ||
256 | { | ||
257 | int maxid, semid, id; | ||
258 | struct semid_ds semary; | ||
259 | struct seminfo seminfo; | ||
260 | struct ipc_perm *ipcp = &semary.sem_perm; | ||
261 | struct passwd *pw; | ||
262 | union semun arg; | ||
263 | |||
264 | arg.array = (ushort *) (void *) &seminfo; | ||
265 | maxid = semctl(0, 0, SEM_INFO, arg); | ||
266 | if (maxid < 0) { | ||
267 | printf("kernel not configured for %s\n", "semaphores"); | ||
268 | return; | ||
269 | } | ||
270 | |||
271 | switch (format) { | ||
272 | case LIMITS: | ||
273 | printf("------ Semaphore %s --------\n", "Limits"); | ||
274 | arg.array = (ushort *) (void *) &seminfo; /* damn union */ | ||
275 | if ((semctl(0, 0, IPC_INFO, arg)) < 0) | ||
276 | return; | ||
277 | printf("max number of arrays = %d\n" | ||
278 | "max semaphores per array = %d\n" | ||
279 | "max semaphores system wide = %d\n" | ||
280 | "max ops per semop call = %d\n" | ||
281 | "semaphore max value = %d\n", | ||
282 | seminfo.semmni, | ||
283 | seminfo.semmsl, | ||
284 | seminfo.semmns, seminfo.semopm, seminfo.semvmx); | ||
285 | return; | ||
286 | |||
287 | case STATUS: | ||
288 | printf("------ Semaphore %s --------\n", "Status"); | ||
289 | printf( "used arrays = %d\n" | ||
290 | "allocated semaphores = %d\n", | ||
291 | seminfo.semusz, seminfo.semaem); | ||
292 | return; | ||
293 | |||
294 | case CREATOR: | ||
295 | printf("------ Semaphore %s --------\n", "Arrays Creators/Owners"); | ||
296 | printf( "%-10s %-10s %-10s %-10s %-10s %-10s\n", | ||
297 | "semid", "perms", "cuid", "cgid", "uid", "gid"); | ||
298 | break; | ||
299 | |||
300 | case TIME: | ||
301 | printf("------ Shared Memory %s --------\n", "Operation/Change Times"); | ||
302 | printf( "%-8s %-10s %-26.24s %-26.24s\n", | ||
303 | "shmid", "owner", "last-op", "last-changed"); | ||
304 | break; | ||
305 | |||
306 | case PID: | ||
307 | break; | ||
308 | |||
309 | default: | ||
310 | printf("------ Semaphore %s --------\n", "Arrays"); | ||
311 | printf( "%-10s %-10s %-10s %-10s %-10s\n", | ||
312 | "key", "semid", "owner", "perms", "nsems"); | ||
313 | break; | ||
314 | } | ||
315 | |||
316 | for (id = 0; id <= maxid; id++) { | ||
317 | arg.buf = (struct semid_ds *) &semary; | ||
318 | semid = semctl(id, 0, SEM_STAT, arg); | ||
319 | if (semid < 0) | ||
320 | continue; | ||
321 | if (format == CREATOR) { | ||
322 | print_perms(semid, ipcp); | ||
323 | continue; | ||
324 | } | ||
325 | pw = getpwuid(ipcp->uid); | ||
326 | switch (format) { | ||
327 | case TIME: | ||
328 | if (pw) | ||
329 | printf("%-8d %-10.10s", semid, pw->pw_name); | ||
330 | else | ||
331 | printf("%-8d %-10d", semid, ipcp->uid); | ||
332 | /* ctime uses static buffer: use separate calls */ | ||
333 | printf(" %-26.24s", semary.sem_otime | ||
334 | ? ctime(&semary.sem_otime) : "Not set"); | ||
335 | printf(" %-26.24s\n", semary.sem_ctime | ||
336 | ? ctime(&semary.sem_ctime) : "Not set"); | ||
337 | break; | ||
338 | case PID: | ||
339 | break; | ||
340 | |||
341 | default: | ||
342 | printf("0x%08x ", ipcp->KEY); | ||
343 | if (pw) | ||
344 | printf("%-10d %-10.9s", semid, pw->pw_name); | ||
345 | else | ||
346 | printf("%-10d %-9d", semid, ipcp->uid); | ||
347 | printf(" %-10o %-10ld\n", ipcp->mode & 0777, | ||
348 | /* | ||
349 | * glibc-2.1.3 and earlier has unsigned short; | ||
350 | * glibc-2.1.91 has variation between | ||
351 | * unsigned short and unsigned long | ||
352 | * Austin prescribes unsigned short. | ||
353 | */ | ||
354 | (long) semary.sem_nsems); | ||
355 | break; | ||
356 | } | ||
357 | } | ||
358 | } | ||
359 | |||
360 | |||
361 | static void do_msg(void) | ||
362 | { | ||
363 | int maxid, msqid, id; | ||
364 | struct msqid_ds msgque; | ||
365 | struct msginfo msginfo; | ||
366 | struct ipc_perm *ipcp = &msgque.msg_perm; | ||
367 | struct passwd *pw; | ||
368 | |||
369 | maxid = msgctl(0, MSG_INFO, (struct msqid_ds *) (void *) &msginfo); | ||
370 | if (maxid < 0) { | ||
371 | printf("kernel not configured for %s\n", "message queues"); | ||
372 | return; | ||
373 | } | ||
374 | |||
375 | switch (format) { | ||
376 | case LIMITS: | ||
377 | if ((msgctl(0, IPC_INFO, (struct msqid_ds *) (void *) &msginfo)) < 0) | ||
378 | return; | ||
379 | printf("------ Message%s --------\n", "s: Limits"); | ||
380 | printf( "max queues system wide = %d\n" | ||
381 | "max size of message (bytes) = %d\n" | ||
382 | "default max size of queue (bytes) = %d\n", | ||
383 | msginfo.msgmni, msginfo.msgmax, msginfo.msgmnb); | ||
384 | return; | ||
385 | |||
386 | case STATUS: | ||
387 | printf("------ Message%s --------\n", "s: Status"); | ||
388 | printf( "allocated queues = %d\n" | ||
389 | "used headers = %d\n" | ||
390 | "used space = %d bytes\n", | ||
391 | msginfo.msgpool, msginfo.msgmap, msginfo.msgtql); | ||
392 | return; | ||
393 | |||
394 | case CREATOR: | ||
395 | printf("------ Message%s --------\n", " Queues: Creators/Owners"); | ||
396 | printf( "%-10s %-10s %-10s %-10s %-10s %-10s\n", | ||
397 | "msqid", "perms", "cuid", "cgid", "uid", "gid"); | ||
398 | break; | ||
399 | |||
400 | case TIME: | ||
401 | printf("------ Message%s --------\n", " Queues Send/Recv/Change Times"); | ||
402 | printf( "%-8s %-10s %-20s %-20s %-20s\n", | ||
403 | "msqid", "owner", "send", "recv", "change"); | ||
404 | break; | ||
405 | |||
406 | case PID: | ||
407 | printf("------ Message%s --------\n", " Queues PIDs"); | ||
408 | printf( "%-10s %-10s %-10s %-10s\n", | ||
409 | "msqid", "owner", "lspid", "lrpid"); | ||
410 | break; | ||
411 | |||
412 | default: | ||
413 | printf("------ Message%s --------\n", " Queues"); | ||
414 | printf( "%-10s %-10s %-10s %-10s %-12s %-12s\n", | ||
415 | "key", "msqid", "owner", "perms", "used-bytes", "messages"); | ||
416 | break; | ||
417 | } | ||
418 | |||
419 | for (id = 0; id <= maxid; id++) { | ||
420 | msqid = msgctl(id, MSG_STAT, &msgque); | ||
421 | if (msqid < 0) | ||
422 | continue; | ||
423 | if (format == CREATOR) { | ||
424 | print_perms(msqid, ipcp); | ||
425 | continue; | ||
426 | } | ||
427 | pw = getpwuid(ipcp->uid); | ||
428 | switch (format) { | ||
429 | case TIME: | ||
430 | if (pw) | ||
431 | printf("%-8d %-10.10s", msqid, pw->pw_name); | ||
432 | else | ||
433 | printf("%-8d %-10d", msqid, ipcp->uid); | ||
434 | printf(" %-20.16s", msgque.msg_stime | ||
435 | ? ctime(&msgque.msg_stime) + 4 : "Not set"); | ||
436 | printf(" %-20.16s", msgque.msg_rtime | ||
437 | ? ctime(&msgque.msg_rtime) + 4 : "Not set"); | ||
438 | printf(" %-20.16s\n", msgque.msg_ctime | ||
439 | ? ctime(&msgque.msg_ctime) + 4 : "Not set"); | ||
440 | break; | ||
441 | case PID: | ||
442 | if (pw) | ||
443 | printf("%-8d %-10.10s", msqid, pw->pw_name); | ||
444 | else | ||
445 | printf("%-8d %-10d", msqid, ipcp->uid); | ||
446 | printf(" %5d %5d\n", msgque.msg_lspid, msgque.msg_lrpid); | ||
447 | break; | ||
448 | |||
449 | default: | ||
450 | printf("0x%08x ", ipcp->KEY); | ||
451 | if (pw) | ||
452 | printf("%-10d %-10.10s", msqid, pw->pw_name); | ||
453 | else | ||
454 | printf("%-10d %-10d", msqid, ipcp->uid); | ||
455 | printf(" %-10o %-12ld %-12ld\n", ipcp->mode & 0777, | ||
456 | /* | ||
457 | * glibc-2.1.3 and earlier has unsigned short; | ||
458 | * glibc-2.1.91 has variation between | ||
459 | * unsigned short, unsigned long | ||
460 | * Austin has msgqnum_t | ||
461 | */ | ||
462 | (long) msgque.msg_cbytes, (long) msgque.msg_qnum); | ||
463 | break; | ||
464 | } | ||
465 | } | ||
466 | } | ||
467 | |||
468 | |||
469 | static void print_shm(int shmid) | ||
470 | { | ||
471 | struct shmid_ds shmds; | ||
472 | struct ipc_perm *ipcp = &shmds.shm_perm; | ||
473 | |||
474 | if (shmctl(shmid, IPC_STAT, &shmds) == -1) { | ||
475 | bb_perror_msg("shmctl"); | ||
476 | return; | ||
477 | } | ||
478 | |||
479 | printf("\nShared memory Segment shmid=%d\n" | ||
480 | "uid=%d\tgid=%d\tcuid=%d\tcgid=%d\n" | ||
481 | "mode=%#o\taccess_perms=%#o\n" | ||
482 | "bytes=%ld\tlpid=%d\tcpid=%d\tnattch=%ld\n", | ||
483 | shmid, | ||
484 | ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid, | ||
485 | ipcp->mode, ipcp->mode & 0777, | ||
486 | (long) shmds.shm_segsz, shmds.shm_lpid, shmds.shm_cpid, | ||
487 | (long) shmds.shm_nattch); | ||
488 | printf("att_time=%-26.24s\n", | ||
489 | shmds.shm_atime ? ctime(&shmds.shm_atime) : "Not set"); | ||
490 | printf("det_time=%-26.24s\n", | ||
491 | shmds.shm_dtime ? ctime(&shmds.shm_dtime) : "Not set"); | ||
492 | printf("change_time=%-26.24s\n\n", ctime(&shmds.shm_ctime)); | ||
493 | } | ||
494 | |||
495 | |||
496 | static void print_msg(int msqid) | ||
497 | { | ||
498 | struct msqid_ds buf; | ||
499 | struct ipc_perm *ipcp = &buf.msg_perm; | ||
500 | |||
501 | if (msgctl(msqid, IPC_STAT, &buf) == -1) { | ||
502 | bb_perror_msg("msgctl"); | ||
503 | return; | ||
504 | } | ||
505 | |||
506 | printf("\nMessage Queue msqid=%d\n" | ||
507 | "uid=%d\tgid=%d\tcuid=%d\tcgid=%d\tmode=%#o\n" | ||
508 | "cbytes=%ld\tqbytes=%ld\tqnum=%ld\tlspid=%d\tlrpid=%d\n", | ||
509 | msqid, ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid, ipcp->mode, | ||
510 | /* | ||
511 | * glibc-2.1.3 and earlier has unsigned short; | ||
512 | * glibc-2.1.91 has variation between | ||
513 | * unsigned short, unsigned long | ||
514 | * Austin has msgqnum_t (for msg_qbytes) | ||
515 | */ | ||
516 | (long) buf.msg_cbytes, (long) buf.msg_qbytes, | ||
517 | (long) buf.msg_qnum, buf.msg_lspid, buf.msg_lrpid); | ||
518 | |||
519 | printf("send_time=%-26.24s\n", | ||
520 | buf.msg_stime ? ctime(&buf.msg_stime) : "Not set"); | ||
521 | printf("rcv_time=%-26.24s\n", | ||
522 | buf.msg_rtime ? ctime(&buf.msg_rtime) : "Not set"); | ||
523 | printf("change_time=%-26.24s\n\n", | ||
524 | buf.msg_ctime ? ctime(&buf.msg_ctime) : "Not set"); | ||
525 | } | ||
526 | |||
527 | static void print_sem(int semid) | ||
528 | { | ||
529 | struct semid_ds semds; | ||
530 | struct ipc_perm *ipcp = &semds.sem_perm; | ||
531 | union semun arg; | ||
532 | unsigned int i; | ||
533 | |||
534 | arg.buf = &semds; | ||
535 | if (semctl(semid, 0, IPC_STAT, arg)) { | ||
536 | bb_perror_msg("semctl"); | ||
537 | return; | ||
538 | } | ||
539 | |||
540 | printf("\nSemaphore Array semid=%d\n" | ||
541 | "uid=%d\t gid=%d\t cuid=%d\t cgid=%d\n" | ||
542 | "mode=%#o, access_perms=%#o\n" | ||
543 | "nsems = %ld\n" | ||
544 | "otime = %-26.24s\n", | ||
545 | semid, | ||
546 | ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid, | ||
547 | ipcp->mode, ipcp->mode & 0777, | ||
548 | (long) semds.sem_nsems, | ||
549 | semds.sem_otime ? ctime(&semds.sem_otime) : "Not set"); | ||
550 | printf("ctime = %-26.24s\n" | ||
551 | "%-10s %-10s %-10s %-10s %-10s\n", | ||
552 | ctime(&semds.sem_ctime), | ||
553 | "semnum", "value", "ncount", "zcount", "pid"); | ||
554 | |||
555 | arg.val = 0; | ||
556 | for (i = 0; i < semds.sem_nsems; i++) { | ||
557 | int val, ncnt, zcnt, pid; | ||
558 | |||
559 | val = semctl(semid, i, GETVAL, arg); | ||
560 | ncnt = semctl(semid, i, GETNCNT, arg); | ||
561 | zcnt = semctl(semid, i, GETZCNT, arg); | ||
562 | pid = semctl(semid, i, GETPID, arg); | ||
563 | if (val < 0 || ncnt < 0 || zcnt < 0 || pid < 0) { | ||
564 | bb_perror_msg_and_die("semctl"); | ||
565 | } | ||
566 | printf("%-10d %-10d %-10d %-10d %-10d\n", i, val, ncnt, zcnt, pid); | ||
567 | } | ||
568 | puts(""); | ||
569 | } | ||
570 | |||
571 | int ipcs_main(int argc, char **argv) | ||
572 | { | ||
573 | int id = 0; | ||
574 | unsigned flags = 0; | ||
575 | unsigned opt; | ||
576 | char *opt_i; | ||
577 | #define flag_print (1<<0) | ||
578 | #define flag_msg (1<<1) | ||
579 | #define flag_sem (1<<2) | ||
580 | #define flag_shm (1<<3) | ||
581 | |||
582 | opt = getopt32(argc, argv, "i:aqsmtcplu", &opt_i); | ||
583 | if (opt & 0x1) { // -i | ||
584 | id = xatoi(opt_i); | ||
585 | flags |= flag_print; | ||
586 | } | ||
587 | if (opt & 0x2) flags |= flag_msg | flag_sem | flag_shm; // -a | ||
588 | if (opt & 0x4) flags |= flag_msg; // -q | ||
589 | if (opt & 0x8) flags |= flag_sem; // -s | ||
590 | if (opt & 0x10) flags |= flag_shm; // -m | ||
591 | if (opt & 0x20) format = TIME; // -t | ||
592 | if (opt & 0x40) format = CREATOR; // -c | ||
593 | if (opt & 0x80) format = PID; // -p | ||
594 | if (opt & 0x100) format = LIMITS; // -l | ||
595 | if (opt & 0x200) format = STATUS; // -u | ||
596 | |||
597 | if (flags & flag_print) { | ||
598 | if (flags & flag_shm) { | ||
599 | print_shm(id); | ||
600 | fflush_stdout_and_exit(0); | ||
601 | } | ||
602 | if (flags & flag_sem) { | ||
603 | print_sem(id); | ||
604 | fflush_stdout_and_exit(0); | ||
605 | } | ||
606 | if (flags & flag_msg) { | ||
607 | print_msg(id); | ||
608 | fflush_stdout_and_exit(0); | ||
609 | } | ||
610 | bb_show_usage(); | ||
611 | } | ||
612 | |||
613 | if (!(flags & (flag_shm | flag_msg | flag_sem))) | ||
614 | flags |= flag_msg | flag_shm | flag_sem; | ||
615 | puts(""); | ||
616 | |||
617 | if (flags & flag_shm) { | ||
618 | do_shm(); | ||
619 | puts(""); | ||
620 | } | ||
621 | if (flags & flag_sem) { | ||
622 | do_sem(); | ||
623 | puts(""); | ||
624 | } | ||
625 | if (flags & flag_msg) { | ||
626 | do_msg(); | ||
627 | puts(""); | ||
628 | } | ||
629 | fflush_stdout_and_exit(0); | ||
630 | } | ||
diff --git a/util-linux/losetup.c b/util-linux/losetup.c new file mode 100644 index 000000000..c7eb85a91 --- /dev/null +++ b/util-linux/losetup.c | |||
@@ -0,0 +1,64 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Mini losetup implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 2002 Matt Kraai. | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include <getopt.h> | ||
11 | #include <stdlib.h> | ||
12 | |||
13 | #include "busybox.h" | ||
14 | |||
15 | int losetup_main(int argc, char **argv) | ||
16 | { | ||
17 | unsigned opt; | ||
18 | char *opt_o; | ||
19 | unsigned long long offset = 0; | ||
20 | |||
21 | opt = getopt32(argc, argv, "do:", &opt_o); | ||
22 | argc -= optind; | ||
23 | argv += optind; | ||
24 | |||
25 | if (opt == 0x3) // -d + -o (illegal) | ||
26 | bb_show_usage(); | ||
27 | |||
28 | if (opt == 0x1) { // -d | ||
29 | /* detach takes exactly one argument */ | ||
30 | if (argc != 1) | ||
31 | bb_show_usage(); | ||
32 | if (!del_loop(argv[0])) | ||
33 | return EXIT_SUCCESS; | ||
34 | bb_perror_nomsg_and_die(); | ||
35 | } | ||
36 | |||
37 | if (opt == 0x2) // -o | ||
38 | offset = xatoull(opt_o); | ||
39 | |||
40 | /* -o or no option */ | ||
41 | |||
42 | if (argc == 2) { | ||
43 | if (set_loop(&argv[0], argv[1], offset) < 0) | ||
44 | bb_perror_nomsg_and_die(); | ||
45 | } else if (argc == 1) { | ||
46 | char *s = query_loop(argv[0]); | ||
47 | if (!s) bb_perror_nomsg_and_die(); | ||
48 | printf("%s: %s\n", argv[0], s); | ||
49 | if (ENABLE_FEATURE_CLEAN_UP) free(s); | ||
50 | } else { | ||
51 | char dev[sizeof(LOOP_NAME"0")] = LOOP_NAME"0"; | ||
52 | char c; | ||
53 | for (c = '0'; c <= '9'; ++c) { | ||
54 | char *s; | ||
55 | dev[sizeof(LOOP_NAME"0")-2] = c; | ||
56 | s = query_loop(dev); | ||
57 | if (s) { | ||
58 | printf("%s: %s\n", dev, s); | ||
59 | if (ENABLE_FEATURE_CLEAN_UP) free(s); | ||
60 | } | ||
61 | } | ||
62 | } | ||
63 | return EXIT_SUCCESS; | ||
64 | } | ||
diff --git a/util-linux/mdev.c b/util-linux/mdev.c new file mode 100644 index 000000000..957316d52 --- /dev/null +++ b/util-linux/mdev.c | |||
@@ -0,0 +1,268 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * | ||
4 | * mdev - Mini udev for busybox | ||
5 | * | ||
6 | * Copyright 2005 Rob Landley <rob@landley.net> | ||
7 | * Copyright 2005 Frank Sorenson <frank@tuxrocks.com> | ||
8 | * | ||
9 | * Licensed under GPL version 2, see file LICENSE in this tarball for details. | ||
10 | */ | ||
11 | |||
12 | #include "busybox.h" | ||
13 | #include "xregex.h" | ||
14 | |||
15 | #define DEV_PATH "/dev" | ||
16 | |||
17 | struct mdev_globals | ||
18 | { | ||
19 | int root_major, root_minor; | ||
20 | } mdev_globals; | ||
21 | |||
22 | #define bbg mdev_globals | ||
23 | |||
24 | /* mknod in /dev based on a path like "/sys/block/hda/hda1" */ | ||
25 | static void make_device(char *path, int delete) | ||
26 | { | ||
27 | char *device_name; | ||
28 | int major, minor, type, len; | ||
29 | int mode = 0660; | ||
30 | uid_t uid = 0; | ||
31 | gid_t gid = 0; | ||
32 | char *temp = path + strlen(path); | ||
33 | char *command = NULL; | ||
34 | |||
35 | /* Try to read major/minor string. Note that the kernel puts \n after | ||
36 | * the data, so we don't need to worry about null terminating the string | ||
37 | * because sscanf() will stop at the first nondigit, which \n is. We | ||
38 | * also depend on path having writeable space after it. */ | ||
39 | |||
40 | if (!delete) { | ||
41 | strcat(path, "/dev"); | ||
42 | len = open_read_close(path, temp + 1, 64); | ||
43 | *temp++ = 0; | ||
44 | if (len < 1) return; | ||
45 | } | ||
46 | |||
47 | /* Determine device name, type, major and minor */ | ||
48 | |||
49 | device_name = strrchr(path, '/') + 1; | ||
50 | type = path[5]=='c' ? S_IFCHR : S_IFBLK; | ||
51 | |||
52 | /* If we have a config file, look up permissions for this device */ | ||
53 | |||
54 | if (ENABLE_FEATURE_MDEV_CONF) { | ||
55 | char *conf, *pos, *end; | ||
56 | int line, fd; | ||
57 | |||
58 | /* mmap the config file */ | ||
59 | fd = open("/etc/mdev.conf", O_RDONLY); | ||
60 | if (fd < 0) | ||
61 | goto end_parse; | ||
62 | len = xlseek(fd, 0, SEEK_END); | ||
63 | conf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); | ||
64 | close(fd); | ||
65 | if (!conf) | ||
66 | goto end_parse; | ||
67 | |||
68 | line = 0; | ||
69 | /* Loop through lines in mmaped file*/ | ||
70 | for (pos=conf; pos-conf<len;) { | ||
71 | int field; | ||
72 | char *end2; | ||
73 | |||
74 | line++; | ||
75 | /* find end of this line */ | ||
76 | for (end=pos; end-conf<len && *end!='\n'; end++) | ||
77 | ; | ||
78 | |||
79 | /* Three fields: regex, uid:gid, mode */ | ||
80 | for (field=0; field < (3 + ENABLE_FEATURE_MDEV_EXEC); | ||
81 | field++) | ||
82 | { | ||
83 | /* Skip whitespace */ | ||
84 | while (pos<end && isspace(*pos)) pos++; | ||
85 | if (pos==end || *pos=='#') break; | ||
86 | for (end2=pos; | ||
87 | end2<end && !isspace(*end2) && *end2!='#'; end2++) | ||
88 | ; | ||
89 | |||
90 | if (field == 0) { | ||
91 | /* Regex to match this device */ | ||
92 | |||
93 | char *regex = strndupa(pos, end2-pos); | ||
94 | regex_t match; | ||
95 | regmatch_t off; | ||
96 | int result; | ||
97 | |||
98 | /* Is this it? */ | ||
99 | xregcomp(&match,regex, REG_EXTENDED); | ||
100 | result = regexec(&match, device_name, 1, &off, 0); | ||
101 | regfree(&match); | ||
102 | |||
103 | /* If not this device, skip rest of line */ | ||
104 | if (result || off.rm_so | ||
105 | || off.rm_eo != strlen(device_name)) | ||
106 | break; | ||
107 | } | ||
108 | if (field == 1) { | ||
109 | /* uid:gid */ | ||
110 | |||
111 | char *s, *s2; | ||
112 | |||
113 | /* Find : */ | ||
114 | for (s=pos; s<end2 && *s!=':'; s++) | ||
115 | ; | ||
116 | if (s == end2) break; | ||
117 | |||
118 | /* Parse UID */ | ||
119 | uid = strtoul(pos, &s2, 10); | ||
120 | if (s != s2) { | ||
121 | struct passwd *pass; | ||
122 | pass = getpwnam(strndupa(pos, s-pos)); | ||
123 | if (!pass) break; | ||
124 | uid = pass->pw_uid; | ||
125 | } | ||
126 | s++; | ||
127 | /* parse GID */ | ||
128 | gid = strtoul(s, &s2, 10); | ||
129 | if (end2 != s2) { | ||
130 | struct group *grp; | ||
131 | grp = getgrnam(strndupa(s, end2-s)); | ||
132 | if (!grp) break; | ||
133 | gid = grp->gr_gid; | ||
134 | } | ||
135 | } | ||
136 | if (field == 2) { | ||
137 | /* mode */ | ||
138 | |||
139 | mode = strtoul(pos, &pos, 8); | ||
140 | if (pos != end2) break; | ||
141 | } | ||
142 | if (ENABLE_FEATURE_MDEV_EXEC && field == 3) { | ||
143 | // Command to run | ||
144 | char *s = "@$*", *s2; | ||
145 | s2 = strchr(s, *pos++); | ||
146 | if (!s2) { | ||
147 | // Force error | ||
148 | field = 1; | ||
149 | break; | ||
150 | } | ||
151 | if ((s2-s+1) & (1<<delete)) | ||
152 | command = xstrndup(pos, end-pos); | ||
153 | } | ||
154 | |||
155 | pos = end2; | ||
156 | } | ||
157 | |||
158 | /* Did everything parse happily? */ | ||
159 | |||
160 | if (field > 2) break; | ||
161 | if (field) bb_error_msg_and_die("bad line %d",line); | ||
162 | |||
163 | /* Next line */ | ||
164 | pos = ++end; | ||
165 | } | ||
166 | munmap(conf, len); | ||
167 | end_parse: /* nothing */ ; | ||
168 | } | ||
169 | |||
170 | umask(0); | ||
171 | if (!delete) { | ||
172 | if (sscanf(temp, "%d:%d", &major, &minor) != 2) return; | ||
173 | if (mknod(device_name, mode | type, makedev(major, minor)) && errno != EEXIST) | ||
174 | bb_perror_msg_and_die("mknod %s", device_name); | ||
175 | |||
176 | if (major == bbg.root_major && minor == bbg.root_minor) | ||
177 | symlink(device_name, "root"); | ||
178 | |||
179 | if (ENABLE_FEATURE_MDEV_CONF) chown(device_name, uid, gid); | ||
180 | } | ||
181 | if (command) { | ||
182 | int rc; | ||
183 | char *s; | ||
184 | |||
185 | s = xasprintf("MDEV=%s", device_name); | ||
186 | putenv(s); | ||
187 | rc = system(command); | ||
188 | s[4] = 0; | ||
189 | putenv(s); | ||
190 | free(s); | ||
191 | free(command); | ||
192 | if (rc == -1) bb_perror_msg_and_die("cannot run %s", command); | ||
193 | } | ||
194 | if (delete) unlink(device_name); | ||
195 | } | ||
196 | |||
197 | /* Recursive search of /sys/block or /sys/class. path must be a writeable | ||
198 | * buffer of size PATH_MAX containing the directory string to start at. */ | ||
199 | |||
200 | static void find_dev(char *path) | ||
201 | { | ||
202 | DIR *dir; | ||
203 | size_t len = strlen(path); | ||
204 | struct dirent *entry; | ||
205 | |||
206 | dir = opendir(path); | ||
207 | if (dir == NULL) | ||
208 | return; | ||
209 | |||
210 | while ((entry = readdir(dir)) != NULL) { | ||
211 | struct stat st; | ||
212 | |||
213 | /* Skip "." and ".." (also skips hidden files, which is ok) */ | ||
214 | |||
215 | if (entry->d_name[0] == '.') | ||
216 | continue; | ||
217 | |||
218 | // uClibc doesn't fill out entry->d_type reliably. so we use lstat(). | ||
219 | |||
220 | snprintf(path+len, PATH_MAX-len, "/%s", entry->d_name); | ||
221 | if (!lstat(path, &st) && S_ISDIR(st.st_mode)) find_dev(path); | ||
222 | path[len] = 0; | ||
223 | |||
224 | /* If there's a dev entry, mknod it */ | ||
225 | |||
226 | if (!strcmp(entry->d_name, "dev")) make_device(path, 0); | ||
227 | } | ||
228 | |||
229 | closedir(dir); | ||
230 | } | ||
231 | |||
232 | int mdev_main(int argc, char *argv[]) | ||
233 | { | ||
234 | char *action; | ||
235 | char *env_path; | ||
236 | RESERVE_CONFIG_BUFFER(temp,PATH_MAX); | ||
237 | |||
238 | xchdir(DEV_PATH); | ||
239 | |||
240 | /* Scan */ | ||
241 | |||
242 | if (argc == 2 && !strcmp(argv[1],"-s")) { | ||
243 | struct stat st; | ||
244 | |||
245 | xstat("/", &st); | ||
246 | bbg.root_major = major(st.st_dev); | ||
247 | bbg.root_minor = minor(st.st_dev); | ||
248 | strcpy(temp,"/sys/block"); | ||
249 | find_dev(temp); | ||
250 | strcpy(temp,"/sys/class"); | ||
251 | find_dev(temp); | ||
252 | |||
253 | /* Hotplug */ | ||
254 | |||
255 | } else { | ||
256 | action = getenv("ACTION"); | ||
257 | env_path = getenv("DEVPATH"); | ||
258 | if (!action || !env_path) | ||
259 | bb_show_usage(); | ||
260 | |||
261 | sprintf(temp, "/sys%s", env_path); | ||
262 | if (!strcmp(action, "add")) make_device(temp,0); | ||
263 | else if (!strcmp(action, "remove")) make_device(temp,1); | ||
264 | } | ||
265 | |||
266 | if (ENABLE_FEATURE_CLEAN_UP) RELEASE_CONFIG_BUFFER(temp); | ||
267 | return 0; | ||
268 | } | ||
diff --git a/util-linux/mkfs_minix.c b/util-linux/mkfs_minix.c new file mode 100644 index 000000000..af19da68c --- /dev/null +++ b/util-linux/mkfs_minix.c | |||
@@ -0,0 +1,762 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * mkfs.c - make a linux (minix) file-system. | ||
4 | * | ||
5 | * (C) 1991 Linus Torvalds. This file may be redistributed as per | ||
6 | * the Linux copyright. | ||
7 | */ | ||
8 | |||
9 | /* | ||
10 | * DD.MM.YY | ||
11 | * | ||
12 | * 24.11.91 - Time began. Used the fsck sources to get started. | ||
13 | * | ||
14 | * 25.11.91 - Corrected some bugs. Added support for ".badblocks" | ||
15 | * The algorithm for ".badblocks" is a bit weird, but | ||
16 | * it should work. Oh, well. | ||
17 | * | ||
18 | * 25.01.92 - Added the -l option for getting the list of bad blocks | ||
19 | * out of a named file. (Dave Rivers, rivers@ponds.uucp) | ||
20 | * | ||
21 | * 28.02.92 - Added %-information when using -c. | ||
22 | * | ||
23 | * 28.02.93 - Added support for other namelengths than the original | ||
24 | * 14 characters so that I can test the new kernel routines.. | ||
25 | * | ||
26 | * 09.10.93 - Make exit status conform to that required by fsutil | ||
27 | * (Rik Faith, faith@cs.unc.edu) | ||
28 | * | ||
29 | * 31.10.93 - Added inode request feature, for backup floppies: use | ||
30 | * 32 inodes, for a news partition use more. | ||
31 | * (Scott Heavner, sdh@po.cwru.edu) | ||
32 | * | ||
33 | * 03.01.94 - Added support for file system valid flag. | ||
34 | * (Dr. Wettstein, greg%wind.uucp@plains.nodak.edu) | ||
35 | * | ||
36 | * 30.10.94 - added support for v2 filesystem | ||
37 | * (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de) | ||
38 | * | ||
39 | * 09.11.94 - Added test to prevent overwrite of mounted fs adapted | ||
40 | * from Theodore Ts'o's (tytso@athena.mit.edu) mke2fs | ||
41 | * program. (Daniel Quinlan, quinlan@yggdrasil.com) | ||
42 | * | ||
43 | * 03.20.95 - Clear first 512 bytes of filesystem to make certain that | ||
44 | * the filesystem is not misidentified as a MS-DOS FAT filesystem. | ||
45 | * (Daniel Quinlan, quinlan@yggdrasil.com) | ||
46 | * | ||
47 | * 02.07.96 - Added small patch from Russell King to make the program a | ||
48 | * good deal more portable (janl@math.uio.no) | ||
49 | * | ||
50 | * Usage: mkfs [-c | -l filename ] [-v] [-nXX] [-iXX] device [size-in-blocks] | ||
51 | * | ||
52 | * -c for readability checking (SLOW!) | ||
53 | * -l for getting a list of bad blocks from a file. | ||
54 | * -n for namelength (currently the kernel only uses 14 or 30) | ||
55 | * -i for number of inodes | ||
56 | * -v for v2 filesystem | ||
57 | * | ||
58 | * The device may be a block device or a image of one, but this isn't | ||
59 | * enforced (but it's not much fun on a character device :-). | ||
60 | * | ||
61 | * Modified for BusyBox by Erik Andersen <andersen@debian.org> -- | ||
62 | * removed getopt based parser and added a hand rolled one. | ||
63 | */ | ||
64 | |||
65 | #include "busybox.h" | ||
66 | #include <mntent.h> | ||
67 | |||
68 | #define DEBUG 0 | ||
69 | |||
70 | /* If debugging, store the very same times/uids/gids for image consistency */ | ||
71 | #if DEBUG | ||
72 | # define CUR_TIME 0 | ||
73 | # define GETUID 0 | ||
74 | # define GETGID 0 | ||
75 | #else | ||
76 | # define CUR_TIME time(NULL) | ||
77 | # define GETUID getuid() | ||
78 | # define GETGID getgid() | ||
79 | #endif | ||
80 | |||
81 | /* | ||
82 | * This is the original minix inode layout on disk. | ||
83 | * Note the 8-bit gid and atime and ctime. | ||
84 | */ | ||
85 | struct minix1_inode { | ||
86 | uint16_t i_mode; | ||
87 | uint16_t i_uid; | ||
88 | uint32_t i_size; | ||
89 | uint32_t i_time; | ||
90 | uint8_t i_gid; | ||
91 | uint8_t i_nlinks; | ||
92 | uint16_t i_zone[9]; | ||
93 | }; | ||
94 | |||
95 | /* | ||
96 | * The new minix inode has all the time entries, as well as | ||
97 | * long block numbers and a third indirect block (7+1+1+1 | ||
98 | * instead of 7+1+1). Also, some previously 8-bit values are | ||
99 | * now 16-bit. The inode is now 64 bytes instead of 32. | ||
100 | */ | ||
101 | struct minix2_inode { | ||
102 | uint16_t i_mode; | ||
103 | uint16_t i_nlinks; | ||
104 | uint16_t i_uid; | ||
105 | uint16_t i_gid; | ||
106 | uint32_t i_size; | ||
107 | uint32_t i_atime; | ||
108 | uint32_t i_mtime; | ||
109 | uint32_t i_ctime; | ||
110 | uint32_t i_zone[10]; | ||
111 | }; | ||
112 | |||
113 | /* | ||
114 | * minix super-block data on disk | ||
115 | */ | ||
116 | struct minix_super_block { | ||
117 | uint16_t s_ninodes; | ||
118 | uint16_t s_nzones; | ||
119 | uint16_t s_imap_blocks; | ||
120 | uint16_t s_zmap_blocks; | ||
121 | uint16_t s_firstdatazone; | ||
122 | uint16_t s_log_zone_size; | ||
123 | uint32_t s_max_size; | ||
124 | uint16_t s_magic; | ||
125 | uint16_t s_state; | ||
126 | uint32_t s_zones; | ||
127 | }; | ||
128 | |||
129 | struct minix_dir_entry { | ||
130 | uint16_t inode; | ||
131 | char name[0]; | ||
132 | }; | ||
133 | |||
134 | /* Believe it or not, but mount.h has this one */ | ||
135 | #undef BLOCK_SIZE | ||
136 | enum { | ||
137 | BLOCK_SIZE = 1024, | ||
138 | BITS_PER_BLOCK = BLOCK_SIZE << 3, | ||
139 | |||
140 | MINIX_ROOT_INO = 1, | ||
141 | MINIX_BAD_INO = 2, | ||
142 | MAX_GOOD_BLOCKS = 512, | ||
143 | |||
144 | MINIX1_SUPER_MAGIC = 0x137F, /* original minix fs */ | ||
145 | MINIX1_SUPER_MAGIC2 = 0x138F, /* minix fs, 30 char names */ | ||
146 | MINIX2_SUPER_MAGIC = 0x2468, /* minix V2 fs */ | ||
147 | MINIX2_SUPER_MAGIC2 = 0x2478, /* minix V2 fs, 30 char names */ | ||
148 | MINIX_VALID_FS = 0x0001, /* clean fs */ | ||
149 | MINIX_ERROR_FS = 0x0002, /* fs has errors */ | ||
150 | |||
151 | INODE_SIZE1 = sizeof(struct minix1_inode), | ||
152 | INODE_SIZE2 = sizeof(struct minix2_inode), | ||
153 | MINIX1_INODES_PER_BLOCK = BLOCK_SIZE / sizeof(struct minix1_inode), | ||
154 | MINIX2_INODES_PER_BLOCK = BLOCK_SIZE / sizeof(struct minix2_inode), | ||
155 | |||
156 | TEST_BUFFER_BLOCKS = 16, | ||
157 | }; | ||
158 | |||
159 | #if ENABLE_FEATURE_MINIX2 | ||
160 | static int version2; | ||
161 | #else | ||
162 | enum { version2 = 0 }; | ||
163 | #endif | ||
164 | |||
165 | static char *device_name; | ||
166 | static int dev_fd = -1; | ||
167 | static uint32_t total_blocks; | ||
168 | static int badblocks; | ||
169 | /* default (changed to 30, per Linus's suggestion, Sun Nov 21 08:05:07 1993) */ | ||
170 | static int namelen = 30; | ||
171 | static int dirsize = 32; | ||
172 | static int magic = MINIX1_SUPER_MAGIC2; | ||
173 | |||
174 | static char root_block[BLOCK_SIZE]; | ||
175 | static char super_block_buffer[BLOCK_SIZE]; | ||
176 | static char boot_block_buffer[512]; | ||
177 | static char *inode_buffer; | ||
178 | |||
179 | static char *inode_map; | ||
180 | static char *zone_map; | ||
181 | |||
182 | static int used_good_blocks; | ||
183 | static unsigned short good_blocks_table[MAX_GOOD_BLOCKS]; | ||
184 | static unsigned long req_nr_inodes; | ||
185 | |||
186 | extern inline unsigned div_roundup(unsigned size, unsigned n) | ||
187 | { | ||
188 | return (size + n-1) / n; | ||
189 | } | ||
190 | |||
191 | #define INODE_BUF1 (((struct minix1_inode*)inode_buffer) - 1) | ||
192 | #define INODE_BUF2 (((struct minix2_inode*)inode_buffer) - 1) | ||
193 | |||
194 | #define SB (*(struct minix_super_block*)super_block_buffer) | ||
195 | |||
196 | #define SB_INODES (SB.s_ninodes) | ||
197 | #define SB_IMAPS (SB.s_imap_blocks) | ||
198 | #define SB_ZMAPS (SB.s_zmap_blocks) | ||
199 | #define SB_FIRSTZONE (SB.s_firstdatazone) | ||
200 | #define SB_ZONE_SIZE (SB.s_log_zone_size) | ||
201 | #define SB_MAXSIZE (SB.s_max_size) | ||
202 | #define SB_MAGIC (SB.s_magic) | ||
203 | |||
204 | #if !ENABLE_FEATURE_MINIX2 | ||
205 | # define SB_ZONES (SB.s_nzones) | ||
206 | # define INODE_BLOCKS div_roundup(SB_INODES, MINIX1_INODES_PER_BLOCK) | ||
207 | #else | ||
208 | # define SB_ZONES (version2 ? SB.s_zones : SB.s_nzones) | ||
209 | # define INODE_BLOCKS div_roundup(SB_INODES, \ | ||
210 | version2 ? MINIX2_INODES_PER_BLOCK : MINIX1_INODES_PER_BLOCK) | ||
211 | #endif | ||
212 | |||
213 | #define INODE_BUFFER_SIZE (INODE_BLOCKS * BLOCK_SIZE) | ||
214 | #define NORM_FIRSTZONE (2 + SB_IMAPS + SB_ZMAPS + INODE_BLOCKS) | ||
215 | |||
216 | static int bit(const char* a, unsigned i) | ||
217 | { | ||
218 | return a[i >> 3] & (1<<(i & 7)); | ||
219 | } | ||
220 | |||
221 | /* Note: do not assume 0/1, it is 0/nonzero */ | ||
222 | #define inode_in_use(x) bit(inode_map,(x)) | ||
223 | #define zone_in_use(x) bit(zone_map,(x)-SB_FIRSTZONE+1) | ||
224 | |||
225 | #define mark_inode(x) setbit(inode_map,(x)) | ||
226 | #define unmark_inode(x) clrbit(inode_map,(x)) | ||
227 | #define mark_zone(x) setbit(zone_map,(x)-SB_FIRSTZONE+1) | ||
228 | #define unmark_zone(x) clrbit(zone_map,(x)-SB_FIRSTZONE+1) | ||
229 | |||
230 | #ifndef BLKGETSIZE | ||
231 | # define BLKGETSIZE _IO(0x12,96) /* return device size */ | ||
232 | #endif | ||
233 | |||
234 | |||
235 | static long valid_offset(int fd, int offset) | ||
236 | { | ||
237 | char ch; | ||
238 | |||
239 | if (lseek(fd, offset, SEEK_SET) < 0) | ||
240 | return 0; | ||
241 | if (read(fd, &ch, 1) < 1) | ||
242 | return 0; | ||
243 | return 1; | ||
244 | } | ||
245 | |||
246 | static int count_blocks(int fd) | ||
247 | { | ||
248 | int high, low; | ||
249 | |||
250 | low = 0; | ||
251 | for (high = 1; valid_offset(fd, high); high *= 2) | ||
252 | low = high; | ||
253 | |||
254 | while (low < high - 1) { | ||
255 | const int mid = (low + high) / 2; | ||
256 | |||
257 | if (valid_offset(fd, mid)) | ||
258 | low = mid; | ||
259 | else | ||
260 | high = mid; | ||
261 | } | ||
262 | valid_offset(fd, 0); | ||
263 | return (low + 1); | ||
264 | } | ||
265 | |||
266 | static int get_size(const char *file) | ||
267 | { | ||
268 | int fd; | ||
269 | long size; | ||
270 | |||
271 | fd = xopen(file, O_RDWR); | ||
272 | if (ioctl(fd, BLKGETSIZE, &size) >= 0) { | ||
273 | close(fd); | ||
274 | return (size * 512); | ||
275 | } | ||
276 | |||
277 | size = count_blocks(fd); | ||
278 | close(fd); | ||
279 | return size; | ||
280 | } | ||
281 | |||
282 | static void write_tables(void) | ||
283 | { | ||
284 | /* Mark the super block valid. */ | ||
285 | SB.s_state |= MINIX_VALID_FS; | ||
286 | SB.s_state &= ~MINIX_ERROR_FS; | ||
287 | |||
288 | msg_eol = "seek to 0 failed"; | ||
289 | xlseek(dev_fd, 0, SEEK_SET); | ||
290 | |||
291 | msg_eol = "cannot clear boot sector"; | ||
292 | xwrite(dev_fd, boot_block_buffer, 512); | ||
293 | |||
294 | msg_eol = "seek to BLOCK_SIZE failed"; | ||
295 | xlseek(dev_fd, BLOCK_SIZE, SEEK_SET); | ||
296 | |||
297 | msg_eol = "cannot write superblock"; | ||
298 | xwrite(dev_fd, super_block_buffer, BLOCK_SIZE); | ||
299 | |||
300 | msg_eol = "cannot write inode map"; | ||
301 | xwrite(dev_fd, inode_map, SB_IMAPS * BLOCK_SIZE); | ||
302 | |||
303 | msg_eol = "cannot write zone map"; | ||
304 | xwrite(dev_fd, zone_map, SB_ZMAPS * BLOCK_SIZE); | ||
305 | |||
306 | msg_eol = "cannot write inodes"; | ||
307 | xwrite(dev_fd, inode_buffer, INODE_BUFFER_SIZE); | ||
308 | |||
309 | msg_eol = "\n"; | ||
310 | } | ||
311 | |||
312 | static void write_block(int blk, char *buffer) | ||
313 | { | ||
314 | xlseek(dev_fd, blk * BLOCK_SIZE, SEEK_SET); | ||
315 | xwrite(dev_fd, buffer, BLOCK_SIZE); | ||
316 | } | ||
317 | |||
318 | static int get_free_block(void) | ||
319 | { | ||
320 | int blk; | ||
321 | |||
322 | if (used_good_blocks + 1 >= MAX_GOOD_BLOCKS) | ||
323 | bb_error_msg_and_die("too many bad blocks"); | ||
324 | if (used_good_blocks) | ||
325 | blk = good_blocks_table[used_good_blocks - 1] + 1; | ||
326 | else | ||
327 | blk = SB_FIRSTZONE; | ||
328 | while (blk < SB_ZONES && zone_in_use(blk)) | ||
329 | blk++; | ||
330 | if (blk >= SB_ZONES) | ||
331 | bb_error_msg_and_die("not enough good blocks"); | ||
332 | good_blocks_table[used_good_blocks] = blk; | ||
333 | used_good_blocks++; | ||
334 | return blk; | ||
335 | } | ||
336 | |||
337 | static void mark_good_blocks(void) | ||
338 | { | ||
339 | int blk; | ||
340 | |||
341 | for (blk = 0; blk < used_good_blocks; blk++) | ||
342 | mark_zone(good_blocks_table[blk]); | ||
343 | } | ||
344 | |||
345 | static int next(int zone) | ||
346 | { | ||
347 | if (!zone) | ||
348 | zone = SB_FIRSTZONE - 1; | ||
349 | while (++zone < SB_ZONES) | ||
350 | if (zone_in_use(zone)) | ||
351 | return zone; | ||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | static void make_bad_inode(void) | ||
356 | { | ||
357 | struct minix1_inode *inode = &INODE_BUF1[MINIX_BAD_INO]; | ||
358 | int i, j, zone; | ||
359 | int ind = 0, dind = 0; | ||
360 | unsigned short ind_block[BLOCK_SIZE >> 1]; | ||
361 | unsigned short dind_block[BLOCK_SIZE >> 1]; | ||
362 | |||
363 | #define NEXT_BAD (zone = next(zone)) | ||
364 | |||
365 | if (!badblocks) | ||
366 | return; | ||
367 | mark_inode(MINIX_BAD_INO); | ||
368 | inode->i_nlinks = 1; | ||
369 | /* BTW, setting this makes all images different */ | ||
370 | /* it's harder to check for bugs then - diff isn't helpful :(... */ | ||
371 | inode->i_time = CUR_TIME; | ||
372 | inode->i_mode = S_IFREG + 0000; | ||
373 | inode->i_size = badblocks * BLOCK_SIZE; | ||
374 | zone = next(0); | ||
375 | for (i = 0; i < 7; i++) { | ||
376 | inode->i_zone[i] = zone; | ||
377 | if (!NEXT_BAD) | ||
378 | goto end_bad; | ||
379 | } | ||
380 | inode->i_zone[7] = ind = get_free_block(); | ||
381 | memset(ind_block, 0, BLOCK_SIZE); | ||
382 | for (i = 0; i < 512; i++) { | ||
383 | ind_block[i] = zone; | ||
384 | if (!NEXT_BAD) | ||
385 | goto end_bad; | ||
386 | } | ||
387 | inode->i_zone[8] = dind = get_free_block(); | ||
388 | memset(dind_block, 0, BLOCK_SIZE); | ||
389 | for (i = 0; i < 512; i++) { | ||
390 | write_block(ind, (char *) ind_block); | ||
391 | dind_block[i] = ind = get_free_block(); | ||
392 | memset(ind_block, 0, BLOCK_SIZE); | ||
393 | for (j = 0; j < 512; j++) { | ||
394 | ind_block[j] = zone; | ||
395 | if (!NEXT_BAD) | ||
396 | goto end_bad; | ||
397 | } | ||
398 | } | ||
399 | bb_error_msg_and_die("too many bad blocks"); | ||
400 | end_bad: | ||
401 | if (ind) | ||
402 | write_block(ind, (char *) ind_block); | ||
403 | if (dind) | ||
404 | write_block(dind, (char *) dind_block); | ||
405 | } | ||
406 | |||
407 | #if ENABLE_FEATURE_MINIX2 | ||
408 | static void make_bad_inode2(void) | ||
409 | { | ||
410 | struct minix2_inode *inode = &INODE_BUF2[MINIX_BAD_INO]; | ||
411 | int i, j, zone; | ||
412 | int ind = 0, dind = 0; | ||
413 | unsigned long ind_block[BLOCK_SIZE >> 2]; | ||
414 | unsigned long dind_block[BLOCK_SIZE >> 2]; | ||
415 | |||
416 | if (!badblocks) | ||
417 | return; | ||
418 | mark_inode(MINIX_BAD_INO); | ||
419 | inode->i_nlinks = 1; | ||
420 | inode->i_atime = inode->i_mtime = inode->i_ctime = CUR_TIME; | ||
421 | inode->i_mode = S_IFREG + 0000; | ||
422 | inode->i_size = badblocks * BLOCK_SIZE; | ||
423 | zone = next(0); | ||
424 | for (i = 0; i < 7; i++) { | ||
425 | inode->i_zone[i] = zone; | ||
426 | if (!NEXT_BAD) | ||
427 | goto end_bad; | ||
428 | } | ||
429 | inode->i_zone[7] = ind = get_free_block(); | ||
430 | memset(ind_block, 0, BLOCK_SIZE); | ||
431 | for (i = 0; i < 256; i++) { | ||
432 | ind_block[i] = zone; | ||
433 | if (!NEXT_BAD) | ||
434 | goto end_bad; | ||
435 | } | ||
436 | inode->i_zone[8] = dind = get_free_block(); | ||
437 | memset(dind_block, 0, BLOCK_SIZE); | ||
438 | for (i = 0; i < 256; i++) { | ||
439 | write_block(ind, (char *) ind_block); | ||
440 | dind_block[i] = ind = get_free_block(); | ||
441 | memset(ind_block, 0, BLOCK_SIZE); | ||
442 | for (j = 0; j < 256; j++) { | ||
443 | ind_block[j] = zone; | ||
444 | if (!NEXT_BAD) | ||
445 | goto end_bad; | ||
446 | } | ||
447 | } | ||
448 | /* Could make triple indirect block here */ | ||
449 | bb_error_msg_and_die("too many bad blocks"); | ||
450 | end_bad: | ||
451 | if (ind) | ||
452 | write_block(ind, (char *) ind_block); | ||
453 | if (dind) | ||
454 | write_block(dind, (char *) dind_block); | ||
455 | } | ||
456 | #endif | ||
457 | |||
458 | static void make_root_inode(void) | ||
459 | { | ||
460 | struct minix1_inode *inode = &INODE_BUF1[MINIX_ROOT_INO]; | ||
461 | |||
462 | mark_inode(MINIX_ROOT_INO); | ||
463 | inode->i_zone[0] = get_free_block(); | ||
464 | inode->i_nlinks = 2; | ||
465 | inode->i_time = CUR_TIME; | ||
466 | if (badblocks) | ||
467 | inode->i_size = 3 * dirsize; | ||
468 | else { | ||
469 | root_block[2 * dirsize] = '\0'; | ||
470 | root_block[2 * dirsize + 1] = '\0'; | ||
471 | inode->i_size = 2 * dirsize; | ||
472 | } | ||
473 | inode->i_mode = S_IFDIR + 0755; | ||
474 | inode->i_uid = GETUID; | ||
475 | if (inode->i_uid) | ||
476 | inode->i_gid = GETGID; | ||
477 | write_block(inode->i_zone[0], root_block); | ||
478 | } | ||
479 | |||
480 | #if ENABLE_FEATURE_MINIX2 | ||
481 | static void make_root_inode2(void) | ||
482 | { | ||
483 | struct minix2_inode *inode = &INODE_BUF2[MINIX_ROOT_INO]; | ||
484 | |||
485 | mark_inode(MINIX_ROOT_INO); | ||
486 | inode->i_zone[0] = get_free_block(); | ||
487 | inode->i_nlinks = 2; | ||
488 | inode->i_atime = inode->i_mtime = inode->i_ctime = CUR_TIME; | ||
489 | if (badblocks) | ||
490 | inode->i_size = 3 * dirsize; | ||
491 | else { | ||
492 | root_block[2 * dirsize] = '\0'; | ||
493 | root_block[2 * dirsize + 1] = '\0'; | ||
494 | inode->i_size = 2 * dirsize; | ||
495 | } | ||
496 | inode->i_mode = S_IFDIR + 0755; | ||
497 | inode->i_uid = GETUID; | ||
498 | if (inode->i_uid) | ||
499 | inode->i_gid = GETGID; | ||
500 | write_block(inode->i_zone[0], root_block); | ||
501 | } | ||
502 | #endif | ||
503 | |||
504 | static void setup_tables(void) | ||
505 | { | ||
506 | unsigned long inodes; | ||
507 | unsigned norm_firstzone; | ||
508 | unsigned sb_zmaps; | ||
509 | unsigned i; | ||
510 | |||
511 | memset(super_block_buffer, 0, BLOCK_SIZE); | ||
512 | memset(boot_block_buffer, 0, 512); | ||
513 | SB_MAGIC = magic; | ||
514 | SB_ZONE_SIZE = 0; | ||
515 | SB_MAXSIZE = version2 ? 0x7fffffff : (7 + 512 + 512 * 512) * 1024; | ||
516 | if (version2) | ||
517 | SB.s_zones = total_blocks; | ||
518 | else | ||
519 | SB.s_nzones = total_blocks; | ||
520 | |||
521 | /* some magic nrs: 1 inode / 3 blocks */ | ||
522 | if (req_nr_inodes == 0) | ||
523 | inodes = total_blocks / 3; | ||
524 | else | ||
525 | inodes = req_nr_inodes; | ||
526 | /* Round up inode count to fill block size */ | ||
527 | if (version2) | ||
528 | inodes = (inodes + MINIX2_INODES_PER_BLOCK - 1) & | ||
529 | ~(MINIX2_INODES_PER_BLOCK - 1); | ||
530 | else | ||
531 | inodes = (inodes + MINIX1_INODES_PER_BLOCK - 1) & | ||
532 | ~(MINIX1_INODES_PER_BLOCK - 1); | ||
533 | if (inodes > 65535) | ||
534 | inodes = 65535; | ||
535 | SB_INODES = inodes; | ||
536 | SB_IMAPS = div_roundup(SB_INODES + 1, BITS_PER_BLOCK); | ||
537 | |||
538 | /* Real bad hack but overwise mkfs.minix can be thrown | ||
539 | * in infinite loop... | ||
540 | * try: | ||
541 | * dd if=/dev/zero of=test.fs count=10 bs=1024 | ||
542 | * mkfs.minix -i 200 test.fs | ||
543 | */ | ||
544 | /* This code is not insane: NORM_FIRSTZONE is not a constant, | ||
545 | * it is calculated from SB_INODES, SB_IMAPS and SB_ZMAPS */ | ||
546 | i = 999; | ||
547 | SB_ZMAPS = 0; | ||
548 | do { | ||
549 | norm_firstzone = NORM_FIRSTZONE; | ||
550 | sb_zmaps = div_roundup(total_blocks - norm_firstzone + 1, BITS_PER_BLOCK); | ||
551 | if (SB_ZMAPS == sb_zmaps) goto got_it; | ||
552 | SB_ZMAPS = sb_zmaps; | ||
553 | /* new SB_ZMAPS, need to recalc NORM_FIRSTZONE */ | ||
554 | } while (--i); | ||
555 | bb_error_msg_and_die("incompatible size/inode count, try different -i N"); | ||
556 | got_it: | ||
557 | |||
558 | SB_FIRSTZONE = norm_firstzone; | ||
559 | inode_map = xmalloc(SB_IMAPS * BLOCK_SIZE); | ||
560 | zone_map = xmalloc(SB_ZMAPS * BLOCK_SIZE); | ||
561 | memset(inode_map, 0xff, SB_IMAPS * BLOCK_SIZE); | ||
562 | memset(zone_map, 0xff, SB_ZMAPS * BLOCK_SIZE); | ||
563 | for (i = SB_FIRSTZONE; i < SB_ZONES; i++) | ||
564 | unmark_zone(i); | ||
565 | for (i = MINIX_ROOT_INO; i <= SB_INODES; i++) | ||
566 | unmark_inode(i); | ||
567 | inode_buffer = xzalloc(INODE_BUFFER_SIZE); | ||
568 | printf("%ld inodes\n", (long)SB_INODES); | ||
569 | printf("%ld blocks\n", (long)SB_ZONES); | ||
570 | printf("Firstdatazone=%ld (%ld)\n", (long)SB_FIRSTZONE, (long)norm_firstzone); | ||
571 | printf("Zonesize=%d\n", BLOCK_SIZE << SB_ZONE_SIZE); | ||
572 | printf("Maxsize=%ld\n", (long)SB_MAXSIZE); | ||
573 | } | ||
574 | |||
575 | /* | ||
576 | * Perform a test of a block; return the number of | ||
577 | * blocks readable/writable. | ||
578 | */ | ||
579 | static long do_check(char *buffer, int try, unsigned current_block) | ||
580 | { | ||
581 | long got; | ||
582 | |||
583 | /* Seek to the correct loc. */ | ||
584 | msg_eol = "seek failed during testing of blocks"; | ||
585 | xlseek(dev_fd, current_block * BLOCK_SIZE, SEEK_SET); | ||
586 | msg_eol = "\n"; | ||
587 | |||
588 | /* Try the read */ | ||
589 | got = read(dev_fd, buffer, try * BLOCK_SIZE); | ||
590 | if (got < 0) | ||
591 | got = 0; | ||
592 | if (got & (BLOCK_SIZE - 1)) { | ||
593 | printf("Weird values in do_check: probably bugs\n"); | ||
594 | } | ||
595 | got /= BLOCK_SIZE; | ||
596 | return got; | ||
597 | } | ||
598 | |||
599 | static unsigned currently_testing; | ||
600 | |||
601 | static void alarm_intr(int alnum) | ||
602 | { | ||
603 | if (currently_testing >= SB_ZONES) | ||
604 | return; | ||
605 | signal(SIGALRM, alarm_intr); | ||
606 | alarm(5); | ||
607 | if (!currently_testing) | ||
608 | return; | ||
609 | printf("%d ...", currently_testing); | ||
610 | fflush(stdout); | ||
611 | } | ||
612 | |||
613 | static void check_blocks(void) | ||
614 | { | ||
615 | int try, got; | ||
616 | /* buffer[] was the biggest static in entire bbox */ | ||
617 | char *buffer = xmalloc(BLOCK_SIZE * TEST_BUFFER_BLOCKS); | ||
618 | |||
619 | currently_testing = 0; | ||
620 | signal(SIGALRM, alarm_intr); | ||
621 | alarm(5); | ||
622 | while (currently_testing < SB_ZONES) { | ||
623 | msg_eol = "seek failed in check_blocks"; | ||
624 | xlseek(dev_fd, currently_testing * BLOCK_SIZE, SEEK_SET); | ||
625 | msg_eol = "\n"; | ||
626 | try = TEST_BUFFER_BLOCKS; | ||
627 | if (currently_testing + try > SB_ZONES) | ||
628 | try = SB_ZONES - currently_testing; | ||
629 | got = do_check(buffer, try, currently_testing); | ||
630 | currently_testing += got; | ||
631 | if (got == try) | ||
632 | continue; | ||
633 | if (currently_testing < SB_FIRSTZONE) | ||
634 | bb_error_msg_and_die("bad blocks before data-area: cannot make fs"); | ||
635 | mark_zone(currently_testing); | ||
636 | badblocks++; | ||
637 | currently_testing++; | ||
638 | } | ||
639 | free(buffer); | ||
640 | printf("%d bad block(s)\n", badblocks); | ||
641 | } | ||
642 | |||
643 | static void get_list_blocks(char *filename) | ||
644 | { | ||
645 | FILE *listfile; | ||
646 | unsigned long blockno; | ||
647 | |||
648 | listfile = xfopen(filename, "r"); | ||
649 | while (!feof(listfile)) { | ||
650 | fscanf(listfile, "%ld\n", &blockno); | ||
651 | mark_zone(blockno); | ||
652 | badblocks++; | ||
653 | } | ||
654 | printf("%d bad block(s)\n", badblocks); | ||
655 | } | ||
656 | |||
657 | int mkfs_minix_main(int argc, char **argv) | ||
658 | { | ||
659 | struct mntent *mp; | ||
660 | unsigned opt; | ||
661 | char *tmp; | ||
662 | struct stat statbuf; | ||
663 | char *str_i, *str_n; | ||
664 | char *listfile = NULL; | ||
665 | |||
666 | if (INODE_SIZE1 * MINIX1_INODES_PER_BLOCK != BLOCK_SIZE) | ||
667 | bb_error_msg_and_die("bad inode size"); | ||
668 | #if ENABLE_FEATURE_MINIX2 | ||
669 | if (INODE_SIZE2 * MINIX2_INODES_PER_BLOCK != BLOCK_SIZE) | ||
670 | bb_error_msg_and_die("bad inode size"); | ||
671 | #endif | ||
672 | |||
673 | opt = getopt32(argc, argv, "ci:l:n:v", &str_i, &listfile, &str_n); | ||
674 | argv += optind; | ||
675 | //if (opt & 1) -c | ||
676 | if (opt & 2) req_nr_inodes = xatoul(str_i); // -i | ||
677 | //if (opt & 4) -l | ||
678 | if (opt & 8) { // -n | ||
679 | namelen = xatoi_u(str_n); | ||
680 | if (namelen == 14) magic = MINIX1_SUPER_MAGIC; | ||
681 | else if (namelen == 30) magic = MINIX1_SUPER_MAGIC2; | ||
682 | else bb_show_usage(); | ||
683 | dirsize = namelen + 2; | ||
684 | } | ||
685 | if (opt & 0x10) { // -v | ||
686 | #if ENABLE_FEATURE_MINIX2 | ||
687 | version2 = 1; | ||
688 | #else | ||
689 | bb_error_msg_and_die("%s: not compiled with minix v2 support", | ||
690 | device_name); | ||
691 | #endif | ||
692 | } | ||
693 | |||
694 | device_name = *argv++; | ||
695 | if (!device_name) | ||
696 | bb_show_usage(); | ||
697 | if (*argv) | ||
698 | total_blocks = xatou32(*argv); | ||
699 | else | ||
700 | total_blocks = get_size(device_name) / 1024; | ||
701 | |||
702 | if (total_blocks < 10) | ||
703 | bb_error_msg_and_die("must have at least 10 blocks"); | ||
704 | |||
705 | if (version2) { | ||
706 | magic = MINIX2_SUPER_MAGIC2; | ||
707 | if (namelen == 14) | ||
708 | magic = MINIX2_SUPER_MAGIC; | ||
709 | } else if (total_blocks > 65535) | ||
710 | total_blocks = 65535; | ||
711 | |||
712 | /* Check if it is mounted */ | ||
713 | mp = find_mount_point(device_name, NULL); | ||
714 | if (mp && strcmp(device_name, mp->mnt_fsname) == 0) | ||
715 | bb_error_msg_and_die("%s is mounted on %s; " | ||
716 | "refusing to make a filesystem", | ||
717 | device_name, mp->mnt_dir); | ||
718 | |||
719 | dev_fd = xopen(device_name, O_RDWR); | ||
720 | if (fstat(dev_fd, &statbuf) < 0) | ||
721 | bb_error_msg_and_die("cannot stat %s", device_name); | ||
722 | if (!S_ISBLK(statbuf.st_mode)) | ||
723 | opt &= ~1; // clear -c (check) | ||
724 | |||
725 | /* I don't know why someone has special code to prevent mkfs.minix | ||
726 | * on IDE devices. Why IDE but not SCSI, etc?... */ | ||
727 | #if 0 | ||
728 | else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340) | ||
729 | /* what is this? */ | ||
730 | bb_error_msg_and_die("will not try " | ||
731 | "to make filesystem on '%s'", device_name); | ||
732 | #endif | ||
733 | |||
734 | tmp = root_block; | ||
735 | *(short *) tmp = 1; | ||
736 | strcpy(tmp + 2, "."); | ||
737 | tmp += dirsize; | ||
738 | *(short *) tmp = 1; | ||
739 | strcpy(tmp + 2, ".."); | ||
740 | tmp += dirsize; | ||
741 | *(short *) tmp = 2; | ||
742 | strcpy(tmp + 2, ".badblocks"); | ||
743 | |||
744 | setup_tables(); | ||
745 | |||
746 | if (opt & 1) // -c ? | ||
747 | check_blocks(); | ||
748 | else if (listfile) | ||
749 | get_list_blocks(listfile); | ||
750 | |||
751 | if (version2) { | ||
752 | make_root_inode2(); | ||
753 | make_bad_inode2(); | ||
754 | } else { | ||
755 | make_root_inode(); | ||
756 | make_bad_inode(); | ||
757 | } | ||
758 | |||
759 | mark_good_blocks(); | ||
760 | write_tables(); | ||
761 | return 0; | ||
762 | } | ||
diff --git a/util-linux/mkswap.c b/util-linux/mkswap.c new file mode 100644 index 000000000..7baa3ecfb --- /dev/null +++ b/util-linux/mkswap.c | |||
@@ -0,0 +1,47 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* mkswap.c - format swap device (Linux v1 only) | ||
3 | * | ||
4 | * Copyright 2006 Rob Landley <rob@landley.net> | ||
5 | * | ||
6 | * Licensed under GPL version 2, see file LICENSE in this tarball for details. | ||
7 | */ | ||
8 | |||
9 | #include "busybox.h" | ||
10 | |||
11 | int mkswap_main(int argc, char *argv[]) | ||
12 | { | ||
13 | int fd, pagesize; | ||
14 | off_t len; | ||
15 | unsigned int hdr[129]; | ||
16 | |||
17 | // No options supported. | ||
18 | |||
19 | if (argc != 2) bb_show_usage(); | ||
20 | |||
21 | // Figure out how big the device is and announce our intentions. | ||
22 | |||
23 | fd = xopen(argv[1], O_RDWR); | ||
24 | len = fdlength(fd); | ||
25 | pagesize = getpagesize(); | ||
26 | printf("Setting up swapspace version 1, size = %"OFF_FMT"d bytes\n", | ||
27 | len - pagesize); | ||
28 | |||
29 | // Make a header. | ||
30 | |||
31 | memset(hdr, 0, sizeof(hdr)); | ||
32 | hdr[0] = 1; | ||
33 | hdr[1] = (len / pagesize) - 1; | ||
34 | |||
35 | // Write the header. Sync to disk because some kernel versions check | ||
36 | // signature on disk (not in cache) during swapon. | ||
37 | |||
38 | xlseek(fd, 1024, SEEK_SET); | ||
39 | xwrite(fd, hdr, sizeof(hdr)); | ||
40 | xlseek(fd, pagesize-10, SEEK_SET); | ||
41 | xwrite(fd, "SWAPSPACE2", 10); | ||
42 | fsync(fd); | ||
43 | |||
44 | if (ENABLE_FEATURE_CLEAN_UP) close(fd); | ||
45 | |||
46 | return 0; | ||
47 | } | ||
diff --git a/util-linux/more.c b/util-linux/more.c new file mode 100644 index 000000000..d048ace92 --- /dev/null +++ b/util-linux/more.c | |||
@@ -0,0 +1,177 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Mini more implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>. | ||
6 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
7 | * | ||
8 | * Latest version blended together by Erik Andersen <andersen@codepoet.org>, | ||
9 | * based on the original more implementation by Bruce, and code from the | ||
10 | * Debian boot-floppies team. | ||
11 | * | ||
12 | * Termios corrects by Vladimir Oleynik <dzo@simtreas.ru> | ||
13 | * | ||
14 | * Licensed under GPLv2 or later, see file License in this tarball for details. | ||
15 | */ | ||
16 | |||
17 | #include "busybox.h" | ||
18 | |||
19 | |||
20 | #if ENABLE_FEATURE_USE_TERMIOS | ||
21 | static int cin_fileno; | ||
22 | #include <termios.h> | ||
23 | #define setTermSettings(fd, argp) tcsetattr(fd, TCSANOW, argp) | ||
24 | #define getTermSettings(fd, argp) tcgetattr(fd, argp); | ||
25 | |||
26 | static struct termios initial_settings, new_settings; | ||
27 | |||
28 | static void set_tty_to_initial_mode(void) | ||
29 | { | ||
30 | setTermSettings(cin_fileno, &initial_settings); | ||
31 | } | ||
32 | |||
33 | static void gotsig(int sig) | ||
34 | { | ||
35 | putchar('\n'); | ||
36 | exit(EXIT_FAILURE); | ||
37 | } | ||
38 | #endif /* FEATURE_USE_TERMIOS */ | ||
39 | |||
40 | |||
41 | int more_main(int argc, char **argv) | ||
42 | { | ||
43 | int c, lines, input = 0; | ||
44 | int please_display_more_prompt = 0; | ||
45 | struct stat st; | ||
46 | FILE *file; | ||
47 | FILE *cin; | ||
48 | int len, page_height; | ||
49 | int terminal_width; | ||
50 | int terminal_height; | ||
51 | |||
52 | argc--; | ||
53 | argv++; | ||
54 | |||
55 | cin = stdin; | ||
56 | /* use input from terminal unless we do "more >outfile" */ | ||
57 | if (isatty(STDOUT_FILENO)) { | ||
58 | cin = fopen(CURRENT_TTY, "r"); | ||
59 | if (!cin) | ||
60 | cin = xfopen(CONSOLE_DEV, "r"); | ||
61 | please_display_more_prompt = 2; | ||
62 | #if ENABLE_FEATURE_USE_TERMIOS | ||
63 | cin_fileno = fileno(cin); | ||
64 | getTermSettings(cin_fileno, &initial_settings); | ||
65 | new_settings = initial_settings; | ||
66 | new_settings.c_lflag &= ~ICANON; | ||
67 | new_settings.c_lflag &= ~ECHO; | ||
68 | new_settings.c_cc[VMIN] = 1; | ||
69 | new_settings.c_cc[VTIME] = 0; | ||
70 | setTermSettings(cin_fileno, &new_settings); | ||
71 | atexit(set_tty_to_initial_mode); | ||
72 | signal(SIGINT, gotsig); | ||
73 | signal(SIGQUIT, gotsig); | ||
74 | signal(SIGTERM, gotsig); | ||
75 | #endif | ||
76 | } | ||
77 | |||
78 | do { | ||
79 | file = stdin; | ||
80 | if (argc != 0) { | ||
81 | file = fopen_or_warn(*argv, "r"); | ||
82 | if (!file) | ||
83 | goto loop; | ||
84 | } | ||
85 | |||
86 | st.st_size = 0; | ||
87 | fstat(fileno(file), &st); | ||
88 | |||
89 | please_display_more_prompt &= ~1; | ||
90 | |||
91 | get_terminal_width_height(fileno(cin), &terminal_width, &terminal_height); | ||
92 | if (terminal_height > 4) | ||
93 | terminal_height -= 2; | ||
94 | if (terminal_width > 0) | ||
95 | terminal_width -= 1; | ||
96 | |||
97 | len = 0; | ||
98 | lines = 0; | ||
99 | page_height = terminal_height; | ||
100 | while ((c = getc(file)) != EOF) { | ||
101 | |||
102 | if ((please_display_more_prompt & 3) == 3) { | ||
103 | len = printf("--More-- "); | ||
104 | if (file != stdin && st.st_size > 0) { | ||
105 | len += printf("(%d%% of %"OFF_FMT"d bytes)", | ||
106 | (int) (ftello(file)*100 / st.st_size), | ||
107 | st.st_size); | ||
108 | } | ||
109 | fflush(stdout); | ||
110 | |||
111 | /* | ||
112 | * We've just displayed the "--More--" prompt, so now we need | ||
113 | * to get input from the user. | ||
114 | */ | ||
115 | input = getc(cin); | ||
116 | #if !ENABLE_FEATURE_USE_TERMIOS | ||
117 | printf("\033[A"); /* up cursor */ | ||
118 | #endif | ||
119 | /* Erase the "More" message */ | ||
120 | printf("\r%*s\r", len, ""); | ||
121 | len = 0; | ||
122 | lines = 0; | ||
123 | page_height = terminal_height; | ||
124 | please_display_more_prompt &= ~1; | ||
125 | |||
126 | if (input == 'q') | ||
127 | goto end; | ||
128 | } | ||
129 | |||
130 | /* | ||
131 | * There are two input streams to worry about here: | ||
132 | * | ||
133 | * c : the character we are reading from the file being "mored" | ||
134 | * input : a character received from the keyboard | ||
135 | * | ||
136 | * If we hit a newline in the _file_ stream, we want to test and | ||
137 | * see if any characters have been hit in the _input_ stream. This | ||
138 | * allows the user to quit while in the middle of a file. | ||
139 | */ | ||
140 | if (c == '\n') { | ||
141 | /* increment by just one line if we are at | ||
142 | * the end of this line */ | ||
143 | if (input == '\n') | ||
144 | please_display_more_prompt |= 1; | ||
145 | /* Adjust the terminal height for any overlap, so that | ||
146 | * no lines get lost off the top. */ | ||
147 | if (len >= terminal_width) { | ||
148 | int quot, rem; | ||
149 | quot = len / terminal_width; | ||
150 | rem = len - (quot * terminal_width); | ||
151 | if (quot) { | ||
152 | if (rem) | ||
153 | page_height -= quot; | ||
154 | else | ||
155 | page_height -= (quot - 1); | ||
156 | } | ||
157 | } | ||
158 | if (++lines >= page_height) { | ||
159 | please_display_more_prompt |= 1; | ||
160 | } | ||
161 | len = 0; | ||
162 | } | ||
163 | /* | ||
164 | * If we just read a newline from the file being 'mored' and any | ||
165 | * key other than a return is hit, scroll by one page | ||
166 | */ | ||
167 | putc(c, stdout); | ||
168 | len++; | ||
169 | } | ||
170 | fclose(file); | ||
171 | fflush(stdout); | ||
172 | loop: | ||
173 | argv++; | ||
174 | } while (--argc > 0); | ||
175 | end: | ||
176 | return 0; | ||
177 | } | ||
diff --git a/util-linux/mount.c b/util-linux/mount.c new file mode 100644 index 000000000..f8ae1df19 --- /dev/null +++ b/util-linux/mount.c | |||
@@ -0,0 +1,1712 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Mini mount implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>. | ||
6 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
7 | * Copyright (C) 2005-2006 by Rob Landley <rob@landley.net> | ||
8 | * | ||
9 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
10 | */ | ||
11 | |||
12 | /* Design notes: There is no spec for mount. Remind me to write one. | ||
13 | |||
14 | mount_main() calls singlemount() which calls mount_it_now(). | ||
15 | |||
16 | mount_main() can loop through /etc/fstab for mount -a | ||
17 | singlemount() can loop through /etc/filesystems for fstype detection. | ||
18 | mount_it_now() does the actual mount. | ||
19 | */ | ||
20 | |||
21 | #include "busybox.h" | ||
22 | #include <mntent.h> | ||
23 | |||
24 | /* Needed for nfs support only... */ | ||
25 | #include <syslog.h> | ||
26 | #include <sys/utsname.h> | ||
27 | #undef TRUE | ||
28 | #undef FALSE | ||
29 | #include <rpc/rpc.h> | ||
30 | #include <rpc/pmap_prot.h> | ||
31 | #include <rpc/pmap_clnt.h> | ||
32 | |||
33 | |||
34 | // Not real flags, but we want to be able to check for this. | ||
35 | enum { | ||
36 | MOUNT_USERS = (1<<28)*ENABLE_DESKTOP, | ||
37 | MOUNT_NOAUTO = (1<<29), | ||
38 | MOUNT_SWAP = (1<<30), | ||
39 | }; | ||
40 | // TODO: more "user" flag compatibility. | ||
41 | // "user" option (from mount manpage): | ||
42 | // Only the user that mounted a filesystem can unmount it again. | ||
43 | // If any user should be able to unmount, then use users instead of user | ||
44 | // in the fstab line. The owner option is similar to the user option, | ||
45 | // with the restriction that the user must be the owner of the special file. | ||
46 | // This may be useful e.g. for /dev/fd if a login script makes | ||
47 | // the console user owner of this device. | ||
48 | |||
49 | /* Standard mount options (from -o options or --options), with corresponding | ||
50 | * flags */ | ||
51 | |||
52 | struct { | ||
53 | char *name; | ||
54 | long flags; | ||
55 | } static mount_options[] = { | ||
56 | // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs. | ||
57 | |||
58 | USE_FEATURE_MOUNT_LOOP( | ||
59 | {"loop", 0}, | ||
60 | ) | ||
61 | |||
62 | USE_FEATURE_MOUNT_FSTAB( | ||
63 | {"defaults", 0}, | ||
64 | /* {"quiet", 0}, - do not filter out, vfat wants to see it */ | ||
65 | {"noauto", MOUNT_NOAUTO}, | ||
66 | {"swap", MOUNT_SWAP}, | ||
67 | USE_DESKTOP({"user", MOUNT_USERS},) | ||
68 | USE_DESKTOP({"users", MOUNT_USERS},) | ||
69 | ) | ||
70 | |||
71 | USE_FEATURE_MOUNT_FLAGS( | ||
72 | // vfs flags | ||
73 | {"nosuid", MS_NOSUID}, | ||
74 | {"suid", ~MS_NOSUID}, | ||
75 | {"dev", ~MS_NODEV}, | ||
76 | {"nodev", MS_NODEV}, | ||
77 | {"exec", ~MS_NOEXEC}, | ||
78 | {"noexec", MS_NOEXEC}, | ||
79 | {"sync", MS_SYNCHRONOUS}, | ||
80 | {"async", ~MS_SYNCHRONOUS}, | ||
81 | {"atime", ~MS_NOATIME}, | ||
82 | {"noatime", MS_NOATIME}, | ||
83 | {"diratime", ~MS_NODIRATIME}, | ||
84 | {"nodiratime", MS_NODIRATIME}, | ||
85 | {"loud", ~MS_SILENT}, | ||
86 | |||
87 | // action flags | ||
88 | |||
89 | {"bind", MS_BIND}, | ||
90 | {"move", MS_MOVE}, | ||
91 | {"shared", MS_SHARED}, | ||
92 | {"slave", MS_SLAVE}, | ||
93 | {"private", MS_PRIVATE}, | ||
94 | {"unbindable", MS_UNBINDABLE}, | ||
95 | {"rshared", MS_SHARED|MS_RECURSIVE}, | ||
96 | {"rslave", MS_SLAVE|MS_RECURSIVE}, | ||
97 | {"rprivate", MS_SLAVE|MS_RECURSIVE}, | ||
98 | {"runbindable", MS_UNBINDABLE|MS_RECURSIVE}, | ||
99 | ) | ||
100 | |||
101 | // Always understood. | ||
102 | |||
103 | {"ro", MS_RDONLY}, // vfs flag | ||
104 | {"rw", ~MS_RDONLY}, // vfs flag | ||
105 | {"remount", MS_REMOUNT}, // action flag | ||
106 | }; | ||
107 | |||
108 | #define VECTOR_SIZE(v) (sizeof(v) / sizeof((v)[0])) | ||
109 | |||
110 | /* Append mount options to string */ | ||
111 | static void append_mount_options(char **oldopts, char *newopts) | ||
112 | { | ||
113 | if (*oldopts && **oldopts) { | ||
114 | /* do not insert options which are already there */ | ||
115 | while (newopts[0]) { | ||
116 | char *p; | ||
117 | int len = strlen(newopts); | ||
118 | p = strchr(newopts, ','); | ||
119 | if (p) len = p - newopts; | ||
120 | p = *oldopts; | ||
121 | while (1) { | ||
122 | if (!strncmp(p, newopts, len) | ||
123 | && (p[len]==',' || p[len]==0)) | ||
124 | goto skip; | ||
125 | p = strchr(p,','); | ||
126 | if(!p) break; | ||
127 | p++; | ||
128 | } | ||
129 | p = xasprintf("%s,%.*s", *oldopts, len, newopts); | ||
130 | free(*oldopts); | ||
131 | *oldopts = p; | ||
132 | skip: | ||
133 | newopts += len; | ||
134 | while (newopts[0] == ',') newopts++; | ||
135 | } | ||
136 | } else { | ||
137 | if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts); | ||
138 | *oldopts = xstrdup(newopts); | ||
139 | } | ||
140 | } | ||
141 | |||
142 | /* Use the mount_options list to parse options into flags. | ||
143 | * Also return list of unrecognized options if unrecognized!=NULL */ | ||
144 | static int parse_mount_options(char *options, char **unrecognized) | ||
145 | { | ||
146 | int flags = MS_SILENT; | ||
147 | |||
148 | // Loop through options | ||
149 | for (;;) { | ||
150 | int i; | ||
151 | char *comma = strchr(options, ','); | ||
152 | |||
153 | if (comma) *comma = 0; | ||
154 | |||
155 | // Find this option in mount_options | ||
156 | for (i = 0; i < VECTOR_SIZE(mount_options); i++) { | ||
157 | if (!strcasecmp(mount_options[i].name, options)) { | ||
158 | long fl = mount_options[i].flags; | ||
159 | if (fl < 0) flags &= fl; | ||
160 | else flags |= fl; | ||
161 | break; | ||
162 | } | ||
163 | } | ||
164 | // If unrecognized not NULL, append unrecognized mount options */ | ||
165 | if (unrecognized && i == VECTOR_SIZE(mount_options)) { | ||
166 | // Add it to strflags, to pass on to kernel | ||
167 | i = *unrecognized ? strlen(*unrecognized) : 0; | ||
168 | *unrecognized = xrealloc(*unrecognized, i+strlen(options)+2); | ||
169 | |||
170 | // Comma separated if it's not the first one | ||
171 | if (i) (*unrecognized)[i++] = ','; | ||
172 | strcpy((*unrecognized)+i, options); | ||
173 | } | ||
174 | |||
175 | // Advance to next option, or finish | ||
176 | if (comma) { | ||
177 | *comma = ','; | ||
178 | options = ++comma; | ||
179 | } else break; | ||
180 | } | ||
181 | |||
182 | return flags; | ||
183 | } | ||
184 | |||
185 | // Return a list of all block device backed filesystems | ||
186 | |||
187 | static llist_t *get_block_backed_filesystems(void) | ||
188 | { | ||
189 | static const char *const filesystems[] = { | ||
190 | "/etc/filesystems", | ||
191 | "/proc/filesystems", | ||
192 | 0 | ||
193 | }; | ||
194 | char *fs, *buf; | ||
195 | llist_t *list = 0; | ||
196 | int i; | ||
197 | FILE *f; | ||
198 | |||
199 | for (i = 0; filesystems[i]; i++) { | ||
200 | f = fopen(filesystems[i], "r"); | ||
201 | if (!f) continue; | ||
202 | |||
203 | while ((buf = xmalloc_getline(f)) != 0) { | ||
204 | if (!strncmp(buf, "nodev", 5) && isspace(buf[5])) | ||
205 | continue; | ||
206 | fs = skip_whitespace(buf); | ||
207 | if (*fs=='#' || *fs=='*' || !*fs) continue; | ||
208 | |||
209 | llist_add_to_end(&list, xstrdup(fs)); | ||
210 | free(buf); | ||
211 | } | ||
212 | if (ENABLE_FEATURE_CLEAN_UP) fclose(f); | ||
213 | } | ||
214 | |||
215 | return list; | ||
216 | } | ||
217 | |||
218 | llist_t *fslist = 0; | ||
219 | |||
220 | #if ENABLE_FEATURE_CLEAN_UP | ||
221 | static void delete_block_backed_filesystems(void) | ||
222 | { | ||
223 | llist_free(fslist, free); | ||
224 | } | ||
225 | #else | ||
226 | void delete_block_backed_filesystems(void); | ||
227 | #endif | ||
228 | |||
229 | #if ENABLE_FEATURE_MTAB_SUPPORT | ||
230 | static int useMtab = 1; | ||
231 | static int fakeIt; | ||
232 | #else | ||
233 | #define useMtab 0 | ||
234 | #define fakeIt 0 | ||
235 | #endif | ||
236 | |||
237 | // Perform actual mount of specific filesystem at specific location. | ||
238 | // NB: mp->xxx fields may be trashed on exit | ||
239 | static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts) | ||
240 | { | ||
241 | int rc = 0; | ||
242 | |||
243 | if (fakeIt) goto mtab; | ||
244 | |||
245 | // Mount, with fallback to read-only if necessary. | ||
246 | |||
247 | for (;;) { | ||
248 | rc = mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type, | ||
249 | vfsflags, filteropts); | ||
250 | if (!rc || (vfsflags&MS_RDONLY) || (errno!=EACCES && errno!=EROFS)) | ||
251 | break; | ||
252 | bb_error_msg("%s is write-protected, mounting read-only", | ||
253 | mp->mnt_fsname); | ||
254 | vfsflags |= MS_RDONLY; | ||
255 | } | ||
256 | |||
257 | // Abort entirely if permission denied. | ||
258 | |||
259 | if (rc && errno == EPERM) | ||
260 | bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); | ||
261 | |||
262 | /* If the mount was successful, and we're maintaining an old-style | ||
263 | * mtab file by hand, add the new entry to it now. */ | ||
264 | mtab: | ||
265 | if (ENABLE_FEATURE_MTAB_SUPPORT && useMtab && !rc && !(vfsflags & MS_REMOUNT)) { | ||
266 | char *fsname; | ||
267 | FILE *mountTable = setmntent(bb_path_mtab_file, "a+"); | ||
268 | int i; | ||
269 | |||
270 | if (!mountTable) { | ||
271 | bb_error_msg("no %s",bb_path_mtab_file); | ||
272 | goto ret; | ||
273 | } | ||
274 | |||
275 | // Add vfs string flags | ||
276 | |||
277 | for (i=0; mount_options[i].flags != MS_REMOUNT; i++) | ||
278 | if (mount_options[i].flags > 0 && (mount_options[i].flags & vfsflags)) | ||
279 | append_mount_options(&(mp->mnt_opts), mount_options[i].name); | ||
280 | |||
281 | // Remove trailing / (if any) from directory we mounted on | ||
282 | |||
283 | i = strlen(mp->mnt_dir) - 1; | ||
284 | if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = 0; | ||
285 | |||
286 | // Convert to canonical pathnames as needed | ||
287 | |||
288 | mp->mnt_dir = bb_simplify_path(mp->mnt_dir); | ||
289 | fsname = 0; | ||
290 | if (!mp->mnt_type || !*mp->mnt_type) { /* bind mount */ | ||
291 | mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname); | ||
292 | mp->mnt_type = "bind"; | ||
293 | } | ||
294 | mp->mnt_freq = mp->mnt_passno = 0; | ||
295 | |||
296 | // Write and close. | ||
297 | |||
298 | addmntent(mountTable, mp); | ||
299 | endmntent(mountTable); | ||
300 | if (ENABLE_FEATURE_CLEAN_UP) { | ||
301 | free(mp->mnt_dir); | ||
302 | free(fsname); | ||
303 | } | ||
304 | } | ||
305 | ret: | ||
306 | return rc; | ||
307 | } | ||
308 | |||
309 | #if ENABLE_FEATURE_MOUNT_NFS | ||
310 | |||
311 | /* | ||
312 | * Linux NFS mount | ||
313 | * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com> | ||
314 | * | ||
315 | * Licensed under GPLv2, see file LICENSE in this tarball for details. | ||
316 | * | ||
317 | * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port | ||
318 | * numbers to be specified on the command line. | ||
319 | * | ||
320 | * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>: | ||
321 | * Omit the call to connect() for Linux version 1.3.11 or later. | ||
322 | * | ||
323 | * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com> | ||
324 | * Implemented the "bg", "fg" and "retry" mount options for NFS. | ||
325 | * | ||
326 | * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org> | ||
327 | * - added Native Language Support | ||
328 | * | ||
329 | * Modified by Olaf Kirch and Trond Myklebust for new NFS code, | ||
330 | * plus NFSv3 stuff. | ||
331 | */ | ||
332 | |||
333 | /* This is just a warning of a common mistake. Possibly this should be a | ||
334 | * uclibc faq entry rather than in busybox... */ | ||
335 | #if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__) | ||
336 | #error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support." | ||
337 | #endif | ||
338 | |||
339 | #define MOUNTPORT 635 | ||
340 | #define MNTPATHLEN 1024 | ||
341 | #define MNTNAMLEN 255 | ||
342 | #define FHSIZE 32 | ||
343 | #define FHSIZE3 64 | ||
344 | |||
345 | typedef char fhandle[FHSIZE]; | ||
346 | |||
347 | typedef struct { | ||
348 | unsigned int fhandle3_len; | ||
349 | char *fhandle3_val; | ||
350 | } fhandle3; | ||
351 | |||
352 | enum mountstat3 { | ||
353 | MNT_OK = 0, | ||
354 | MNT3ERR_PERM = 1, | ||
355 | MNT3ERR_NOENT = 2, | ||
356 | MNT3ERR_IO = 5, | ||
357 | MNT3ERR_ACCES = 13, | ||
358 | MNT3ERR_NOTDIR = 20, | ||
359 | MNT3ERR_INVAL = 22, | ||
360 | MNT3ERR_NAMETOOLONG = 63, | ||
361 | MNT3ERR_NOTSUPP = 10004, | ||
362 | MNT3ERR_SERVERFAULT = 10006, | ||
363 | }; | ||
364 | typedef enum mountstat3 mountstat3; | ||
365 | |||
366 | struct fhstatus { | ||
367 | unsigned int fhs_status; | ||
368 | union { | ||
369 | fhandle fhs_fhandle; | ||
370 | } fhstatus_u; | ||
371 | }; | ||
372 | typedef struct fhstatus fhstatus; | ||
373 | |||
374 | struct mountres3_ok { | ||
375 | fhandle3 fhandle; | ||
376 | struct { | ||
377 | unsigned int auth_flavours_len; | ||
378 | char *auth_flavours_val; | ||
379 | } auth_flavours; | ||
380 | }; | ||
381 | typedef struct mountres3_ok mountres3_ok; | ||
382 | |||
383 | struct mountres3 { | ||
384 | mountstat3 fhs_status; | ||
385 | union { | ||
386 | mountres3_ok mountinfo; | ||
387 | } mountres3_u; | ||
388 | }; | ||
389 | typedef struct mountres3 mountres3; | ||
390 | |||
391 | typedef char *dirpath; | ||
392 | |||
393 | typedef char *name; | ||
394 | |||
395 | typedef struct mountbody *mountlist; | ||
396 | |||
397 | struct mountbody { | ||
398 | name ml_hostname; | ||
399 | dirpath ml_directory; | ||
400 | mountlist ml_next; | ||
401 | }; | ||
402 | typedef struct mountbody mountbody; | ||
403 | |||
404 | typedef struct groupnode *groups; | ||
405 | |||
406 | struct groupnode { | ||
407 | name gr_name; | ||
408 | groups gr_next; | ||
409 | }; | ||
410 | typedef struct groupnode groupnode; | ||
411 | |||
412 | typedef struct exportnode *exports; | ||
413 | |||
414 | struct exportnode { | ||
415 | dirpath ex_dir; | ||
416 | groups ex_groups; | ||
417 | exports ex_next; | ||
418 | }; | ||
419 | typedef struct exportnode exportnode; | ||
420 | |||
421 | struct ppathcnf { | ||
422 | int pc_link_max; | ||
423 | short pc_max_canon; | ||
424 | short pc_max_input; | ||
425 | short pc_name_max; | ||
426 | short pc_path_max; | ||
427 | short pc_pipe_buf; | ||
428 | u_char pc_vdisable; | ||
429 | char pc_xxx; | ||
430 | short pc_mask[2]; | ||
431 | }; | ||
432 | typedef struct ppathcnf ppathcnf; | ||
433 | |||
434 | #define MOUNTPROG 100005 | ||
435 | #define MOUNTVERS 1 | ||
436 | |||
437 | #define MOUNTPROC_NULL 0 | ||
438 | #define MOUNTPROC_MNT 1 | ||
439 | #define MOUNTPROC_DUMP 2 | ||
440 | #define MOUNTPROC_UMNT 3 | ||
441 | #define MOUNTPROC_UMNTALL 4 | ||
442 | #define MOUNTPROC_EXPORT 5 | ||
443 | #define MOUNTPROC_EXPORTALL 6 | ||
444 | |||
445 | #define MOUNTVERS_POSIX 2 | ||
446 | |||
447 | #define MOUNTPROC_PATHCONF 7 | ||
448 | |||
449 | #define MOUNT_V3 3 | ||
450 | |||
451 | #define MOUNTPROC3_NULL 0 | ||
452 | #define MOUNTPROC3_MNT 1 | ||
453 | #define MOUNTPROC3_DUMP 2 | ||
454 | #define MOUNTPROC3_UMNT 3 | ||
455 | #define MOUNTPROC3_UMNTALL 4 | ||
456 | #define MOUNTPROC3_EXPORT 5 | ||
457 | |||
458 | enum { | ||
459 | #ifndef NFS_FHSIZE | ||
460 | NFS_FHSIZE = 32, | ||
461 | #endif | ||
462 | #ifndef NFS_PORT | ||
463 | NFS_PORT = 2049 | ||
464 | #endif | ||
465 | }; | ||
466 | |||
467 | /* | ||
468 | * We want to be able to compile mount on old kernels in such a way | ||
469 | * that the binary will work well on more recent kernels. | ||
470 | * Thus, if necessary we teach nfsmount.c the structure of new fields | ||
471 | * that will come later. | ||
472 | * | ||
473 | * Moreover, the new kernel includes conflict with glibc includes | ||
474 | * so it is easiest to ignore the kernel altogether (at compile time). | ||
475 | */ | ||
476 | |||
477 | struct nfs2_fh { | ||
478 | char data[32]; | ||
479 | }; | ||
480 | struct nfs3_fh { | ||
481 | unsigned short size; | ||
482 | unsigned char data[64]; | ||
483 | }; | ||
484 | |||
485 | struct nfs_mount_data { | ||
486 | int version; /* 1 */ | ||
487 | int fd; /* 1 */ | ||
488 | struct nfs2_fh old_root; /* 1 */ | ||
489 | int flags; /* 1 */ | ||
490 | int rsize; /* 1 */ | ||
491 | int wsize; /* 1 */ | ||
492 | int timeo; /* 1 */ | ||
493 | int retrans; /* 1 */ | ||
494 | int acregmin; /* 1 */ | ||
495 | int acregmax; /* 1 */ | ||
496 | int acdirmin; /* 1 */ | ||
497 | int acdirmax; /* 1 */ | ||
498 | struct sockaddr_in addr; /* 1 */ | ||
499 | char hostname[256]; /* 1 */ | ||
500 | int namlen; /* 2 */ | ||
501 | unsigned int bsize; /* 3 */ | ||
502 | struct nfs3_fh root; /* 4 */ | ||
503 | }; | ||
504 | |||
505 | /* bits in the flags field */ | ||
506 | enum { | ||
507 | NFS_MOUNT_SOFT = 0x0001, /* 1 */ | ||
508 | NFS_MOUNT_INTR = 0x0002, /* 1 */ | ||
509 | NFS_MOUNT_SECURE = 0x0004, /* 1 */ | ||
510 | NFS_MOUNT_POSIX = 0x0008, /* 1 */ | ||
511 | NFS_MOUNT_NOCTO = 0x0010, /* 1 */ | ||
512 | NFS_MOUNT_NOAC = 0x0020, /* 1 */ | ||
513 | NFS_MOUNT_TCP = 0x0040, /* 2 */ | ||
514 | NFS_MOUNT_VER3 = 0x0080, /* 3 */ | ||
515 | NFS_MOUNT_KERBEROS = 0x0100, /* 3 */ | ||
516 | NFS_MOUNT_NONLM = 0x0200 /* 3 */ | ||
517 | }; | ||
518 | |||
519 | |||
520 | /* | ||
521 | * We need to translate between nfs status return values and | ||
522 | * the local errno values which may not be the same. | ||
523 | * | ||
524 | * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno: | ||
525 | * "after #include <errno.h> the symbol errno is reserved for any use, | ||
526 | * it cannot even be used as a struct tag or field name". | ||
527 | */ | ||
528 | |||
529 | #ifndef EDQUOT | ||
530 | #define EDQUOT ENOSPC | ||
531 | #endif | ||
532 | |||
533 | // Convert each NFSERR_BLAH into EBLAH | ||
534 | |||
535 | static const struct { | ||
536 | int stat; | ||
537 | int errnum; | ||
538 | } nfs_errtbl[] = { | ||
539 | {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST}, | ||
540 | {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG}, | ||
541 | {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT}, | ||
542 | {70,ESTALE}, {71,EREMOTE}, {-1,EIO} | ||
543 | }; | ||
544 | |||
545 | static char *nfs_strerror(int status) | ||
546 | { | ||
547 | int i; | ||
548 | static char buf[sizeof("unknown nfs status return value: ") + sizeof(int)*3]; | ||
549 | |||
550 | for (i = 0; nfs_errtbl[i].stat != -1; i++) { | ||
551 | if (nfs_errtbl[i].stat == status) | ||
552 | return strerror(nfs_errtbl[i].errnum); | ||
553 | } | ||
554 | sprintf(buf, "unknown nfs status return value: %d", status); | ||
555 | return buf; | ||
556 | } | ||
557 | |||
558 | static bool_t xdr_fhandle(XDR *xdrs, fhandle objp) | ||
559 | { | ||
560 | if (!xdr_opaque(xdrs, objp, FHSIZE)) | ||
561 | return FALSE; | ||
562 | return TRUE; | ||
563 | } | ||
564 | |||
565 | static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp) | ||
566 | { | ||
567 | if (!xdr_u_int(xdrs, &objp->fhs_status)) | ||
568 | return FALSE; | ||
569 | switch (objp->fhs_status) { | ||
570 | case 0: | ||
571 | if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle)) | ||
572 | return FALSE; | ||
573 | break; | ||
574 | default: | ||
575 | break; | ||
576 | } | ||
577 | return TRUE; | ||
578 | } | ||
579 | |||
580 | static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp) | ||
581 | { | ||
582 | if (!xdr_string(xdrs, objp, MNTPATHLEN)) | ||
583 | return FALSE; | ||
584 | return TRUE; | ||
585 | } | ||
586 | |||
587 | static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp) | ||
588 | { | ||
589 | if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3)) | ||
590 | return FALSE; | ||
591 | return TRUE; | ||
592 | } | ||
593 | |||
594 | static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp) | ||
595 | { | ||
596 | if (!xdr_fhandle3(xdrs, &objp->fhandle)) | ||
597 | return FALSE; | ||
598 | if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0, | ||
599 | sizeof (int), (xdrproc_t) xdr_int)) | ||
600 | return FALSE; | ||
601 | return TRUE; | ||
602 | } | ||
603 | |||
604 | static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp) | ||
605 | { | ||
606 | if (!xdr_enum(xdrs, (enum_t *) objp)) | ||
607 | return FALSE; | ||
608 | return TRUE; | ||
609 | } | ||
610 | |||
611 | static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp) | ||
612 | { | ||
613 | if (!xdr_mountstat3(xdrs, &objp->fhs_status)) | ||
614 | return FALSE; | ||
615 | switch (objp->fhs_status) { | ||
616 | case MNT_OK: | ||
617 | if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo)) | ||
618 | return FALSE; | ||
619 | break; | ||
620 | default: | ||
621 | break; | ||
622 | } | ||
623 | return TRUE; | ||
624 | } | ||
625 | |||
626 | #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2) | ||
627 | |||
628 | /* | ||
629 | * nfs_mount_version according to the sources seen at compile time. | ||
630 | */ | ||
631 | static int nfs_mount_version; | ||
632 | static int kernel_version; | ||
633 | |||
634 | /* | ||
635 | * Unfortunately, the kernel prints annoying console messages | ||
636 | * in case of an unexpected nfs mount version (instead of | ||
637 | * just returning some error). Therefore we'll have to try | ||
638 | * and figure out what version the kernel expects. | ||
639 | * | ||
640 | * Variables: | ||
641 | * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time | ||
642 | * NFS_MOUNT_VERSION: these nfsmount sources at compile time | ||
643 | * nfs_mount_version: version this source and running kernel can handle | ||
644 | */ | ||
645 | static void | ||
646 | find_kernel_nfs_mount_version(void) | ||
647 | { | ||
648 | if (kernel_version) | ||
649 | return; | ||
650 | |||
651 | nfs_mount_version = 4; /* default */ | ||
652 | |||
653 | kernel_version = get_linux_version_code(); | ||
654 | if (kernel_version) { | ||
655 | if (kernel_version < KERNEL_VERSION(2,1,32)) | ||
656 | nfs_mount_version = 1; | ||
657 | else if (kernel_version < KERNEL_VERSION(2,2,18) || | ||
658 | (kernel_version >= KERNEL_VERSION(2,3,0) && | ||
659 | kernel_version < KERNEL_VERSION(2,3,99))) | ||
660 | nfs_mount_version = 3; | ||
661 | /* else v4 since 2.3.99pre4 */ | ||
662 | } | ||
663 | } | ||
664 | |||
665 | static struct pmap * | ||
666 | get_mountport(struct sockaddr_in *server_addr, | ||
667 | long unsigned prog, | ||
668 | long unsigned version, | ||
669 | long unsigned proto, | ||
670 | long unsigned port) | ||
671 | { | ||
672 | struct pmaplist *pmap; | ||
673 | static struct pmap p = {0, 0, 0, 0}; | ||
674 | |||
675 | server_addr->sin_port = PMAPPORT; | ||
676 | pmap = pmap_getmaps(server_addr); | ||
677 | |||
678 | if (version > MAX_NFSPROT) | ||
679 | version = MAX_NFSPROT; | ||
680 | if (!prog) | ||
681 | prog = MOUNTPROG; | ||
682 | p.pm_prog = prog; | ||
683 | p.pm_vers = version; | ||
684 | p.pm_prot = proto; | ||
685 | p.pm_port = port; | ||
686 | |||
687 | while (pmap) { | ||
688 | if (pmap->pml_map.pm_prog != prog) | ||
689 | goto next; | ||
690 | if (!version && p.pm_vers > pmap->pml_map.pm_vers) | ||
691 | goto next; | ||
692 | if (version > 2 && pmap->pml_map.pm_vers != version) | ||
693 | goto next; | ||
694 | if (version && version <= 2 && pmap->pml_map.pm_vers > 2) | ||
695 | goto next; | ||
696 | if (pmap->pml_map.pm_vers > MAX_NFSPROT || | ||
697 | (proto && p.pm_prot && pmap->pml_map.pm_prot != proto) || | ||
698 | (port && pmap->pml_map.pm_port != port)) | ||
699 | goto next; | ||
700 | memcpy(&p, &pmap->pml_map, sizeof(p)); | ||
701 | next: | ||
702 | pmap = pmap->pml_next; | ||
703 | } | ||
704 | if (!p.pm_vers) | ||
705 | p.pm_vers = MOUNTVERS; | ||
706 | if (!p.pm_port) | ||
707 | p.pm_port = MOUNTPORT; | ||
708 | if (!p.pm_prot) | ||
709 | p.pm_prot = IPPROTO_TCP; | ||
710 | return &p; | ||
711 | } | ||
712 | |||
713 | static int daemonize(void) | ||
714 | { | ||
715 | int fd; | ||
716 | int pid = fork(); | ||
717 | if (pid < 0) /* error */ | ||
718 | return -errno; | ||
719 | if (pid > 0) /* parent */ | ||
720 | return 0; | ||
721 | /* child */ | ||
722 | fd = xopen(bb_dev_null, O_RDWR); | ||
723 | dup2(fd, 0); | ||
724 | dup2(fd, 1); | ||
725 | dup2(fd, 2); | ||
726 | if (fd > 2) close(fd); | ||
727 | setsid(); | ||
728 | openlog(applet_name, LOG_PID, LOG_DAEMON); | ||
729 | logmode = LOGMODE_SYSLOG; | ||
730 | return 1; | ||
731 | } | ||
732 | |||
733 | // TODO | ||
734 | static inline int we_saw_this_host_before(const char *hostname) | ||
735 | { | ||
736 | return 0; | ||
737 | } | ||
738 | |||
739 | /* RPC strerror analogs are terminally idiotic: | ||
740 | * *mandatory* prefix and \n at end. | ||
741 | * This hopefully helps. Usage: | ||
742 | * error_msg_rpc(clnt_*error*(" ")) */ | ||
743 | static void error_msg_rpc(const char *msg) | ||
744 | { | ||
745 | int len; | ||
746 | while (msg[0] == ' ' || msg[0] == ':') msg++; | ||
747 | len = strlen(msg); | ||
748 | while (len && msg[len-1] == '\n') len--; | ||
749 | bb_error_msg("%.*s", len, msg); | ||
750 | } | ||
751 | |||
752 | // NB: mp->xxx fields may be trashed on exit | ||
753 | static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts) | ||
754 | { | ||
755 | CLIENT *mclient; | ||
756 | char *hostname; | ||
757 | char *pathname; | ||
758 | char *mounthost; | ||
759 | struct nfs_mount_data data; | ||
760 | char *opt; | ||
761 | struct hostent *hp; | ||
762 | struct sockaddr_in server_addr; | ||
763 | struct sockaddr_in mount_server_addr; | ||
764 | int msock, fsock; | ||
765 | union { | ||
766 | struct fhstatus nfsv2; | ||
767 | struct mountres3 nfsv3; | ||
768 | } status; | ||
769 | int daemonized; | ||
770 | char *s; | ||
771 | int port; | ||
772 | int mountport; | ||
773 | int proto; | ||
774 | int bg; | ||
775 | int soft; | ||
776 | int intr; | ||
777 | int posix; | ||
778 | int nocto; | ||
779 | int noac; | ||
780 | int nolock; | ||
781 | int retry; | ||
782 | int tcp; | ||
783 | int mountprog; | ||
784 | int mountvers; | ||
785 | int nfsprog; | ||
786 | int nfsvers; | ||
787 | int retval; | ||
788 | |||
789 | find_kernel_nfs_mount_version(); | ||
790 | |||
791 | daemonized = 0; | ||
792 | mounthost = NULL; | ||
793 | retval = ETIMEDOUT; | ||
794 | msock = fsock = -1; | ||
795 | mclient = NULL; | ||
796 | |||
797 | /* NB: hostname, mounthost, filteropts must be free()d prior to return */ | ||
798 | |||
799 | filteropts = xstrdup(filteropts); /* going to trash it later... */ | ||
800 | |||
801 | hostname = xstrdup(mp->mnt_fsname); | ||
802 | /* mount_main() guarantees that ':' is there */ | ||
803 | s = strchr(hostname, ':'); | ||
804 | pathname = s + 1; | ||
805 | *s = '\0'; | ||
806 | /* Ignore all but first hostname in replicated mounts | ||
807 | until they can be fully supported. (mack@sgi.com) */ | ||
808 | s = strchr(hostname, ','); | ||
809 | if (s) { | ||
810 | *s = '\0'; | ||
811 | bb_error_msg("warning: multiple hostnames not supported"); | ||
812 | } | ||
813 | |||
814 | server_addr.sin_family = AF_INET; | ||
815 | if (!inet_aton(hostname, &server_addr.sin_addr)) { | ||
816 | hp = gethostbyname(hostname); | ||
817 | if (hp == NULL) { | ||
818 | bb_herror_msg("%s", hostname); | ||
819 | goto fail; | ||
820 | } | ||
821 | if (hp->h_length > sizeof(struct in_addr)) { | ||
822 | bb_error_msg("got bad hp->h_length"); | ||
823 | hp->h_length = sizeof(struct in_addr); | ||
824 | } | ||
825 | memcpy(&server_addr.sin_addr, | ||
826 | hp->h_addr, hp->h_length); | ||
827 | } | ||
828 | |||
829 | memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr)); | ||
830 | |||
831 | /* add IP address to mtab options for use when unmounting */ | ||
832 | |||
833 | if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */ | ||
834 | mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr)); | ||
835 | } else { | ||
836 | char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts, | ||
837 | mp->mnt_opts[0] ? "," : "", | ||
838 | inet_ntoa(server_addr.sin_addr)); | ||
839 | free(mp->mnt_opts); | ||
840 | mp->mnt_opts = tmp; | ||
841 | } | ||
842 | |||
843 | /* Set default options. | ||
844 | * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to | ||
845 | * let the kernel decide. | ||
846 | * timeo is filled in after we know whether it'll be TCP or UDP. */ | ||
847 | memset(&data, 0, sizeof(data)); | ||
848 | data.retrans = 3; | ||
849 | data.acregmin = 3; | ||
850 | data.acregmax = 60; | ||
851 | data.acdirmin = 30; | ||
852 | data.acdirmax = 60; | ||
853 | data.namlen = NAME_MAX; | ||
854 | |||
855 | bg = 0; | ||
856 | soft = 0; | ||
857 | intr = 0; | ||
858 | posix = 0; | ||
859 | nocto = 0; | ||
860 | nolock = 0; | ||
861 | noac = 0; | ||
862 | retry = 10000; /* 10000 minutes ~ 1 week */ | ||
863 | tcp = 0; | ||
864 | |||
865 | mountprog = MOUNTPROG; | ||
866 | mountvers = 0; | ||
867 | port = 0; | ||
868 | mountport = 0; | ||
869 | nfsprog = 100003; | ||
870 | nfsvers = 0; | ||
871 | |||
872 | /* parse options */ | ||
873 | |||
874 | for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) { | ||
875 | char *opteq = strchr(opt, '='); | ||
876 | if (opteq) { | ||
877 | const char *const options[] = { | ||
878 | /* 0 */ "rsize", | ||
879 | /* 1 */ "wsize", | ||
880 | /* 2 */ "timeo", | ||
881 | /* 3 */ "retrans", | ||
882 | /* 4 */ "acregmin", | ||
883 | /* 5 */ "acregmax", | ||
884 | /* 6 */ "acdirmin", | ||
885 | /* 7 */ "acdirmax", | ||
886 | /* 8 */ "actimeo", | ||
887 | /* 9 */ "retry", | ||
888 | /* 10 */ "port", | ||
889 | /* 11 */ "mountport", | ||
890 | /* 12 */ "mounthost", | ||
891 | /* 13 */ "mountprog", | ||
892 | /* 14 */ "mountvers", | ||
893 | /* 15 */ "nfsprog", | ||
894 | /* 16 */ "nfsvers", | ||
895 | /* 17 */ "vers", | ||
896 | /* 18 */ "proto", | ||
897 | /* 19 */ "namlen", | ||
898 | /* 20 */ "addr", | ||
899 | NULL | ||
900 | }; | ||
901 | int val = xatoi_u(opteq + 1); | ||
902 | *opteq = '\0'; | ||
903 | switch (index_in_str_array(options, opt)) { | ||
904 | case 0: // "rsize" | ||
905 | data.rsize = val; | ||
906 | break; | ||
907 | case 1: // "wsize" | ||
908 | data.wsize = val; | ||
909 | break; | ||
910 | case 2: // "timeo" | ||
911 | data.timeo = val; | ||
912 | break; | ||
913 | case 3: // "retrans" | ||
914 | data.retrans = val; | ||
915 | break; | ||
916 | case 4: // "acregmin" | ||
917 | data.acregmin = val; | ||
918 | break; | ||
919 | case 5: // "acregmax" | ||
920 | data.acregmax = val; | ||
921 | break; | ||
922 | case 6: // "acdirmin" | ||
923 | data.acdirmin = val; | ||
924 | break; | ||
925 | case 7: // "acdirmax" | ||
926 | data.acdirmax = val; | ||
927 | break; | ||
928 | case 8: // "actimeo" | ||
929 | data.acregmin = val; | ||
930 | data.acregmax = val; | ||
931 | data.acdirmin = val; | ||
932 | data.acdirmax = val; | ||
933 | break; | ||
934 | case 9: // "retry" | ||
935 | retry = val; | ||
936 | break; | ||
937 | case 10: // "port" | ||
938 | port = val; | ||
939 | break; | ||
940 | case 11: // "mountport" | ||
941 | mountport = val; | ||
942 | break; | ||
943 | case 12: // "mounthost" | ||
944 | mounthost = xstrndup(opteq+1, | ||
945 | strcspn(opteq+1," \t\n\r,")); | ||
946 | break; | ||
947 | case 13: // "mountprog" | ||
948 | mountprog = val; | ||
949 | break; | ||
950 | case 14: // "mountvers" | ||
951 | mountvers = val; | ||
952 | break; | ||
953 | case 15: // "nfsprog" | ||
954 | nfsprog = val; | ||
955 | break; | ||
956 | case 16: // "nfsvers" | ||
957 | case 17: // "vers" | ||
958 | nfsvers = val; | ||
959 | break; | ||
960 | case 18: // "proto" | ||
961 | if (!strncmp(opteq+1, "tcp", 3)) | ||
962 | tcp = 1; | ||
963 | else if (!strncmp(opteq+1, "udp", 3)) | ||
964 | tcp = 0; | ||
965 | else | ||
966 | bb_error_msg("warning: unrecognized proto= option"); | ||
967 | break; | ||
968 | case 19: // "namlen" | ||
969 | if (nfs_mount_version >= 2) | ||
970 | data.namlen = val; | ||
971 | else | ||
972 | bb_error_msg("warning: option namlen is not supported\n"); | ||
973 | break; | ||
974 | case 20: // "addr" - ignore | ||
975 | break; | ||
976 | default: | ||
977 | bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val); | ||
978 | goto fail; | ||
979 | } | ||
980 | } | ||
981 | else { | ||
982 | const char *const options[] = { | ||
983 | "bg", | ||
984 | "fg", | ||
985 | "soft", | ||
986 | "hard", | ||
987 | "intr", | ||
988 | "posix", | ||
989 | "cto", | ||
990 | "ac", | ||
991 | "tcp", | ||
992 | "udp", | ||
993 | "lock", | ||
994 | NULL | ||
995 | }; | ||
996 | int val = 1; | ||
997 | if (!strncmp(opt, "no", 2)) { | ||
998 | val = 0; | ||
999 | opt += 2; | ||
1000 | } | ||
1001 | switch (index_in_str_array(options, opt)) { | ||
1002 | case 0: // "bg" | ||
1003 | bg = val; | ||
1004 | break; | ||
1005 | case 1: // "fg" | ||
1006 | bg = !val; | ||
1007 | break; | ||
1008 | case 2: // "soft" | ||
1009 | soft = val; | ||
1010 | break; | ||
1011 | case 3: // "hard" | ||
1012 | soft = !val; | ||
1013 | break; | ||
1014 | case 4: // "intr" | ||
1015 | intr = val; | ||
1016 | break; | ||
1017 | case 5: // "posix" | ||
1018 | posix = val; | ||
1019 | break; | ||
1020 | case 6: // "cto" | ||
1021 | nocto = !val; | ||
1022 | break; | ||
1023 | case 7: // "ac" | ||
1024 | noac = !val; | ||
1025 | break; | ||
1026 | case 8: // "tcp" | ||
1027 | tcp = val; | ||
1028 | break; | ||
1029 | case 9: // "udp" | ||
1030 | tcp = !val; | ||
1031 | break; | ||
1032 | case 10: // "lock" | ||
1033 | if (nfs_mount_version >= 3) | ||
1034 | nolock = !val; | ||
1035 | else | ||
1036 | bb_error_msg("warning: option nolock is not supported"); | ||
1037 | break; | ||
1038 | default: | ||
1039 | bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt); | ||
1040 | goto fail; | ||
1041 | } | ||
1042 | } | ||
1043 | } | ||
1044 | proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP; | ||
1045 | |||
1046 | data.flags = (soft ? NFS_MOUNT_SOFT : 0) | ||
1047 | | (intr ? NFS_MOUNT_INTR : 0) | ||
1048 | | (posix ? NFS_MOUNT_POSIX : 0) | ||
1049 | | (nocto ? NFS_MOUNT_NOCTO : 0) | ||
1050 | | (noac ? NFS_MOUNT_NOAC : 0); | ||
1051 | if (nfs_mount_version >= 2) | ||
1052 | data.flags |= (tcp ? NFS_MOUNT_TCP : 0); | ||
1053 | if (nfs_mount_version >= 3) | ||
1054 | data.flags |= (nolock ? NFS_MOUNT_NONLM : 0); | ||
1055 | if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) { | ||
1056 | bb_error_msg("NFSv%d not supported", nfsvers); | ||
1057 | goto fail; | ||
1058 | } | ||
1059 | if (nfsvers && !mountvers) | ||
1060 | mountvers = (nfsvers < 3) ? 1 : nfsvers; | ||
1061 | if (nfsvers && nfsvers < mountvers) { | ||
1062 | mountvers = nfsvers; | ||
1063 | } | ||
1064 | |||
1065 | /* Adjust options if none specified */ | ||
1066 | if (!data.timeo) | ||
1067 | data.timeo = tcp ? 70 : 7; | ||
1068 | |||
1069 | data.version = nfs_mount_version; | ||
1070 | |||
1071 | if (vfsflags & MS_REMOUNT) | ||
1072 | goto do_mount; | ||
1073 | |||
1074 | /* | ||
1075 | * If the previous mount operation on the same host was | ||
1076 | * backgrounded, and the "bg" for this mount is also set, | ||
1077 | * give up immediately, to avoid the initial timeout. | ||
1078 | */ | ||
1079 | if (bg && we_saw_this_host_before(hostname)) { | ||
1080 | daemonized = daemonize(); /* parent or error */ | ||
1081 | if (daemonized <= 0) { /* parent or error */ | ||
1082 | retval = -daemonized; | ||
1083 | goto ret; | ||
1084 | } | ||
1085 | } | ||
1086 | |||
1087 | /* create mount daemon client */ | ||
1088 | /* See if the nfs host = mount host. */ | ||
1089 | if (mounthost) { | ||
1090 | if (mounthost[0] >= '0' && mounthost[0] <= '9') { | ||
1091 | mount_server_addr.sin_family = AF_INET; | ||
1092 | mount_server_addr.sin_addr.s_addr = inet_addr(hostname); | ||
1093 | } else { | ||
1094 | hp = gethostbyname(mounthost); | ||
1095 | if (hp == NULL) { | ||
1096 | bb_herror_msg("%s", mounthost); | ||
1097 | goto fail; | ||
1098 | } else { | ||
1099 | if (hp->h_length > sizeof(struct in_addr)) { | ||
1100 | bb_error_msg("got bad hp->h_length?"); | ||
1101 | hp->h_length = sizeof(struct in_addr); | ||
1102 | } | ||
1103 | mount_server_addr.sin_family = AF_INET; | ||
1104 | memcpy(&mount_server_addr.sin_addr, | ||
1105 | hp->h_addr, hp->h_length); | ||
1106 | } | ||
1107 | } | ||
1108 | } | ||
1109 | |||
1110 | /* | ||
1111 | * The following loop implements the mount retries. When the mount | ||
1112 | * times out, and the "bg" option is set, we background ourself | ||
1113 | * and continue trying. | ||
1114 | * | ||
1115 | * The case where the mount point is not present and the "bg" | ||
1116 | * option is set, is treated as a timeout. This is done to | ||
1117 | * support nested mounts. | ||
1118 | * | ||
1119 | * The "retry" count specified by the user is the number of | ||
1120 | * minutes to retry before giving up. | ||
1121 | */ | ||
1122 | { | ||
1123 | struct timeval total_timeout; | ||
1124 | struct timeval retry_timeout; | ||
1125 | struct pmap* pm_mnt; | ||
1126 | time_t t; | ||
1127 | time_t prevt; | ||
1128 | time_t timeout; | ||
1129 | |||
1130 | retry_timeout.tv_sec = 3; | ||
1131 | retry_timeout.tv_usec = 0; | ||
1132 | total_timeout.tv_sec = 20; | ||
1133 | total_timeout.tv_usec = 0; | ||
1134 | timeout = time(NULL) + 60 * retry; | ||
1135 | prevt = 0; | ||
1136 | t = 30; | ||
1137 | retry: | ||
1138 | /* be careful not to use too many CPU cycles */ | ||
1139 | if (t - prevt < 30) | ||
1140 | sleep(30); | ||
1141 | |||
1142 | pm_mnt = get_mountport(&mount_server_addr, | ||
1143 | mountprog, | ||
1144 | mountvers, | ||
1145 | proto, | ||
1146 | mountport); | ||
1147 | nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers; | ||
1148 | |||
1149 | /* contact the mount daemon via TCP */ | ||
1150 | mount_server_addr.sin_port = htons(pm_mnt->pm_port); | ||
1151 | msock = RPC_ANYSOCK; | ||
1152 | |||
1153 | switch (pm_mnt->pm_prot) { | ||
1154 | case IPPROTO_UDP: | ||
1155 | mclient = clntudp_create(&mount_server_addr, | ||
1156 | pm_mnt->pm_prog, | ||
1157 | pm_mnt->pm_vers, | ||
1158 | retry_timeout, | ||
1159 | &msock); | ||
1160 | if (mclient) | ||
1161 | break; | ||
1162 | mount_server_addr.sin_port = htons(pm_mnt->pm_port); | ||
1163 | msock = RPC_ANYSOCK; | ||
1164 | case IPPROTO_TCP: | ||
1165 | mclient = clnttcp_create(&mount_server_addr, | ||
1166 | pm_mnt->pm_prog, | ||
1167 | pm_mnt->pm_vers, | ||
1168 | &msock, 0, 0); | ||
1169 | break; | ||
1170 | default: | ||
1171 | mclient = 0; | ||
1172 | } | ||
1173 | if (!mclient) { | ||
1174 | if (!daemonized && prevt == 0) | ||
1175 | error_msg_rpc(clnt_spcreateerror(" ")); | ||
1176 | } else { | ||
1177 | enum clnt_stat clnt_stat; | ||
1178 | /* try to mount hostname:pathname */ | ||
1179 | mclient->cl_auth = authunix_create_default(); | ||
1180 | |||
1181 | /* make pointers in xdr_mountres3 NULL so | ||
1182 | * that xdr_array allocates memory for us | ||
1183 | */ | ||
1184 | memset(&status, 0, sizeof(status)); | ||
1185 | |||
1186 | if (pm_mnt->pm_vers == 3) | ||
1187 | clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT, | ||
1188 | (xdrproc_t) xdr_dirpath, | ||
1189 | (caddr_t) &pathname, | ||
1190 | (xdrproc_t) xdr_mountres3, | ||
1191 | (caddr_t) &status, | ||
1192 | total_timeout); | ||
1193 | else | ||
1194 | clnt_stat = clnt_call(mclient, MOUNTPROC_MNT, | ||
1195 | (xdrproc_t) xdr_dirpath, | ||
1196 | (caddr_t) &pathname, | ||
1197 | (xdrproc_t) xdr_fhstatus, | ||
1198 | (caddr_t) &status, | ||
1199 | total_timeout); | ||
1200 | |||
1201 | if (clnt_stat == RPC_SUCCESS) | ||
1202 | goto prepare_kernel_data; /* we're done */ | ||
1203 | if (errno != ECONNREFUSED) { | ||
1204 | error_msg_rpc(clnt_sperror(mclient, " ")); | ||
1205 | goto fail; /* don't retry */ | ||
1206 | } | ||
1207 | /* Connection refused */ | ||
1208 | if (!daemonized && prevt == 0) /* print just once */ | ||
1209 | error_msg_rpc(clnt_sperror(mclient, " ")); | ||
1210 | auth_destroy(mclient->cl_auth); | ||
1211 | clnt_destroy(mclient); | ||
1212 | mclient = 0; | ||
1213 | close(msock); | ||
1214 | } | ||
1215 | |||
1216 | /* Timeout. We are going to retry... maybe */ | ||
1217 | |||
1218 | if (!bg) | ||
1219 | goto fail; | ||
1220 | if (!daemonized) { | ||
1221 | daemonized = daemonize(); | ||
1222 | if (daemonized <= 0) { /* parent or error */ | ||
1223 | retval = -daemonized; | ||
1224 | goto ret; | ||
1225 | } | ||
1226 | } | ||
1227 | prevt = t; | ||
1228 | t = time(NULL); | ||
1229 | if (t >= timeout) | ||
1230 | /* TODO error message */ | ||
1231 | goto fail; | ||
1232 | |||
1233 | goto retry; | ||
1234 | } | ||
1235 | |||
1236 | prepare_kernel_data: | ||
1237 | |||
1238 | if (nfsvers == 2) { | ||
1239 | if (status.nfsv2.fhs_status != 0) { | ||
1240 | bb_error_msg("%s:%s failed, reason given by server: %s", | ||
1241 | hostname, pathname, | ||
1242 | nfs_strerror(status.nfsv2.fhs_status)); | ||
1243 | goto fail; | ||
1244 | } | ||
1245 | memcpy(data.root.data, | ||
1246 | (char *) status.nfsv2.fhstatus_u.fhs_fhandle, | ||
1247 | NFS_FHSIZE); | ||
1248 | data.root.size = NFS_FHSIZE; | ||
1249 | memcpy(data.old_root.data, | ||
1250 | (char *) status.nfsv2.fhstatus_u.fhs_fhandle, | ||
1251 | NFS_FHSIZE); | ||
1252 | } else { | ||
1253 | fhandle3 *my_fhandle; | ||
1254 | if (status.nfsv3.fhs_status != 0) { | ||
1255 | bb_error_msg("%s:%s failed, reason given by server: %s", | ||
1256 | hostname, pathname, | ||
1257 | nfs_strerror(status.nfsv3.fhs_status)); | ||
1258 | goto fail; | ||
1259 | } | ||
1260 | my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle; | ||
1261 | memset(data.old_root.data, 0, NFS_FHSIZE); | ||
1262 | memset(&data.root, 0, sizeof(data.root)); | ||
1263 | data.root.size = my_fhandle->fhandle3_len; | ||
1264 | memcpy(data.root.data, | ||
1265 | (char *) my_fhandle->fhandle3_val, | ||
1266 | my_fhandle->fhandle3_len); | ||
1267 | |||
1268 | data.flags |= NFS_MOUNT_VER3; | ||
1269 | } | ||
1270 | |||
1271 | /* create nfs socket for kernel */ | ||
1272 | |||
1273 | if (tcp) { | ||
1274 | if (nfs_mount_version < 3) { | ||
1275 | bb_error_msg("NFS over TCP is not supported"); | ||
1276 | goto fail; | ||
1277 | } | ||
1278 | fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | ||
1279 | } else | ||
1280 | fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); | ||
1281 | if (fsock < 0) { | ||
1282 | bb_perror_msg("nfs socket"); | ||
1283 | goto fail; | ||
1284 | } | ||
1285 | if (bindresvport(fsock, 0) < 0) { | ||
1286 | bb_perror_msg("nfs bindresvport"); | ||
1287 | goto fail; | ||
1288 | } | ||
1289 | if (port == 0) { | ||
1290 | server_addr.sin_port = PMAPPORT; | ||
1291 | port = pmap_getport(&server_addr, nfsprog, nfsvers, | ||
1292 | tcp ? IPPROTO_TCP : IPPROTO_UDP); | ||
1293 | if (port == 0) | ||
1294 | port = NFS_PORT; | ||
1295 | } | ||
1296 | server_addr.sin_port = htons(port); | ||
1297 | |||
1298 | /* prepare data structure for kernel */ | ||
1299 | |||
1300 | data.fd = fsock; | ||
1301 | memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr)); | ||
1302 | strncpy(data.hostname, hostname, sizeof(data.hostname)); | ||
1303 | |||
1304 | /* clean up */ | ||
1305 | |||
1306 | auth_destroy(mclient->cl_auth); | ||
1307 | clnt_destroy(mclient); | ||
1308 | close(msock); | ||
1309 | |||
1310 | if (bg) { | ||
1311 | /* We must wait until mount directory is available */ | ||
1312 | struct stat statbuf; | ||
1313 | int delay = 1; | ||
1314 | while (stat(mp->mnt_dir, &statbuf) == -1) { | ||
1315 | if (!daemonized) { | ||
1316 | daemonized = daemonize(); | ||
1317 | if (daemonized <= 0) { /* parent or error */ | ||
1318 | retval = -daemonized; | ||
1319 | goto ret; | ||
1320 | } | ||
1321 | } | ||
1322 | sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */ | ||
1323 | delay *= 2; | ||
1324 | if (delay > 30) | ||
1325 | delay = 30; | ||
1326 | } | ||
1327 | } | ||
1328 | |||
1329 | do_mount: /* perform actual mount */ | ||
1330 | |||
1331 | mp->mnt_type = "nfs"; | ||
1332 | retval = mount_it_now(mp, vfsflags, (char*)&data); | ||
1333 | goto ret; | ||
1334 | |||
1335 | fail: /* abort */ | ||
1336 | |||
1337 | if (msock != -1) { | ||
1338 | if (mclient) { | ||
1339 | auth_destroy(mclient->cl_auth); | ||
1340 | clnt_destroy(mclient); | ||
1341 | } | ||
1342 | close(msock); | ||
1343 | } | ||
1344 | if (fsock != -1) | ||
1345 | close(fsock); | ||
1346 | |||
1347 | ret: | ||
1348 | free(hostname); | ||
1349 | free(mounthost); | ||
1350 | free(filteropts); | ||
1351 | return retval; | ||
1352 | } | ||
1353 | |||
1354 | #else /* !ENABLE_FEATURE_MOUNT_NFS */ | ||
1355 | |||
1356 | /* Never called. Call should be optimized out. */ | ||
1357 | int nfsmount(struct mntent *mp, int vfsflags, char *filteropts); | ||
1358 | |||
1359 | #endif /* !ENABLE_FEATURE_MOUNT_NFS */ | ||
1360 | |||
1361 | // Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem | ||
1362 | // type detection. Returns 0 for success, nonzero for failure. | ||
1363 | // NB: mp->xxx fields may be trashed on exit | ||
1364 | static int singlemount(struct mntent *mp, int ignore_busy) | ||
1365 | { | ||
1366 | int rc = -1, vfsflags; | ||
1367 | char *loopFile = 0, *filteropts = 0; | ||
1368 | llist_t *fl = 0; | ||
1369 | struct stat st; | ||
1370 | |||
1371 | vfsflags = parse_mount_options(mp->mnt_opts, &filteropts); | ||
1372 | |||
1373 | // Treat fstype "auto" as unspecified. | ||
1374 | |||
1375 | if (mp->mnt_type && !strcmp(mp->mnt_type,"auto")) mp->mnt_type = 0; | ||
1376 | |||
1377 | // Might this be an CIFS filesystem? | ||
1378 | |||
1379 | if (ENABLE_FEATURE_MOUNT_CIFS && | ||
1380 | (!mp->mnt_type || !strcmp(mp->mnt_type,"cifs")) && | ||
1381 | (mp->mnt_fsname[0]==mp->mnt_fsname[1] && (mp->mnt_fsname[0]=='/' || mp->mnt_fsname[0]=='\\'))) | ||
1382 | { | ||
1383 | struct hostent *he; | ||
1384 | char ip[32], *s; | ||
1385 | |||
1386 | rc = 1; | ||
1387 | // Replace '/' with '\' and verify that unc points to "//server/share". | ||
1388 | |||
1389 | for (s = mp->mnt_fsname; *s; ++s) | ||
1390 | if (*s == '/') *s = '\\'; | ||
1391 | |||
1392 | // get server IP | ||
1393 | |||
1394 | s = strrchr(mp->mnt_fsname, '\\'); | ||
1395 | if (s == mp->mnt_fsname+1) goto report_error; | ||
1396 | *s = 0; | ||
1397 | he = gethostbyname(mp->mnt_fsname+2); | ||
1398 | *s = '\\'; | ||
1399 | if (!he) goto report_error; | ||
1400 | |||
1401 | // Insert ip=... option into string flags. (NOTE: Add IPv6 support.) | ||
1402 | |||
1403 | sprintf(ip, "ip=%d.%d.%d.%d", he->h_addr[0], he->h_addr[1], | ||
1404 | he->h_addr[2], he->h_addr[3]); | ||
1405 | parse_mount_options(ip, &filteropts); | ||
1406 | |||
1407 | // compose new unc '\\server-ip\share' | ||
1408 | |||
1409 | mp->mnt_fsname = xasprintf("\\\\%s%s", ip+3, | ||
1410 | strchr(mp->mnt_fsname+2,'\\')); | ||
1411 | |||
1412 | // lock is required | ||
1413 | vfsflags |= MS_MANDLOCK; | ||
1414 | |||
1415 | mp->mnt_type = "cifs"; | ||
1416 | rc = mount_it_now(mp, vfsflags, filteropts); | ||
1417 | if (ENABLE_FEATURE_CLEAN_UP) free(mp->mnt_fsname); | ||
1418 | goto report_error; | ||
1419 | } | ||
1420 | |||
1421 | // Might this be an NFS filesystem? | ||
1422 | |||
1423 | if (ENABLE_FEATURE_MOUNT_NFS && | ||
1424 | (!mp->mnt_type || !strcmp(mp->mnt_type,"nfs")) && | ||
1425 | strchr(mp->mnt_fsname, ':') != NULL) | ||
1426 | { | ||
1427 | rc = nfsmount(mp, vfsflags, filteropts); | ||
1428 | goto report_error; | ||
1429 | } | ||
1430 | |||
1431 | // Look at the file. (Not found isn't a failure for remount, or for | ||
1432 | // a synthetic filesystem like proc or sysfs.) | ||
1433 | |||
1434 | if (!lstat(mp->mnt_fsname, &st) && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) | ||
1435 | { | ||
1436 | // Do we need to allocate a loopback device for it? | ||
1437 | |||
1438 | if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) { | ||
1439 | loopFile = bb_simplify_path(mp->mnt_fsname); | ||
1440 | mp->mnt_fsname = 0; | ||
1441 | switch (set_loop(&(mp->mnt_fsname), loopFile, 0)) { | ||
1442 | case 0: | ||
1443 | case 1: | ||
1444 | break; | ||
1445 | default: | ||
1446 | bb_error_msg( errno == EPERM || errno == EACCES | ||
1447 | ? bb_msg_perm_denied_are_you_root | ||
1448 | : "cannot setup loop device"); | ||
1449 | return errno; | ||
1450 | } | ||
1451 | |||
1452 | // Autodetect bind mounts | ||
1453 | |||
1454 | } else if (S_ISDIR(st.st_mode) && !mp->mnt_type) | ||
1455 | vfsflags |= MS_BIND; | ||
1456 | } | ||
1457 | |||
1458 | /* If we know the fstype (or don't need to), jump straight | ||
1459 | * to the actual mount. */ | ||
1460 | |||
1461 | if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) | ||
1462 | rc = mount_it_now(mp, vfsflags, filteropts); | ||
1463 | |||
1464 | // Loop through filesystem types until mount succeeds or we run out | ||
1465 | |||
1466 | else { | ||
1467 | |||
1468 | /* Initialize list of block backed filesystems. This has to be | ||
1469 | * done here so that during "mount -a", mounts after /proc shows up | ||
1470 | * can autodetect. */ | ||
1471 | |||
1472 | if (!fslist) { | ||
1473 | fslist = get_block_backed_filesystems(); | ||
1474 | if (ENABLE_FEATURE_CLEAN_UP && fslist) | ||
1475 | atexit(delete_block_backed_filesystems); | ||
1476 | } | ||
1477 | |||
1478 | for (fl = fslist; fl; fl = fl->link) { | ||
1479 | mp->mnt_type = fl->data; | ||
1480 | rc = mount_it_now(mp, vfsflags, filteropts); | ||
1481 | if (!rc) break; | ||
1482 | } | ||
1483 | } | ||
1484 | |||
1485 | // If mount failed, clean up loop file (if any). | ||
1486 | |||
1487 | if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) { | ||
1488 | del_loop(mp->mnt_fsname); | ||
1489 | if (ENABLE_FEATURE_CLEAN_UP) { | ||
1490 | free(loopFile); | ||
1491 | free(mp->mnt_fsname); | ||
1492 | } | ||
1493 | } | ||
1494 | |||
1495 | report_error: | ||
1496 | if (ENABLE_FEATURE_CLEAN_UP) free(filteropts); | ||
1497 | |||
1498 | if (rc && errno == EBUSY && ignore_busy) rc = 0; | ||
1499 | if (rc < 0) | ||
1500 | /* perror here sometimes says "mounting ... on ... failed: Success" */ | ||
1501 | bb_error_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir); | ||
1502 | |||
1503 | return rc; | ||
1504 | } | ||
1505 | |||
1506 | // Parse options, if necessary parse fstab/mtab, and call singlemount for | ||
1507 | // each directory to be mounted. | ||
1508 | |||
1509 | const char must_be_root[] = "you must be root"; | ||
1510 | |||
1511 | int mount_main(int argc, char **argv) | ||
1512 | { | ||
1513 | enum { OPT_ALL = 0x10 }; | ||
1514 | |||
1515 | char *cmdopts = xstrdup(""), *fstype=0, *storage_path=0; | ||
1516 | char *opt_o; | ||
1517 | const char *fstabname; | ||
1518 | FILE *fstab; | ||
1519 | int i, j, rc = 0; | ||
1520 | unsigned opt; | ||
1521 | struct mntent mtpair[2], *mtcur = mtpair; | ||
1522 | SKIP_DESKTOP(const int nonroot = 0;) | ||
1523 | USE_DESKTOP( int nonroot = (getuid() != 0);) | ||
1524 | |||
1525 | /* parse long options, like --bind and --move. Note that -o option | ||
1526 | * and --option are synonymous. Yes, this means --remount,rw works. */ | ||
1527 | |||
1528 | for (i = j = 0; i < argc; i++) { | ||
1529 | if (argv[i][0] == '-' && argv[i][1] == '-') { | ||
1530 | append_mount_options(&cmdopts, argv[i]+2); | ||
1531 | } else argv[j++] = argv[i]; | ||
1532 | } | ||
1533 | argv[j] = 0; | ||
1534 | argc = j; | ||
1535 | |||
1536 | // Parse remaining options | ||
1537 | |||
1538 | opt = getopt32(argc, argv, "o:t:rwanfvs", &opt_o, &fstype); | ||
1539 | if (opt & 0x1) append_mount_options(&cmdopts, opt_o); // -o | ||
1540 | //if (opt & 0x2) // -t | ||
1541 | if (opt & 0x4) append_mount_options(&cmdopts, "ro"); // -r | ||
1542 | if (opt & 0x8) append_mount_options(&cmdopts, "rw"); // -w | ||
1543 | //if (opt & 0x10) // -a | ||
1544 | if (opt & 0x20) USE_FEATURE_MTAB_SUPPORT(useMtab = 0); // -n | ||
1545 | if (opt & 0x40) USE_FEATURE_MTAB_SUPPORT(fakeIt = 1); // -f | ||
1546 | //if (opt & 0x80) // -v: verbose (ignore) | ||
1547 | //if (opt & 0x100) // -s: sloppy (ignore) | ||
1548 | argv += optind; | ||
1549 | argc -= optind; | ||
1550 | |||
1551 | // Three or more non-option arguments? Die with a usage message. | ||
1552 | |||
1553 | if (argc > 2) bb_show_usage(); | ||
1554 | |||
1555 | // If we have no arguments, show currently mounted filesystems | ||
1556 | |||
1557 | if (!argc) { | ||
1558 | if (!(opt & OPT_ALL)) { | ||
1559 | FILE *mountTable = setmntent(bb_path_mtab_file, "r"); | ||
1560 | |||
1561 | if (!mountTable) bb_error_msg_and_die("no %s", bb_path_mtab_file); | ||
1562 | |||
1563 | while (getmntent_r(mountTable, mtpair, bb_common_bufsiz1, | ||
1564 | sizeof(bb_common_bufsiz1))) | ||
1565 | { | ||
1566 | // Don't show rootfs. FIXME: why?? | ||
1567 | if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue; | ||
1568 | |||
1569 | if (!fstype || !strcmp(mtpair->mnt_type, fstype)) | ||
1570 | printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname, | ||
1571 | mtpair->mnt_dir, mtpair->mnt_type, | ||
1572 | mtpair->mnt_opts); | ||
1573 | } | ||
1574 | if (ENABLE_FEATURE_CLEAN_UP) endmntent(mountTable); | ||
1575 | return EXIT_SUCCESS; | ||
1576 | } | ||
1577 | } else storage_path = bb_simplify_path(argv[0]); | ||
1578 | |||
1579 | // When we have two arguments, the second is the directory and we can | ||
1580 | // skip looking at fstab entirely. We can always abspath() the directory | ||
1581 | // argument when we get it. | ||
1582 | |||
1583 | if (argc == 2) { | ||
1584 | if (nonroot) | ||
1585 | bb_error_msg_and_die(must_be_root); | ||
1586 | mtpair->mnt_fsname = argv[0]; | ||
1587 | mtpair->mnt_dir = argv[1]; | ||
1588 | mtpair->mnt_type = fstype; | ||
1589 | mtpair->mnt_opts = cmdopts; | ||
1590 | rc = singlemount(mtpair, 0); | ||
1591 | goto clean_up; | ||
1592 | } | ||
1593 | |||
1594 | i = parse_mount_options(cmdopts, 0); | ||
1595 | if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags | ||
1596 | bb_error_msg_and_die(must_be_root); | ||
1597 | |||
1598 | // If we have a shared subtree flag, don't worry about fstab or mtab. | ||
1599 | |||
1600 | if (ENABLE_FEATURE_MOUNT_FLAGS && | ||
1601 | (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))) | ||
1602 | { | ||
1603 | rc = mount("", argv[0], "", i, ""); | ||
1604 | if (rc) bb_perror_msg_and_die("%s", argv[0]); | ||
1605 | goto clean_up; | ||
1606 | } | ||
1607 | |||
1608 | // Open either fstab or mtab | ||
1609 | |||
1610 | fstabname = "/etc/fstab"; | ||
1611 | if (i & MS_REMOUNT) { | ||
1612 | fstabname = bb_path_mtab_file; | ||
1613 | } | ||
1614 | fstab = setmntent(fstabname, "r"); | ||
1615 | if (!fstab) | ||
1616 | bb_perror_msg_and_die("cannot read %s", fstabname); | ||
1617 | |||
1618 | // Loop through entries until we find what we're looking for. | ||
1619 | |||
1620 | memset(mtpair, 0, sizeof(mtpair)); | ||
1621 | for (;;) { | ||
1622 | struct mntent *mtnext = (mtcur==mtpair ? mtpair+1 : mtpair); | ||
1623 | |||
1624 | // Get next fstab entry | ||
1625 | |||
1626 | if (!getmntent_r(fstab, mtcur, bb_common_bufsiz1 | ||
1627 | + (mtcur==mtpair ? sizeof(bb_common_bufsiz1)/2 : 0), | ||
1628 | sizeof(bb_common_bufsiz1)/2)) | ||
1629 | { | ||
1630 | // Were we looking for something specific? | ||
1631 | |||
1632 | if (argc) { | ||
1633 | |||
1634 | // If we didn't find anything, complain. | ||
1635 | |||
1636 | if (!mtnext->mnt_fsname) | ||
1637 | bb_error_msg_and_die("can't find %s in %s", | ||
1638 | argv[0], fstabname); | ||
1639 | |||
1640 | mtcur = mtnext; | ||
1641 | if (nonroot) { | ||
1642 | // fstab must have "users" or "user" | ||
1643 | if (!(parse_mount_options(mtcur->mnt_opts, 0) & MOUNT_USERS)) | ||
1644 | bb_error_msg_and_die(must_be_root); | ||
1645 | } | ||
1646 | |||
1647 | // Mount the last thing we found. | ||
1648 | |||
1649 | mtcur->mnt_opts = xstrdup(mtcur->mnt_opts); | ||
1650 | append_mount_options(&(mtcur->mnt_opts), cmdopts); | ||
1651 | rc = singlemount(mtcur, 0); | ||
1652 | free(mtcur->mnt_opts); | ||
1653 | } | ||
1654 | goto clean_up; | ||
1655 | } | ||
1656 | |||
1657 | /* If we're trying to mount something specific and this isn't it, | ||
1658 | * skip it. Note we must match both the exact text in fstab (ala | ||
1659 | * "proc") or a full path from root */ | ||
1660 | |||
1661 | if (argc) { | ||
1662 | |||
1663 | // Is this what we're looking for? | ||
1664 | |||
1665 | if (strcmp(argv[0], mtcur->mnt_fsname) && | ||
1666 | strcmp(storage_path, mtcur->mnt_fsname) && | ||
1667 | strcmp(argv[0], mtcur->mnt_dir) && | ||
1668 | strcmp(storage_path, mtcur->mnt_dir)) continue; | ||
1669 | |||
1670 | // Remember this entry. Something later may have overmounted | ||
1671 | // it, and we want the _last_ match. | ||
1672 | |||
1673 | mtcur = mtnext; | ||
1674 | |||
1675 | // If we're mounting all. | ||
1676 | |||
1677 | } else { | ||
1678 | // Do we need to match a filesystem type? | ||
1679 | // TODO: support "-t type1,type2"; "-t notype1,type2" | ||
1680 | |||
1681 | if (fstype && strcmp(mtcur->mnt_type, fstype)) continue; | ||
1682 | |||
1683 | // Skip noauto and swap anyway. | ||
1684 | |||
1685 | if (parse_mount_options(mtcur->mnt_opts, 0) | ||
1686 | & (MOUNT_NOAUTO | MOUNT_SWAP)) continue; | ||
1687 | |||
1688 | // No, mount -a won't mount anything, | ||
1689 | // even user mounts, for mere humans. | ||
1690 | |||
1691 | if (nonroot) | ||
1692 | bb_error_msg_and_die(must_be_root); | ||
1693 | |||
1694 | // Mount this thing. | ||
1695 | |||
1696 | if (singlemount(mtcur, 1)) { | ||
1697 | /* Count number of failed mounts */ | ||
1698 | rc++; | ||
1699 | } | ||
1700 | } | ||
1701 | } | ||
1702 | if (ENABLE_FEATURE_CLEAN_UP) endmntent(fstab); | ||
1703 | |||
1704 | clean_up: | ||
1705 | |||
1706 | if (ENABLE_FEATURE_CLEAN_UP) { | ||
1707 | free(storage_path); | ||
1708 | free(cmdopts); | ||
1709 | } | ||
1710 | |||
1711 | return rc; | ||
1712 | } | ||
diff --git a/util-linux/pivot_root.c b/util-linux/pivot_root.c new file mode 100644 index 000000000..bd02302c7 --- /dev/null +++ b/util-linux/pivot_root.c | |||
@@ -0,0 +1,24 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * pivot_root.c - Change root file system. Based on util-linux 2.10s | ||
4 | * | ||
5 | * busyboxed by Evin Robertson | ||
6 | * pivot_root syscall stubbed by Erik Andersen, so it will compile | ||
7 | * regardless of the kernel being used. | ||
8 | * | ||
9 | * Licensed under GPL version 2, see file LICENSE in this tarball for details. | ||
10 | */ | ||
11 | #include "busybox.h" | ||
12 | |||
13 | extern int pivot_root(const char * new_root,const char * put_old); | ||
14 | |||
15 | int pivot_root_main(int argc, char **argv) | ||
16 | { | ||
17 | if (argc != 3) | ||
18 | bb_show_usage(); | ||
19 | |||
20 | if (pivot_root(argv[1],argv[2]) < 0) | ||
21 | bb_perror_msg_and_die("pivot_root"); | ||
22 | |||
23 | return EXIT_SUCCESS; | ||
24 | } | ||
diff --git a/util-linux/rdate.c b/util-linux/rdate.c new file mode 100644 index 000000000..12105953d --- /dev/null +++ b/util-linux/rdate.c | |||
@@ -0,0 +1,86 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * The Rdate command will ask a time server for the RFC 868 time | ||
4 | * and optionally set the system time. | ||
5 | * | ||
6 | * by Sterling Huxley <sterling@europa.com> | ||
7 | * | ||
8 | * Licensed under GPL v2 or later, see file License for details. | ||
9 | */ | ||
10 | |||
11 | #include <sys/types.h> | ||
12 | #include <sys/socket.h> | ||
13 | #include <netinet/in.h> | ||
14 | #include <netdb.h> | ||
15 | #include <stdio.h> | ||
16 | #include <string.h> | ||
17 | #include <time.h> | ||
18 | #include <stdlib.h> | ||
19 | #include <unistd.h> | ||
20 | #include <signal.h> | ||
21 | |||
22 | #include "busybox.h" | ||
23 | |||
24 | |||
25 | static const int RFC_868_BIAS = 2208988800UL; | ||
26 | |||
27 | static void socket_timeout(int sig) | ||
28 | { | ||
29 | bb_error_msg_and_die("timeout connecting to time server"); | ||
30 | } | ||
31 | |||
32 | static time_t askremotedate(const char *host) | ||
33 | { | ||
34 | unsigned long nett; | ||
35 | struct sockaddr_in s_in; | ||
36 | int fd; | ||
37 | |||
38 | bb_lookup_host(&s_in, host); | ||
39 | s_in.sin_port = bb_lookup_port("time", "tcp", 37); | ||
40 | |||
41 | /* Add a timeout for dead or inaccessible servers */ | ||
42 | alarm(10); | ||
43 | signal(SIGALRM, socket_timeout); | ||
44 | |||
45 | fd = xconnect_tcp_v4(&s_in); | ||
46 | |||
47 | if (safe_read(fd, (void *)&nett, 4) != 4) /* read time from server */ | ||
48 | bb_error_msg_and_die("%s did not send the complete time", host); | ||
49 | close(fd); | ||
50 | |||
51 | /* convert from network byte order to local byte order. | ||
52 | * RFC 868 time is the number of seconds | ||
53 | * since 00:00 (midnight) 1 January 1900 GMT | ||
54 | * the RFC 868 time 2,208,988,800 corresponds to 00:00 1 Jan 1970 GMT | ||
55 | * Subtract the RFC 868 time to get Linux epoch | ||
56 | */ | ||
57 | |||
58 | return ntohl(nett) - RFC_868_BIAS; | ||
59 | } | ||
60 | |||
61 | int rdate_main(int argc, char **argv) | ||
62 | { | ||
63 | time_t remote_time; | ||
64 | unsigned long flags; | ||
65 | |||
66 | opt_complementary = "-1"; | ||
67 | flags = getopt32(argc, argv, "sp"); | ||
68 | |||
69 | remote_time = askremotedate(argv[optind]); | ||
70 | |||
71 | if ((flags & 2) == 0) { | ||
72 | time_t current_time; | ||
73 | |||
74 | time(¤t_time); | ||
75 | if (current_time == remote_time) | ||
76 | bb_error_msg("current time matches remote time"); | ||
77 | else | ||
78 | if (stime(&remote_time) < 0) | ||
79 | bb_perror_msg_and_die("cannot set time of day"); | ||
80 | } | ||
81 | |||
82 | if ((flags & 1) == 0) | ||
83 | printf("%s", ctime(&remote_time)); | ||
84 | |||
85 | return EXIT_SUCCESS; | ||
86 | } | ||
diff --git a/util-linux/readprofile.c b/util-linux/readprofile.c new file mode 100644 index 000000000..dd810f021 --- /dev/null +++ b/util-linux/readprofile.c | |||
@@ -0,0 +1,236 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * readprofile.c - used to read /proc/profile | ||
4 | * | ||
5 | * Copyright (C) 1994,1996 Alessandro Rubini (rubini@ipvvis.unipv.it) | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | /* | ||
11 | * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL> | ||
12 | * - added Native Language Support | ||
13 | * 1999-09-01 Stephane Eranian <eranian@cello.hpl.hp.com> | ||
14 | * - 64bit clean patch | ||
15 | * 3Feb2001 Andrew Morton <andrewm@uow.edu.au> | ||
16 | * - -M option to write profile multiplier. | ||
17 | * 2001-11-07 Werner Almesberger <wa@almesberger.net> | ||
18 | * - byte order auto-detection and -n option | ||
19 | * 2001-11-09 Werner Almesberger <wa@almesberger.net> | ||
20 | * - skip step size (index 0) | ||
21 | * 2002-03-09 John Levon <moz@compsoc.man.ac.uk> | ||
22 | * - make maplineno do something | ||
23 | * 2002-11-28 Mads Martin Joergensen + | ||
24 | * - also try /boot/System.map-`uname -r` | ||
25 | * 2003-04-09 Werner Almesberger <wa@almesberger.net> | ||
26 | * - fixed off-by eight error and improved heuristics in byte order detection | ||
27 | * 2003-08-12 Nikita Danilov <Nikita@Namesys.COM> | ||
28 | * - added -s option; example of use: | ||
29 | * "readprofile -s -m /boot/System.map-test | grep __d_lookup | sort -n -k3" | ||
30 | * | ||
31 | * Taken from util-linux and adapted for busybox by | ||
32 | * Paul Mundt <lethal@linux-sh.org>. | ||
33 | */ | ||
34 | |||
35 | #include "busybox.h" | ||
36 | #include <sys/utsname.h> | ||
37 | |||
38 | #define S_LEN 128 | ||
39 | |||
40 | /* These are the defaults */ | ||
41 | static const char defaultmap[] = "/boot/System.map"; | ||
42 | static const char defaultpro[] = "/proc/profile"; | ||
43 | |||
44 | int readprofile_main(int argc, char **argv) | ||
45 | { | ||
46 | FILE *map; | ||
47 | const char *mapFile, *proFile, *mult = 0; | ||
48 | unsigned long indx = 1; | ||
49 | size_t len; | ||
50 | uint64_t add0 = 0; | ||
51 | unsigned int step; | ||
52 | unsigned int *buf, total, fn_len; | ||
53 | unsigned long long fn_add, next_add; /* current and next address */ | ||
54 | char fn_name[S_LEN], next_name[S_LEN]; /* current and next name */ | ||
55 | char mapline[S_LEN]; | ||
56 | char mode[8]; | ||
57 | int optAll = 0, optInfo = 0, optReset = 0; | ||
58 | int optVerbose = 0, optNative = 0; | ||
59 | int optBins = 0, optSub = 0; | ||
60 | int maplineno = 1; | ||
61 | int header_printed; | ||
62 | |||
63 | #define next (current^1) | ||
64 | |||
65 | proFile = defaultpro; | ||
66 | mapFile = defaultmap; | ||
67 | |||
68 | opt_complementary = "nn:aa:bb:ss:ii:rr:vv"; | ||
69 | getopt32(argc, argv, "M:m:p:nabsirv", | ||
70 | &mult, &mapFile, &proFile, | ||
71 | &optNative, &optAll, &optBins, &optSub, | ||
72 | &optInfo, &optReset, &optVerbose); | ||
73 | |||
74 | if (optReset || mult) { | ||
75 | int multiplier, fd, to_write; | ||
76 | |||
77 | /* | ||
78 | * When writing the multiplier, if the length of the write is | ||
79 | * not sizeof(int), the multiplier is not changed | ||
80 | */ | ||
81 | if (mult) { | ||
82 | multiplier = xatoi_u(mult); | ||
83 | to_write = sizeof(int); | ||
84 | } else { | ||
85 | multiplier = 0; | ||
86 | to_write = 1; /* sth different from sizeof(int) */ | ||
87 | } | ||
88 | |||
89 | fd = xopen(defaultpro, O_WRONLY); | ||
90 | |||
91 | if (full_write(fd, &multiplier, to_write) != to_write) | ||
92 | bb_perror_msg_and_die("error writing %s", defaultpro); | ||
93 | |||
94 | close(fd); | ||
95 | return EXIT_SUCCESS; | ||
96 | } | ||
97 | |||
98 | /* | ||
99 | * Use an fd for the profiling buffer, to skip stdio overhead | ||
100 | */ | ||
101 | len = INT_MAX; | ||
102 | buf = xmalloc_open_read_close(proFile, &len); | ||
103 | if (!optNative) { | ||
104 | int entries = len/sizeof(*buf); | ||
105 | int big = 0, small = 0, i; | ||
106 | unsigned *p; | ||
107 | |||
108 | for (p = buf+1; p < buf+entries; p++) { | ||
109 | if (*p & ~0U << (sizeof(*buf)*4)) | ||
110 | big++; | ||
111 | if (*p & ((1 << (sizeof(*buf)*4))-1)) | ||
112 | small++; | ||
113 | } | ||
114 | if (big > small) { | ||
115 | bb_error_msg("assuming reversed byte order, " | ||
116 | "use -n to force native byte order"); | ||
117 | for (p = buf; p < buf+entries; p++) | ||
118 | for (i = 0; i < sizeof(*buf)/2; i++) { | ||
119 | unsigned char *b = (unsigned char *) p; | ||
120 | unsigned char tmp; | ||
121 | |||
122 | tmp = b[i]; | ||
123 | b[i] = b[sizeof(*buf)-i-1]; | ||
124 | b[sizeof(*buf)-i-1] = tmp; | ||
125 | } | ||
126 | } | ||
127 | } | ||
128 | |||
129 | step = buf[0]; | ||
130 | if (optInfo) { | ||
131 | printf("Sampling_step: %i\n", step); | ||
132 | return EXIT_SUCCESS; | ||
133 | } | ||
134 | |||
135 | total = 0; | ||
136 | |||
137 | map = xfopen(mapFile, "r"); | ||
138 | |||
139 | while (fgets(mapline, S_LEN, map)) { | ||
140 | if (sscanf(mapline, "%llx %s %s", &fn_add, mode, fn_name) != 3) | ||
141 | bb_error_msg_and_die("%s(%i): wrong map line", | ||
142 | mapFile, maplineno); | ||
143 | |||
144 | if (!strcmp(fn_name, "_stext")) /* only elf works like this */ { | ||
145 | add0 = fn_add; | ||
146 | break; | ||
147 | } | ||
148 | maplineno++; | ||
149 | } | ||
150 | |||
151 | if (!add0) | ||
152 | bb_error_msg_and_die("can't find \"_stext\" in %s", mapFile); | ||
153 | |||
154 | /* | ||
155 | * Main loop. | ||
156 | */ | ||
157 | while (fgets(mapline, S_LEN, map)) { | ||
158 | unsigned int this = 0; | ||
159 | |||
160 | if (sscanf(mapline, "%llx %s %s", &next_add, mode, next_name) != 3) | ||
161 | bb_error_msg_and_die("%s(%i): wrong map line", | ||
162 | mapFile, maplineno); | ||
163 | |||
164 | header_printed = 0; | ||
165 | |||
166 | /* ignore any LEADING (before a '[tT]' symbol is found) | ||
167 | Absolute symbols */ | ||
168 | if ((*mode == 'A' || *mode == '?') && total == 0) continue; | ||
169 | if (*mode != 'T' && *mode != 't' && | ||
170 | *mode != 'W' && *mode != 'w') | ||
171 | break; /* only text is profiled */ | ||
172 | |||
173 | if (indx >= len / sizeof(*buf)) | ||
174 | bb_error_msg_and_die("profile address out of range. " | ||
175 | "Wrong map file?"); | ||
176 | |||
177 | while (indx < (next_add-add0)/step) { | ||
178 | if (optBins && (buf[indx] || optAll)) { | ||
179 | if (!header_printed) { | ||
180 | printf("%s:\n", fn_name); | ||
181 | header_printed = 1; | ||
182 | } | ||
183 | printf("\t%"PRIx64"\t%u\n", (indx - 1)*step + add0, buf[indx]); | ||
184 | } | ||
185 | this += buf[indx++]; | ||
186 | } | ||
187 | total += this; | ||
188 | |||
189 | if (optBins) { | ||
190 | if (optVerbose || this > 0) | ||
191 | printf(" total\t\t\t\t%u\n", this); | ||
192 | } else if ((this || optAll) && | ||
193 | (fn_len = next_add-fn_add) != 0) { | ||
194 | if (optVerbose) | ||
195 | printf("%016llx %-40s %6i %8.4f\n", fn_add, | ||
196 | fn_name, this, this/(double)fn_len); | ||
197 | else | ||
198 | printf("%6i %-40s %8.4f\n", | ||
199 | this, fn_name, this/(double)fn_len); | ||
200 | if (optSub) { | ||
201 | unsigned long long scan; | ||
202 | |||
203 | for (scan = (fn_add-add0)/step + 1; | ||
204 | scan < (next_add-add0)/step; scan++) { | ||
205 | unsigned long long addr; | ||
206 | |||
207 | addr = (scan - 1)*step + add0; | ||
208 | printf("\t%#llx\t%s+%#llx\t%u\n", | ||
209 | addr, fn_name, addr - fn_add, | ||
210 | buf[scan]); | ||
211 | } | ||
212 | } | ||
213 | } | ||
214 | |||
215 | fn_add = next_add; | ||
216 | strcpy(fn_name, next_name); | ||
217 | |||
218 | maplineno++; | ||
219 | } | ||
220 | |||
221 | /* clock ticks, out of kernel text - probably modules */ | ||
222 | printf("%6i %s\n", buf[len/sizeof(*buf)-1], "*unknown*"); | ||
223 | |||
224 | /* trailer */ | ||
225 | if (optVerbose) | ||
226 | printf("%016x %-40s %6i %8.4f\n", | ||
227 | 0, "total", total, total/(double)(fn_add-add0)); | ||
228 | else | ||
229 | printf("%6i %-40s %8.4f\n", | ||
230 | total, "total", total/(double)(fn_add-add0)); | ||
231 | |||
232 | fclose(map); | ||
233 | free(buf); | ||
234 | |||
235 | return EXIT_SUCCESS; | ||
236 | } | ||
diff --git a/util-linux/setarch.c b/util-linux/setarch.c new file mode 100644 index 000000000..d7e1c0917 --- /dev/null +++ b/util-linux/setarch.c | |||
@@ -0,0 +1,52 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Linux32/linux64 allows for changing uname emulation. | ||
4 | * | ||
5 | * Copyright 2002 Andi Kleen, SuSE Labs. | ||
6 | * | ||
7 | * Licensed under GPL v2 or later, see file License for details. | ||
8 | */ | ||
9 | |||
10 | #include <stdlib.h> | ||
11 | #include <unistd.h> | ||
12 | #include <string.h> | ||
13 | #include <errno.h> | ||
14 | #include <stdio.h> | ||
15 | #include <sys/personality.h> | ||
16 | |||
17 | #include "busybox.h" | ||
18 | |||
19 | int setarch_main(int ATTRIBUTE_UNUSED argc, char **argv) | ||
20 | { | ||
21 | int pers = -1; | ||
22 | |||
23 | /* Figure out what personality we are supposed to switch to ... | ||
24 | * we can be invoked as either: | ||
25 | * argv[0],argv[1] -> "setarch","personality" | ||
26 | * argv[0] -> "personality" | ||
27 | */ | ||
28 | retry: | ||
29 | if (argv[0][5] == '6') /* linux64 */ | ||
30 | pers = PER_LINUX; | ||
31 | else if (argv[0][5] == '3') /* linux32 */ | ||
32 | pers = PER_LINUX32; | ||
33 | else if (pers == -1 && argv[1] != NULL) { | ||
34 | pers = PER_LINUX32; | ||
35 | ++argv; | ||
36 | goto retry; | ||
37 | } | ||
38 | |||
39 | /* make user actually gave us something to do */ | ||
40 | ++argv; | ||
41 | if (argv[0] == NULL) | ||
42 | bb_show_usage(); | ||
43 | |||
44 | /* Try to set personality */ | ||
45 | if (personality(pers) >= 0) { | ||
46 | |||
47 | /* Try to execute the program */ | ||
48 | execvp(argv[0], argv); | ||
49 | } | ||
50 | |||
51 | bb_perror_msg_and_die("%s", argv[0]); | ||
52 | } | ||
diff --git a/util-linux/swaponoff.c b/util-linux/swaponoff.c new file mode 100644 index 000000000..8434d121c --- /dev/null +++ b/util-linux/swaponoff.c | |||
@@ -0,0 +1,77 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Mini swapon/swapoff implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under the GPL version 2, see the file LICENSE in this tarball. | ||
8 | */ | ||
9 | |||
10 | #include "busybox.h" | ||
11 | #include <mntent.h> | ||
12 | #include <sys/swap.h> | ||
13 | |||
14 | |||
15 | static int swap_enable_disable(char *device) | ||
16 | { | ||
17 | int status; | ||
18 | struct stat st; | ||
19 | |||
20 | xstat(device, &st); | ||
21 | |||
22 | /* test for holes */ | ||
23 | if (S_ISREG(st.st_mode)) | ||
24 | if (st.st_blocks * 512 < st.st_size) | ||
25 | bb_error_msg_and_die("swap file has holes"); | ||
26 | |||
27 | if (applet_name[5] == 'n') | ||
28 | status = swapon(device, 0); | ||
29 | else | ||
30 | status = swapoff(device); | ||
31 | |||
32 | if (status != 0) { | ||
33 | bb_perror_msg("%s", device); | ||
34 | return 1; | ||
35 | } | ||
36 | |||
37 | return 0; | ||
38 | } | ||
39 | |||
40 | static int do_em_all(void) | ||
41 | { | ||
42 | struct mntent *m; | ||
43 | FILE *f; | ||
44 | int err; | ||
45 | |||
46 | f = setmntent("/etc/fstab", "r"); | ||
47 | if (f == NULL) | ||
48 | bb_perror_msg_and_die("/etc/fstab"); | ||
49 | |||
50 | err = 0; | ||
51 | while ((m = getmntent(f)) != NULL) | ||
52 | if (strcmp(m->mnt_type, MNTTYPE_SWAP) == 0) | ||
53 | err += swap_enable_disable(m->mnt_fsname); | ||
54 | |||
55 | endmntent(f); | ||
56 | |||
57 | return err; | ||
58 | } | ||
59 | |||
60 | #define DO_ALL 0x01 | ||
61 | |||
62 | int swap_on_off_main(int argc, char **argv) | ||
63 | { | ||
64 | int ret; | ||
65 | |||
66 | if (argc == 1) | ||
67 | bb_show_usage(); | ||
68 | |||
69 | ret = getopt32(argc, argv, "a"); | ||
70 | if (ret & DO_ALL) | ||
71 | return do_em_all(); | ||
72 | |||
73 | ret = 0; | ||
74 | while (*++argv) | ||
75 | ret += swap_enable_disable(*argv); | ||
76 | return ret; | ||
77 | } | ||
diff --git a/util-linux/switch_root.c b/util-linux/switch_root.c new file mode 100644 index 000000000..4c23f69da --- /dev/null +++ b/util-linux/switch_root.c | |||
@@ -0,0 +1,122 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* Copyright 2005 Rob Landley <rob@landley.net> | ||
3 | * | ||
4 | * Switch from rootfs to another filesystem as the root of the mount tree. | ||
5 | * | ||
6 | * Licensed under GPL version 2, see file LICENSE in this tarball for details. | ||
7 | */ | ||
8 | |||
9 | #include "busybox.h" | ||
10 | #include <sys/vfs.h> | ||
11 | |||
12 | |||
13 | // Make up for header deficiencies. | ||
14 | |||
15 | #ifndef RAMFS_MAGIC | ||
16 | #define RAMFS_MAGIC 0x858458f6 | ||
17 | #endif | ||
18 | |||
19 | #ifndef TMPFS_MAGIC | ||
20 | #define TMPFS_MAGIC 0x01021994 | ||
21 | #endif | ||
22 | |||
23 | #ifndef MS_MOVE | ||
24 | #define MS_MOVE 8192 | ||
25 | #endif | ||
26 | |||
27 | dev_t rootdev; | ||
28 | |||
29 | // Recursively delete contents of rootfs. | ||
30 | |||
31 | static void delete_contents(char *directory) | ||
32 | { | ||
33 | DIR *dir; | ||
34 | struct dirent *d; | ||
35 | struct stat st; | ||
36 | |||
37 | // Don't descend into other filesystems | ||
38 | if (lstat(directory, &st) || st.st_dev != rootdev) return; | ||
39 | |||
40 | // Recursively delete the contents of directories. | ||
41 | if (S_ISDIR(st.st_mode)) { | ||
42 | if((dir = opendir(directory))) { | ||
43 | while ((d = readdir(dir))) { | ||
44 | char *newdir=d->d_name; | ||
45 | |||
46 | // Skip . and .. | ||
47 | if(*newdir=='.' && (!newdir[1] || (newdir[1]=='.' && !newdir[2]))) | ||
48 | continue; | ||
49 | |||
50 | // Recurse to delete contents | ||
51 | newdir = alloca(strlen(directory) + strlen(d->d_name) + 2); | ||
52 | sprintf(newdir, "%s/%s", directory, d->d_name); | ||
53 | delete_contents(newdir); | ||
54 | } | ||
55 | closedir(dir); | ||
56 | |||
57 | // Directory should now be empty. Zap it. | ||
58 | rmdir(directory); | ||
59 | } | ||
60 | |||
61 | // It wasn't a directory. Zap it. | ||
62 | |||
63 | } else unlink(directory); | ||
64 | } | ||
65 | |||
66 | int switch_root_main(int argc, char *argv[]) | ||
67 | { | ||
68 | char *newroot, *console=NULL; | ||
69 | struct stat st1, st2; | ||
70 | struct statfs stfs; | ||
71 | |||
72 | // Parse args (-c console) | ||
73 | |||
74 | opt_complementary = "-2"; | ||
75 | getopt32(argc, argv, "c:", &console); | ||
76 | |||
77 | // Change to new root directory and verify it's a different fs. | ||
78 | |||
79 | newroot=argv[optind++]; | ||
80 | |||
81 | if (chdir(newroot) || lstat(".", &st1) || lstat("/", &st2) || | ||
82 | st1.st_dev == st2.st_dev) | ||
83 | { | ||
84 | bb_error_msg_and_die("bad newroot %s", newroot); | ||
85 | } | ||
86 | rootdev=st2.st_dev; | ||
87 | |||
88 | // Additional sanity checks: we're about to rm -rf /, so be REALLY SURE | ||
89 | // we mean it. (I could make this a CONFIG option, but I would get email | ||
90 | // from all the people who WILL eat their filesystemss.) | ||
91 | |||
92 | if (lstat("/init", &st1) || !S_ISREG(st1.st_mode) || statfs("/", &stfs) || | ||
93 | (stfs.f_type != RAMFS_MAGIC && stfs.f_type != TMPFS_MAGIC) || | ||
94 | getpid() != 1) | ||
95 | { | ||
96 | bb_error_msg_and_die("not rootfs"); | ||
97 | } | ||
98 | |||
99 | // Zap everything out of rootdev | ||
100 | |||
101 | delete_contents("/"); | ||
102 | |||
103 | // Overmount / with newdir and chroot into it. The chdir is needed to | ||
104 | // recalculate "." and ".." links. | ||
105 | |||
106 | if (mount(".", "/", NULL, MS_MOVE, NULL) || chroot(".") || chdir("/")) | ||
107 | bb_error_msg_and_die("moving root"); | ||
108 | |||
109 | // If a new console specified, redirect stdin/stdout/stderr to that. | ||
110 | |||
111 | if (console) { | ||
112 | close(0); | ||
113 | if(open(console, O_RDWR) < 0) | ||
114 | bb_error_msg_and_die("bad console '%s'", console); | ||
115 | dup2(0, 1); | ||
116 | dup2(0, 2); | ||
117 | } | ||
118 | |||
119 | // Exec real init. (This is why we must be pid 1.) | ||
120 | execv(argv[optind], argv+optind); | ||
121 | bb_error_msg_and_die("bad init '%s'", argv[optind]); | ||
122 | } | ||
diff --git a/util-linux/umount.c b/util-linux/umount.c new file mode 100644 index 000000000..6ba72aed1 --- /dev/null +++ b/util-linux/umount.c | |||
@@ -0,0 +1,151 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Mini umount implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * Copyright (C) 2005 by Rob Landley <rob@landley.net> | ||
7 | * | ||
8 | * Licensed under GPL version 2, see file LICENSE in this tarball for details. | ||
9 | */ | ||
10 | |||
11 | #include "busybox.h" | ||
12 | #include <mntent.h> | ||
13 | #include <getopt.h> | ||
14 | |||
15 | #define OPTION_STRING "flDnravd" | ||
16 | #define OPT_FORCE 1 | ||
17 | #define OPT_LAZY 2 | ||
18 | #define OPT_DONTFREELOOP 4 | ||
19 | #define OPT_NO_MTAB 8 | ||
20 | #define OPT_REMOUNT 16 | ||
21 | #define OPT_ALL (ENABLE_FEATURE_UMOUNT_ALL ? 32 : 0) | ||
22 | |||
23 | int umount_main(int argc, char **argv) | ||
24 | { | ||
25 | int doForce; | ||
26 | char path[2*PATH_MAX]; | ||
27 | struct mntent me; | ||
28 | FILE *fp; | ||
29 | int status = EXIT_SUCCESS; | ||
30 | unsigned opt; | ||
31 | struct mtab_list { | ||
32 | char *dir; | ||
33 | char *device; | ||
34 | struct mtab_list *next; | ||
35 | } *mtl, *m; | ||
36 | |||
37 | /* Parse any options */ | ||
38 | |||
39 | opt = getopt32(argc, argv, OPTION_STRING); | ||
40 | |||
41 | argc -= optind; | ||
42 | argv += optind; | ||
43 | |||
44 | doForce = MAX((opt & OPT_FORCE), (opt & OPT_LAZY)); | ||
45 | |||
46 | /* Get a list of mount points from mtab. We read them all in now mostly | ||
47 | * for umount -a (so we don't have to worry about the list changing while | ||
48 | * we iterate over it, or about getting stuck in a loop on the same failing | ||
49 | * entry. Notice that this also naturally reverses the list so that -a | ||
50 | * umounts the most recent entries first. */ | ||
51 | |||
52 | m = mtl = 0; | ||
53 | |||
54 | /* If we're umounting all, then m points to the start of the list and | ||
55 | * the argument list should be empty (which will match all). */ | ||
56 | |||
57 | fp = setmntent(bb_path_mtab_file, "r"); | ||
58 | if (!fp) { | ||
59 | if (opt & OPT_ALL) | ||
60 | bb_error_msg_and_die("cannot open %s", bb_path_mtab_file); | ||
61 | } else { | ||
62 | while (getmntent_r(fp, &me, path, sizeof(path))) { | ||
63 | m = xmalloc(sizeof(struct mtab_list)); | ||
64 | m->next = mtl; | ||
65 | m->device = xstrdup(me.mnt_fsname); | ||
66 | m->dir = xstrdup(me.mnt_dir); | ||
67 | mtl = m; | ||
68 | } | ||
69 | endmntent(fp); | ||
70 | } | ||
71 | |||
72 | /* If we're not umounting all, we need at least one argument. */ | ||
73 | if (!(opt & OPT_ALL)) { | ||
74 | m = 0; | ||
75 | if (!argc) bb_show_usage(); | ||
76 | } | ||
77 | |||
78 | // Loop through everything we're supposed to umount, and do so. | ||
79 | for (;;) { | ||
80 | int curstat; | ||
81 | char *zapit = *argv; | ||
82 | |||
83 | // Do we already know what to umount this time through the loop? | ||
84 | if (m) safe_strncpy(path, m->dir, PATH_MAX); | ||
85 | // For umount -a, end of mtab means time to exit. | ||
86 | else if (opt & OPT_ALL) break; | ||
87 | // Get next command line argument (and look it up in mtab list) | ||
88 | else if (!argc--) break; | ||
89 | else { | ||
90 | argv++; | ||
91 | realpath(zapit, path); | ||
92 | for (m = mtl; m; m = m->next) | ||
93 | if (!strcmp(path, m->dir) || !strcmp(path, m->device)) | ||
94 | break; | ||
95 | } | ||
96 | // If we couldn't find this sucker in /etc/mtab, punt by passing our | ||
97 | // command line argument straight to the umount syscall. Otherwise, | ||
98 | // umount the directory even if we were given the block device. | ||
99 | if (m) zapit = m->dir; | ||
100 | |||
101 | // Let's ask the thing nicely to unmount. | ||
102 | curstat = umount(zapit); | ||
103 | |||
104 | // Force the unmount, if necessary. | ||
105 | if (curstat && doForce) { | ||
106 | curstat = umount2(zapit, doForce); | ||
107 | if (curstat) | ||
108 | bb_error_msg("forced umount of %s failed!", zapit); | ||
109 | } | ||
110 | |||
111 | // If still can't umount, maybe remount read-only? | ||
112 | if (curstat && (opt & OPT_REMOUNT) && errno == EBUSY && m) { | ||
113 | curstat = mount(m->device, zapit, NULL, MS_REMOUNT|MS_RDONLY, NULL); | ||
114 | bb_error_msg(curstat ? "cannot remount %s read-only" : | ||
115 | "%s busy - remounted read-only", m->device); | ||
116 | } | ||
117 | |||
118 | if (curstat) { | ||
119 | status = EXIT_FAILURE; | ||
120 | bb_perror_msg("cannot umount %s", zapit); | ||
121 | } else { | ||
122 | /* De-allocate the loop device. This ioctl should be ignored on | ||
123 | * any non-loop block devices. */ | ||
124 | if (ENABLE_FEATURE_MOUNT_LOOP && !(opt & OPT_DONTFREELOOP) && m) | ||
125 | del_loop(m->device); | ||
126 | if (ENABLE_FEATURE_MTAB_SUPPORT && !(opt & OPT_NO_MTAB) && m) | ||
127 | erase_mtab(m->dir); | ||
128 | } | ||
129 | |||
130 | // Find next matching mtab entry for -a or umount /dev | ||
131 | // Note this means that "umount /dev/blah" will unmount all instances | ||
132 | // of /dev/blah, not just the most recent. | ||
133 | while (m && (m = m->next)) | ||
134 | if ((opt & OPT_ALL) || !strcmp(path, m->device)) | ||
135 | break; | ||
136 | } | ||
137 | |||
138 | // Free mtab list if necessary | ||
139 | |||
140 | if (ENABLE_FEATURE_CLEAN_UP) { | ||
141 | while (mtl) { | ||
142 | m = mtl->next; | ||
143 | free(mtl->device); | ||
144 | free(mtl->dir); | ||
145 | free(mtl); | ||
146 | mtl = m; | ||
147 | } | ||
148 | } | ||
149 | |||
150 | return status; | ||
151 | } | ||