diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs | |
download | mrst-s0i3-test-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.gz mrst-s0i3-test-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.xz mrst-s0i3-test-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'fs')
978 files changed, 544798 insertions, 0 deletions
diff --git a/fs/Kconfig b/fs/Kconfig new file mode 100644 index 00000000000..6a4ad4bb7a5 --- /dev/null +++ b/fs/Kconfig @@ -0,0 +1,1729 @@ +# +# File system configuration +# + +menu "File systems" + +config EXT2_FS + tristate "Second extended fs support" + help + Ext2 is a standard Linux file system for hard disks. + + To compile this file system support as a module, choose M here: the + module will be called ext2. Be aware however that the file system + of your root partition (the one containing the directory /) cannot + be compiled as a module, and so this could be dangerous. + + If unsure, say Y. + +config EXT2_FS_XATTR + bool "Ext2 extended attributes" + depends on EXT2_FS + help + Extended attributes are name:value pairs associated with inodes by + the kernel or by users (see the attr(5) manual page, or visit + <http://acl.bestbits.at/> for details). + + If unsure, say N. + +config EXT2_FS_POSIX_ACL + bool "Ext2 POSIX Access Control Lists" + depends on EXT2_FS_XATTR + help + Posix Access Control Lists (ACLs) support permissions for users and + groups beyond the owner/group/world scheme. + + To learn more about Access Control Lists, visit the Posix ACLs for + Linux website <http://acl.bestbits.at/>. + + If you don't know what Access Control Lists are, say N + +config EXT2_FS_SECURITY + bool "Ext2 Security Labels" + depends on EXT2_FS_XATTR + help + Security labels support alternative access control models + implemented by security modules like SELinux. This option + enables an extended attribute handler for file security + labels in the ext2 filesystem. + + If you are not using a security module that requires using + extended attributes for file security labels, say N. + +config EXT3_FS + tristate "Ext3 journalling file system support" + help + This is the journaling version of the Second extended file system + (often called ext3), the de facto standard Linux file system + (method to organize files on a storage device) for hard disks. + + The journaling code included in this driver means you do not have + to run e2fsck (file system checker) on your file systems after a + crash. The journal keeps track of any changes that were being made + at the time the system crashed, and can ensure that your file system + is consistent without the need for a lengthy check. + + Other than adding the journal to the file system, the on-disk format + of ext3 is identical to ext2. It is possible to freely switch + between using the ext3 driver and the ext2 driver, as long as the + file system has been cleanly unmounted, or e2fsck is run on the file + system. + + To add a journal on an existing ext2 file system or change the + behavior of ext3 file systems, you can use the tune2fs utility ("man + tune2fs"). To modify attributes of files and directories on ext3 + file systems, use chattr ("man chattr"). You need to be using + e2fsprogs version 1.20 or later in order to create ext3 journals + (available at <http://sourceforge.net/projects/e2fsprogs/>). + + To compile this file system support as a module, choose M here: the + module will be called ext3. Be aware however that the file system + of your root partition (the one containing the directory /) cannot + be compiled as a module, and so this may be dangerous. + +config EXT3_FS_XATTR + bool "Ext3 extended attributes" + depends on EXT3_FS + default y + help + Extended attributes are name:value pairs associated with inodes by + the kernel or by users (see the attr(5) manual page, or visit + <http://acl.bestbits.at/> for details). + + If unsure, say N. + + You need this for POSIX ACL support on ext3. + +config EXT3_FS_POSIX_ACL + bool "Ext3 POSIX Access Control Lists" + depends on EXT3_FS_XATTR + help + Posix Access Control Lists (ACLs) support permissions for users and + groups beyond the owner/group/world scheme. + + To learn more about Access Control Lists, visit the Posix ACLs for + Linux website <http://acl.bestbits.at/>. + + If you don't know what Access Control Lists are, say N + +config EXT3_FS_SECURITY + bool "Ext3 Security Labels" + depends on EXT3_FS_XATTR + help + Security labels support alternative access control models + implemented by security modules like SELinux. This option + enables an extended attribute handler for file security + labels in the ext3 filesystem. + + If you are not using a security module that requires using + extended attributes for file security labels, say N. + +config JBD +# CONFIG_JBD could be its own option (even modular), but until there are +# other users than ext3, we will simply make it be the same as CONFIG_EXT3_FS +# dep_tristate ' Journal Block Device support (JBD for ext3)' CONFIG_JBD $CONFIG_EXT3_FS + tristate + default EXT3_FS + help + This is a generic journaling layer for block devices. It is + currently used by the ext3 file system, but it could also be used to + add journal support to other file systems or block devices such as + RAID or LVM. + + If you are using the ext3 file system, you need to say Y here. If + you are not using ext3 then you will probably want to say N. + + To compile this device as a module, choose M here: the module will be + called jbd. If you are compiling ext3 into the kernel, you cannot + compile this code as a module. + +config JBD_DEBUG + bool "JBD (ext3) debugging support" + depends on JBD + help + If you are using the ext3 journaled file system (or potentially any + other file system/device using JBD), this option allows you to + enable debugging output while the system is running, in order to + help track down any problems you are having. By default the + debugging output will be turned off. + + If you select Y here, then you will be able to turn on debugging + with "echo N > /proc/sys/fs/jbd-debug", where N is a number between + 1 and 5, the higher the number, the more debugging output is + generated. To turn debugging off again, do + "echo 0 > /proc/sys/fs/jbd-debug". + +config FS_MBCACHE +# Meta block cache for Extended Attributes (ext2/ext3) + tristate + depends on EXT2_FS_XATTR || EXT3_FS_XATTR + default y if EXT2_FS=y || EXT3_FS=y + default m if EXT2_FS=m || EXT3_FS=m + +config REISERFS_FS + tristate "Reiserfs support" + help + Stores not just filenames but the files themselves in a balanced + tree. Uses journaling. + + Balanced trees are more efficient than traditional file system + architectural foundations. + + In general, ReiserFS is as fast as ext2, but is very efficient with + large directories and small files. Additional patches are needed + for NFS and quotas, please see <http://www.namesys.com/> for links. + + It is more easily extended to have features currently found in + database and keyword search systems than block allocation based file + systems are. The next version will be so extended, and will support + plugins consistent with our motto ``It takes more than a license to + make source code open.'' + + Read <http://www.namesys.com/> to learn more about reiserfs. + + Sponsored by Threshold Networks, Emusic.com, and Bigstorage.com. + + If you like it, you can pay us to add new features to it that you + need, buy a support contract, or pay us to port it to another OS. + +config REISERFS_CHECK + bool "Enable reiserfs debug mode" + depends on REISERFS_FS + help + If you set this to Y, then ReiserFS will perform every check it can + possibly imagine of its internal consistency throughout its + operation. It will also go substantially slower. More than once we + have forgotten that this was on, and then gone despondent over the + latest benchmarks.:-) Use of this option allows our team to go all + out in checking for consistency when debugging without fear of its + effect on end users. If you are on the verge of sending in a bug + report, say Y and you might get a useful error message. Almost + everyone should say N. + +config REISERFS_PROC_INFO + bool "Stats in /proc/fs/reiserfs" + depends on REISERFS_FS + help + Create under /proc/fs/reiserfs a hierarchy of files, displaying + various ReiserFS statistics and internal data at the expense of + making your kernel or module slightly larger (+8 KB). This also + increases the amount of kernel memory required for each mount. + Almost everyone but ReiserFS developers and people fine-tuning + reiserfs or tracing problems should say N. + +config REISERFS_FS_XATTR + bool "ReiserFS extended attributes" + depends on REISERFS_FS + help + Extended attributes are name:value pairs associated with inodes by + the kernel or by users (see the attr(5) manual page, or visit + <http://acl.bestbits.at/> for details). + + If unsure, say N. + +config REISERFS_FS_POSIX_ACL + bool "ReiserFS POSIX Access Control Lists" + depends on REISERFS_FS_XATTR + help + Posix Access Control Lists (ACLs) support permissions for users and + groups beyond the owner/group/world scheme. + + To learn more about Access Control Lists, visit the Posix ACLs for + Linux website <http://acl.bestbits.at/>. + + If you don't know what Access Control Lists are, say N + +config REISERFS_FS_SECURITY + bool "ReiserFS Security Labels" + depends on REISERFS_FS_XATTR + help + Security labels support alternative access control models + implemented by security modules like SELinux. This option + enables an extended attribute handler for file security + labels in the ReiserFS filesystem. + + If you are not using a security module that requires using + extended attributes for file security labels, say N. + +config JFS_FS + tristate "JFS filesystem support" + select NLS + help + This is a port of IBM's Journaled Filesystem . More information is + available in the file <file:Documentation/filesystems/jfs.txt>. + + If you do not intend to use the JFS filesystem, say N. + +config JFS_POSIX_ACL + bool "JFS POSIX Access Control Lists" + depends on JFS_FS + help + Posix Access Control Lists (ACLs) support permissions for users and + groups beyond the owner/group/world scheme. + + To learn more about Access Control Lists, visit the Posix ACLs for + Linux website <http://acl.bestbits.at/>. + + If you don't know what Access Control Lists are, say N + +config JFS_SECURITY + bool "JFS Security Labels" + depends on JFS_FS + help + Security labels support alternative access control models + implemented by security modules like SELinux. This option + enables an extended attribute handler for file security + labels in the jfs filesystem. + + If you are not using a security module that requires using + extended attributes for file security labels, say N. + +config JFS_DEBUG + bool "JFS debugging" + depends on JFS_FS + help + If you are experiencing any problems with the JFS filesystem, say + Y here. This will result in additional debugging messages to be + written to the system log. Under normal circumstances, this + results in very little overhead. + +config JFS_STATISTICS + bool "JFS statistics" + depends on JFS_FS + help + Enabling this option will cause statistics from the JFS file system + to be made available to the user in the /proc/fs/jfs/ directory. + +config FS_POSIX_ACL +# Posix ACL utility routines (for now, only ext2/ext3/jfs/reiserfs) +# +# NOTE: you can implement Posix ACLs without these helpers (XFS does). +# Never use this symbol for ifdefs. +# + bool + depends on EXT2_FS_POSIX_ACL || EXT3_FS_POSIX_ACL || JFS_POSIX_ACL || REISERFS_FS_POSIX_ACL || NFSD_V4 + default y + +source "fs/xfs/Kconfig" + +config MINIX_FS + tristate "Minix fs support" + help + Minix is a simple operating system used in many classes about OS's. + The minix file system (method to organize files on a hard disk + partition or a floppy disk) was the original file system for Linux, + but has been superseded by the second extended file system ext2fs. + You don't want to use the minix file system on your hard disk + because of certain built-in restrictions, but it is sometimes found + on older Linux floppy disks. This option will enlarge your kernel + by about 28 KB. If unsure, say N. + + To compile this file system support as a module, choose M here: the + module will be called minix. Note that the file system of your root + partition (the one containing the directory /) cannot be compiled as + a module. + +config ROMFS_FS + tristate "ROM file system support" + ---help--- + This is a very small read-only file system mainly intended for + initial ram disks of installation disks, but it could be used for + other read-only media as well. Read + <file:Documentation/filesystems/romfs.txt> for details. + + To compile this file system support as a module, choose M here: the + module will be called romfs. Note that the file system of your + root partition (the one containing the directory /) cannot be a + module. + + If you don't know whether you need it, then you don't need it: + answer N. + +config QUOTA + bool "Quota support" + help + If you say Y here, you will be able to set per user limits for disk + usage (also called disk quotas). Currently, it works for the + ext2, ext3, and reiserfs file system. ext3 also supports journalled + quotas for which you don't need to run quotacheck(8) after an unclean + shutdown. You need additional software in order to use quota support + (you can download sources from + <http://www.sf.net/projects/linuxquota/>). For further details, read + the Quota mini-HOWTO, available from + <http://www.tldp.org/docs.html#howto>, or the documentation provided + with the quota tools. Probably the quota support is only useful for + multi user systems. If unsure, say N. + +config QFMT_V1 + tristate "Old quota format support" + depends on QUOTA + help + This quota format was (is) used by kernels earlier than 2.4.22. If + you have quota working and you don't want to convert to new quota + format say Y here. + +config QFMT_V2 + tristate "Quota format v2 support" + depends on QUOTA + help + This quota format allows using quotas with 32-bit UIDs/GIDs. If you + need this functionality say Y here. Note that you will need recent + quota utilities (>= 3.01) for new quota format with this kernel. + +config QUOTACTL + bool + depends on XFS_QUOTA || QUOTA + default y + +config DNOTIFY + bool "Dnotify support" if EMBEDDED + default y + help + Dnotify is a directory-based per-fd file change notification system + that uses signals to communicate events to user-space. There exist + superior alternatives, but some applications may still rely on + dnotify. + + Because of this, if unsure, say Y. + +config AUTOFS_FS + tristate "Kernel automounter support" + help + The automounter is a tool to automatically mount remote file systems + on demand. This implementation is partially kernel-based to reduce + overhead in the already-mounted case; this is unlike the BSD + automounter (amd), which is a pure user space daemon. + + To use the automounter you need the user-space tools from the autofs + package; you can find the location in <file:Documentation/Changes>. + You also want to answer Y to "NFS file system support", below. + + If you want to use the newer version of the automounter with more + features, say N here and say Y to "Kernel automounter v4 support", + below. + + To compile this support as a module, choose M here: the module will be + called autofs. + + If you are not a part of a fairly large, distributed network, you + probably do not need an automounter, and can say N here. + +config AUTOFS4_FS + tristate "Kernel automounter version 4 support (also supports v3)" + help + The automounter is a tool to automatically mount remote file systems + on demand. This implementation is partially kernel-based to reduce + overhead in the already-mounted case; this is unlike the BSD + automounter (amd), which is a pure user space daemon. + + To use the automounter you need the user-space tools from + <ftp://ftp.kernel.org/pub/linux/daemons/autofs/v4/>; you also + want to answer Y to "NFS file system support", below. + + To compile this support as a module, choose M here: the module will be + called autofs4. You will need to add "alias autofs autofs4" to your + modules configuration file. + + If you are not a part of a fairly large, distributed network or + don't have a laptop which needs to dynamically reconfigure to the + local network, you probably do not need an automounter, and can say + N here. + +menu "CD-ROM/DVD Filesystems" + +config ISO9660_FS + tristate "ISO 9660 CDROM file system support" + help + This is the standard file system used on CD-ROMs. It was previously + known as "High Sierra File System" and is called "hsfs" on other + Unix systems. The so-called Rock-Ridge extensions which allow for + long Unix filenames and symbolic links are also supported by this + driver. If you have a CD-ROM drive and want to do more with it than + just listen to audio CDs and watch its LEDs, say Y (and read + <file:Documentation/filesystems/isofs.txt> and the CD-ROM-HOWTO, + available from <http://www.tldp.org/docs.html#howto>), thereby + enlarging your kernel by about 27 KB; otherwise say N. + + To compile this file system support as a module, choose M here: the + module will be called isofs. + +config JOLIET + bool "Microsoft Joliet CDROM extensions" + depends on ISO9660_FS + select NLS + help + Joliet is a Microsoft extension for the ISO 9660 CD-ROM file system + which allows for long filenames in unicode format (unicode is the + new 16 bit character code, successor to ASCII, which encodes the + characters of almost all languages of the world; see + <http://www.unicode.org/> for more information). Say Y here if you + want to be able to read Joliet CD-ROMs under Linux. + +config ZISOFS + bool "Transparent decompression extension" + depends on ISO9660_FS + select ZLIB_INFLATE + help + This is a Linux-specific extension to RockRidge which lets you store + data in compressed form on a CD-ROM and have it transparently + decompressed when the CD-ROM is accessed. See + <http://www.kernel.org/pub/linux/utils/fs/zisofs/> for the tools + necessary to create such a filesystem. Say Y here if you want to be + able to read such compressed CD-ROMs. + +config ZISOFS_FS +# for fs/nls/Config.in + tristate + depends on ZISOFS + default ISO9660_FS + +config UDF_FS + tristate "UDF file system support" + help + This is the new file system used on some CD-ROMs and DVDs. Say Y if + you intend to mount DVD discs or CDRW's written in packet mode, or + if written to by other UDF utilities, such as DirectCD. + Please read <file:Documentation/filesystems/udf.txt>. + + To compile this file system support as a module, choose M here: the + module will be called udf. + + If unsure, say N. + +config UDF_NLS + bool + default y + depends on (UDF_FS=m && NLS) || (UDF_FS=y && NLS=y) + +endmenu + +menu "DOS/FAT/NT Filesystems" + +config FAT_FS + tristate + select NLS + help + If you want to use one of the FAT-based file systems (the MS-DOS and + VFAT (Windows 95) file systems), then you must say Y or M here + to include FAT support. You will then be able to mount partitions or + diskettes with FAT-based file systems and transparently access the + files on them, i.e. MSDOS files will look and behave just like all + other Unix files. + + This FAT support is not a file system in itself, it only provides + the foundation for the other file systems. You will have to say Y or + M to at least one of "MSDOS fs support" or "VFAT fs support" in + order to make use of it. + + Another way to read and write MSDOS floppies and hard drive + partitions from within Linux (but not transparently) is with the + mtools ("man mtools") program suite. You don't need to say Y here in + order to do that. + + If you need to move large files on floppies between a DOS and a + Linux box, say Y here, mount the floppy under Linux with an MSDOS + file system and use GNU tar's M option. GNU tar is a program + available for Unix and DOS ("man tar" or "info tar"). + + It is now also becoming possible to read and write compressed FAT + file systems; read <file:Documentation/filesystems/fat_cvf.txt> for + details. + + The FAT support will enlarge your kernel by about 37 KB. If unsure, + say Y. + + To compile this as a module, choose M here: the module will be called + fat. Note that if you compile the FAT support as a module, you + cannot compile any of the FAT-based file systems into the kernel + -- they will have to be modules as well. + +config MSDOS_FS + tristate "MSDOS fs support" + select FAT_FS + help + This allows you to mount MSDOS partitions of your hard drive (unless + they are compressed; to access compressed MSDOS partitions under + Linux, you can either use the DOS emulator DOSEMU, described in the + DOSEMU-HOWTO, available from + <http://www.tldp.org/docs.html#howto>, or try dmsdosfs in + <ftp://ibiblio.org/pub/Linux/system/filesystems/dosfs/>. If you + intend to use dosemu with a non-compressed MSDOS partition, say Y + here) and MSDOS floppies. This means that file access becomes + transparent, i.e. the MSDOS files look and behave just like all + other Unix files. + + If you have Windows 95 or Windows NT installed on your MSDOS + partitions, you should use the VFAT file system (say Y to "VFAT fs + support" below), or you will not be able to see the long filenames + generated by Windows 95 / Windows NT. + + This option will enlarge your kernel by about 7 KB. If unsure, + answer Y. This will only work if you said Y to "DOS FAT fs support" + as well. To compile this as a module, choose M here: the module will + be called msdos. + +config VFAT_FS + tristate "VFAT (Windows-95) fs support" + select FAT_FS + help + This option provides support for normal Windows file systems with + long filenames. That includes non-compressed FAT-based file systems + used by Windows 95, Windows 98, Windows NT 4.0, and the Unix + programs from the mtools package. + + The VFAT support enlarges your kernel by about 10 KB and it only + works if you said Y to the "DOS FAT fs support" above. Please read + the file <file:Documentation/filesystems/vfat.txt> for details. If + unsure, say Y. + + To compile this as a module, choose M here: the module will be called + vfat. + +config FAT_DEFAULT_CODEPAGE + int "Default codepage for FAT" + depends on MSDOS_FS || VFAT_FS + default 437 + help + This option should be set to the codepage of your FAT filesystems. + It can be overridden with the "codepage" mount option. + See <file:Documentation/filesystems/vfat.txt> for more information. + +config FAT_DEFAULT_IOCHARSET + string "Default iocharset for FAT" + depends on VFAT_FS + default "iso8859-1" + help + Set this to the default input/output character set you'd + like FAT to use. It should probably match the character set + that most of your FAT filesystems use, and can be overridden + with the "iocharset" mount option for FAT filesystems. + Note that "utf8" is not recommended for FAT filesystems. + If unsure, you shouldn't set "utf8" here. + See <file:Documentation/filesystems/vfat.txt> for more information. + +config NTFS_FS + tristate "NTFS file system support" + select NLS + help + NTFS is the file system of Microsoft Windows NT, 2000, XP and 2003. + + Saying Y or M here enables read support. There is partial, but + safe, write support available. For write support you must also + say Y to "NTFS write support" below. + + There are also a number of user-space tools available, called + ntfsprogs. These include ntfsundelete and ntfsresize, that work + without NTFS support enabled in the kernel. + + This is a rewrite from scratch of Linux NTFS support and replaced + the old NTFS code starting with Linux 2.5.11. A backport to + the Linux 2.4 kernel series is separately available as a patch + from the project web site. + + For more information see <file:Documentation/filesystems/ntfs.txt> + and <http://linux-ntfs.sourceforge.net/>. + + To compile this file system support as a module, choose M here: the + module will be called ntfs. + + If you are not using Windows NT, 2000, XP or 2003 in addition to + Linux on your computer it is safe to say N. + +config NTFS_DEBUG + bool "NTFS debugging support" + depends on NTFS_FS + help + If you are experiencing any problems with the NTFS file system, say + Y here. This will result in additional consistency checks to be + performed by the driver as well as additional debugging messages to + be written to the system log. Note that debugging messages are + disabled by default. To enable them, supply the option debug_msgs=1 + at the kernel command line when booting the kernel or as an option + to insmod when loading the ntfs module. Once the driver is active, + you can enable debugging messages by doing (as root): + echo 1 > /proc/sys/fs/ntfs-debug + Replacing the "1" with "0" would disable debug messages. + + If you leave debugging messages disabled, this results in little + overhead, but enabling debug messages results in very significant + slowdown of the system. + + When reporting bugs, please try to have available a full dump of + debugging messages while the misbehaviour was occurring. + +config NTFS_RW + bool "NTFS write support" + depends on NTFS_FS + help + This enables the partial, but safe, write support in the NTFS driver. + + The only supported operation is overwriting existing files, without + changing the file length. No file or directory creation, deletion or + renaming is possible. Note only non-resident files can be written to + so you may find that some very small files (<500 bytes or so) cannot + be written to. + + While we cannot guarantee that it will not damage any data, we have + so far not received a single report where the driver would have + damaged someones data so we assume it is perfectly safe to use. + + Note: While write support is safe in this version (a rewrite from + scratch of the NTFS support), it should be noted that the old NTFS + write support, included in Linux 2.5.10 and before (since 1997), + is not safe. + + This is currently useful with TopologiLinux. TopologiLinux is run + on top of any DOS/Microsoft Windows system without partitioning your + hard disk. Unlike other Linux distributions TopologiLinux does not + need its own partition. For more information see + <http://topologi-linux.sourceforge.net/> + + It is perfectly safe to say N here. + +endmenu + +menu "Pseudo filesystems" + +config PROC_FS + bool "/proc file system support" + help + This is a virtual file system providing information about the status + of the system. "Virtual" means that it doesn't take up any space on + your hard disk: the files are created on the fly by the kernel when + you try to access them. Also, you cannot read the files with older + version of the program less: you need to use more or cat. + + It's totally cool; for example, "cat /proc/interrupts" gives + information about what the different IRQs are used for at the moment + (there is a small number of Interrupt ReQuest lines in your computer + that are used by the attached devices to gain the CPU's attention -- + often a source of trouble if two devices are mistakenly configured + to use the same IRQ). The program procinfo to display some + information about your system gathered from the /proc file system. + + Before you can use the /proc file system, it has to be mounted, + meaning it has to be given a location in the directory hierarchy. + That location should be /proc. A command such as "mount -t proc proc + /proc" or the equivalent line in /etc/fstab does the job. + + The /proc file system is explained in the file + <file:Documentation/filesystems/proc.txt> and on the proc(5) manpage + ("man 5 proc"). + + This option will enlarge your kernel by about 67 KB. Several + programs depend on this, so everyone should say Y here. + +config PROC_KCORE + bool "/proc/kcore support" if !ARM + depends on PROC_FS && MMU + +config SYSFS + bool "sysfs file system support" if EMBEDDED + default y + help + The sysfs filesystem is a virtual filesystem that the kernel uses to + export internal kernel objects, their attributes, and their + relationships to one another. + + Users can use sysfs to ascertain useful information about the running + kernel, such as the devices the kernel has discovered on each bus and + which driver each is bound to. sysfs can also be used to tune devices + and other kernel subsystems. + + Some system agents rely on the information in sysfs to operate. + /sbin/hotplug uses device and object attributes in sysfs to assist in + delegating policy decisions, like persistantly naming devices. + + sysfs is currently used by the block subsystem to mount the root + partition. If sysfs is disabled you must specify the boot device on + the kernel boot command line via its major and minor numbers. For + example, "root=03:01" for /dev/hda1. + + Designers of embedded systems may wish to say N here to conserve space. + +config DEVFS_FS + bool "/dev file system support (OBSOLETE)" + depends on EXPERIMENTAL + help + This is support for devfs, a virtual file system (like /proc) which + provides the file system interface to device drivers, normally found + in /dev. Devfs does not depend on major and minor number + allocations. Device drivers register entries in /dev which then + appear automatically, which means that the system administrator does + not have to create character and block special device files in the + /dev directory using the mknod command (or MAKEDEV script) anymore. + + This is work in progress. If you want to use this, you *must* read + the material in <file:Documentation/filesystems/devfs/>, especially + the file README there. + + Note that devfs no longer manages /dev/pts! If you are using UNIX98 + ptys, you will also need to mount the /dev/pts filesystem (devpts). + + Note that devfs has been obsoleted by udev, + <http://www.kernel.org/pub/linux/utils/kernel/hotplug/>. + It has been stripped down to a bare minimum and is only provided for + legacy installations that use its naming scheme which is + unfortunately different from the names normal Linux installations + use. + + If unsure, say N. + +config DEVFS_MOUNT + bool "Automatically mount at boot" + depends on DEVFS_FS + help + This option appears if you have CONFIG_DEVFS_FS enabled. Setting + this to 'Y' will make the kernel automatically mount devfs onto /dev + when the system is booted, before the init thread is started. + You can override this with the "devfs=nomount" boot option. + + If unsure, say N. + +config DEVFS_DEBUG + bool "Debug devfs" + depends on DEVFS_FS + help + If you say Y here, then the /dev file system code will generate + debugging messages. See the file + <file:Documentation/filesystems/devfs/boot-options> for more + details. + + If unsure, say N. + +config DEVPTS_FS_XATTR + bool "/dev/pts Extended Attributes" + depends on UNIX98_PTYS + help + Extended attributes are name:value pairs associated with inodes by + the kernel or by users (see the attr(5) manual page, or visit + <http://acl.bestbits.at/> for details). + + If unsure, say N. + +config DEVPTS_FS_SECURITY + bool "/dev/pts Security Labels" + depends on DEVPTS_FS_XATTR + help + Security labels support alternative access control models + implemented by security modules like SELinux. This option + enables an extended attribute handler for file security + labels in the /dev/pts filesystem. + + If you are not using a security module that requires using + extended attributes for file security labels, say N. + +config TMPFS + bool "Virtual memory file system support (former shm fs)" + help + Tmpfs is a file system which keeps all files in virtual memory. + + Everything in tmpfs is temporary in the sense that no files will be + created on your hard drive. The files live in memory and swap + space. If you unmount a tmpfs instance, everything stored therein is + lost. + + See <file:Documentation/filesystems/tmpfs.txt> for details. + +config TMPFS_XATTR + bool "tmpfs Extended Attributes" + depends on TMPFS + help + Extended attributes are name:value pairs associated with inodes by + the kernel or by users (see the attr(5) manual page, or visit + <http://acl.bestbits.at/> for details). + + If unsure, say N. + +config TMPFS_SECURITY + bool "tmpfs Security Labels" + depends on TMPFS_XATTR + help + Security labels support alternative access control models + implemented by security modules like SELinux. This option + enables an extended attribute handler for file security + labels in the tmpfs filesystem. + If you are not using a security module that requires using + extended attributes for file security labels, say N. + +config HUGETLBFS + bool "HugeTLB file system support" + depends X86 || IA64 || PPC64 || SPARC64 || SUPERH || X86_64 || BROKEN + +config HUGETLB_PAGE + def_bool HUGETLBFS + +config RAMFS + bool + default y + ---help--- + Ramfs is a file system which keeps all files in RAM. It allows + read and write access. + + It is more of an programming example than a useable file system. If + you need a file system which lives in RAM with limit checking use + tmpfs. + + To compile this as a module, choose M here: the module will be called + ramfs. + +endmenu + +menu "Miscellaneous filesystems" + +config ADFS_FS + tristate "ADFS file system support (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + The Acorn Disc Filing System is the standard file system of the + RiscOS operating system which runs on Acorn's ARM-based Risc PC + systems and the Acorn Archimedes range of machines. If you say Y + here, Linux will be able to read from ADFS partitions on hard drives + and from ADFS-formatted floppy discs. If you also want to be able to + write to those devices, say Y to "ADFS write support" below. + + The ADFS partition should be the first partition (i.e., + /dev/[hs]d?1) on each of your drives. Please read the file + <file:Documentation/filesystems/adfs.txt> for further details. + + To compile this code as a module, choose M here: the module will be + called adfs. + + If unsure, say N. + +config ADFS_FS_RW + bool "ADFS write support (DANGEROUS)" + depends on ADFS_FS + help + If you say Y here, you will be able to write to ADFS partitions on + hard drives and ADFS-formatted floppy disks. This is experimental + codes, so if you're unsure, say N. + +config AFFS_FS + tristate "Amiga FFS file system support (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + The Fast File System (FFS) is the common file system used on hard + disks by Amiga(tm) systems since AmigaOS Version 1.3 (34.20). Say Y + if you want to be able to read and write files from and to an Amiga + FFS partition on your hard drive. Amiga floppies however cannot be + read with this driver due to an incompatibility of the floppy + controller used in an Amiga and the standard floppy controller in + PCs and workstations. Read <file:Documentation/filesystems/affs.txt> + and <file:fs/affs/Changes>. + + With this driver you can also mount disk files used by Bernd + Schmidt's Un*X Amiga Emulator + (<http://www.freiburg.linux.de/~uae/>). + If you want to do this, you will also need to say Y or M to "Loop + device support", above. + + To compile this file system support as a module, choose M here: the + module will be called affs. If unsure, say N. + +config HFS_FS + tristate "Apple Macintosh file system support (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + If you say Y here, you will be able to mount Macintosh-formatted + floppy disks and hard drive partitions with full read-write access. + Please read <file:fs/hfs/HFS.txt> to learn about the available mount + options. + + To compile this file system support as a module, choose M here: the + module will be called hfs. + +config HFSPLUS_FS + tristate "Apple Extended HFS file system support" + select NLS + select NLS_UTF8 + help + If you say Y here, you will be able to mount extended format + Macintosh-formatted hard drive partitions with full read-write access. + + This file system is often called HFS+ and was introduced with + MacOS 8. It includes all Mac specific filesystem data such as + data forks and creator codes, but it also has several UNIX + style features such as file ownership and permissions. + +config BEFS_FS + tristate "BeOS file system (BeFS) support (read only) (EXPERIMENTAL)" + depends on EXPERIMENTAL + select NLS + help + The BeOS File System (BeFS) is the native file system of Be, Inc's + BeOS. Notable features include support for arbitrary attributes + on files and directories, and database-like indeces on selected + attributes. (Also note that this driver doesn't make those features + available at this time). It is a 64 bit filesystem, so it supports + extremly large volumes and files. + + If you use this filesystem, you should also say Y to at least one + of the NLS (native language support) options below. + + If you don't know what this is about, say N. + + To compile this as a module, choose M here: the module will be + called befs. + +config BEFS_DEBUG + bool "Debug BeFS" + depends on BEFS_FS + help + If you say Y here, you can use the 'debug' mount option to enable + debugging output from the driver. + +config BFS_FS + tristate "BFS file system support (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + Boot File System (BFS) is a file system used under SCO UnixWare to + allow the bootloader access to the kernel image and other important + files during the boot process. It is usually mounted under /stand + and corresponds to the slice marked as "STAND" in the UnixWare + partition. You should say Y if you want to read or write the files + on your /stand slice from within Linux. You then also need to say Y + to "UnixWare slices support", below. More information about the BFS + file system is contained in the file + <file:Documentation/filesystems/bfs.txt>. + + If you don't know what this is about, say N. + + To compile this as a module, choose M here: the module will be called + bfs. Note that the file system of your root partition (the one + containing the directory /) cannot be compiled as a module. + + + +config EFS_FS + tristate "EFS file system support (read only) (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + EFS is an older file system used for non-ISO9660 CD-ROMs and hard + disk partitions by SGI's IRIX operating system (IRIX 6.0 and newer + uses the XFS file system for hard disk partitions however). + + This implementation only offers read-only access. If you don't know + what all this is about, it's safe to say N. For more information + about EFS see its home page at <http://aeschi.ch.eu.org/efs/>. + + To compile the EFS file system support as a module, choose M here: the + module will be called efs. + +config JFFS_FS + tristate "Journalling Flash File System (JFFS) support" + depends on MTD + help + JFFS is the Journaling Flash File System developed by Axis + Communications in Sweden, aimed at providing a crash/powerdown-safe + file system for disk-less embedded devices. Further information is + available at (<http://developer.axis.com/software/jffs/>). + +config JFFS_FS_VERBOSE + int "JFFS debugging verbosity (0 = quiet, 3 = noisy)" + depends on JFFS_FS + default "0" + help + Determines the verbosity level of the JFFS debugging messages. + +config JFFS_PROC_FS + bool "JFFS stats available in /proc filesystem" + depends on JFFS_FS && PROC_FS + help + Enabling this option will cause statistics from mounted JFFS file systems + to be made available to the user in the /proc/fs/jffs/ directory. + +config JFFS2_FS + tristate "Journalling Flash File System v2 (JFFS2) support" + select CRC32 + depends on MTD + help + JFFS2 is the second generation of the Journalling Flash File System + for use on diskless embedded devices. It provides improved wear + levelling, compression and support for hard links. You cannot use + this on normal block devices, only on 'MTD' devices. + + Further information on the design and implementation of JFFS2 is + available at <http://sources.redhat.com/jffs2/>. + +config JFFS2_FS_DEBUG + int "JFFS2 debugging verbosity (0 = quiet, 2 = noisy)" + depends on JFFS2_FS + default "0" + help + This controls the amount of debugging messages produced by the JFFS2 + code. Set it to zero for use in production systems. For evaluation, + testing and debugging, it's advisable to set it to one. This will + enable a few assertions and will print debugging messages at the + KERN_DEBUG loglevel, where they won't normally be visible. Level 2 + is unlikely to be useful - it enables extra debugging in certain + areas which at one point needed debugging, but when the bugs were + located and fixed, the detailed messages were relegated to level 2. + + If reporting bugs, please try to have available a full dump of the + messages at debug level 1 while the misbehaviour was occurring. + +config JFFS2_FS_NAND + bool "JFFS2 support for NAND flash" + depends on JFFS2_FS + default n + help + This enables the support for NAND flash in JFFS2. NAND is a newer + type of flash chip design than the traditional NOR flash, with + higher density but a handful of characteristics which make it more + interesting for the file system to use. + + Say 'N' unless you have NAND flash. + +config JFFS2_FS_NOR_ECC + bool "JFFS2 support for ECC'd NOR flash (EXPERIMENTAL)" + depends on JFFS2_FS && EXPERIMENTAL + default n + help + This enables the experimental support for NOR flash with transparent + ECC for JFFS2. This type of flash chip is not common, however it is + available from ST Microelectronics. + +config JFFS2_COMPRESSION_OPTIONS + bool "Advanced compression options for JFFS2" + depends on JFFS2_FS + default n + help + Enabling this option allows you to explicitly choose which + compression modules, if any, are enabled in JFFS2. Removing + compressors and mean you cannot read existing file systems, + and enabling experimental compressors can mean that you + write a file system which cannot be read by a standard kernel. + + If unsure, you should _definitely_ say 'N'. + +config JFFS2_ZLIB + bool "JFFS2 ZLIB compression support" if JFFS2_COMPRESSION_OPTIONS + select ZLIB_INFLATE + select ZLIB_DEFLATE + depends on JFFS2_FS + default y + help + Zlib is designed to be a free, general-purpose, legally unencumbered, + lossless data-compression library for use on virtually any computer + hardware and operating system. See <http://www.gzip.org/zlib/> for + further information. + + Say 'Y' if unsure. + +config JFFS2_RTIME + bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS + depends on JFFS2_FS + default y + help + Rtime does manage to recompress already-compressed data. Say 'Y' if unsure. + +config JFFS2_RUBIN + bool "JFFS2 RUBIN compression support" if JFFS2_COMPRESSION_OPTIONS + depends on JFFS2_FS + default n + help + RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure. + +choice + prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS + default JFFS2_CMODE_PRIORITY + depends on JFFS2_FS + help + You can set here the default compression mode of JFFS2 from + the available compression modes. Don't touch if unsure. + +config JFFS2_CMODE_NONE + bool "no compression" + help + Uses no compression. + +config JFFS2_CMODE_PRIORITY + bool "priority" + help + Tries the compressors in a predefinied order and chooses the first + successful one. + +config JFFS2_CMODE_SIZE + bool "size (EXPERIMENTAL)" + help + Tries all compressors and chooses the one which has the smallest + result. + +endchoice + +config CRAMFS + tristate "Compressed ROM file system support (cramfs)" + select ZLIB_INFLATE + help + Saying Y here includes support for CramFs (Compressed ROM File + System). CramFs is designed to be a simple, small, and compressed + file system for ROM based embedded systems. CramFs is read-only, + limited to 256MB file systems (with 16MB files), and doesn't support + 16/32 bits uid/gid, hard links and timestamps. + + See <file:Documentation/filesystems/cramfs.txt> and + <file:fs/cramfs/README> for further information. + + To compile this as a module, choose M here: the module will be called + cramfs. Note that the root file system (the one containing the + directory /) cannot be compiled as a module. + + If unsure, say N. + +config VXFS_FS + tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)" + help + FreeVxFS is a file system driver that support the VERITAS VxFS(TM) + file system format. VERITAS VxFS(TM) is the standard file system + of SCO UnixWare (and possibly others) and optionally available + for Sunsoft Solaris, HP-UX and many other operating systems. + Currently only readonly access is supported. + + NOTE: the file system type as used by mount(1), mount(2) and + fstab(5) is 'vxfs' as it describes the file system format, not + the actual driver. + + To compile this as a module, choose M here: the module will be + called freevxfs. If unsure, say N. + + +config HPFS_FS + tristate "OS/2 HPFS file system support" + help + OS/2 is IBM's operating system for PC's, the same as Warp, and HPFS + is the file system used for organizing files on OS/2 hard disk + partitions. Say Y if you want to be able to read files from and + write files to an OS/2 HPFS partition on your hard drive. OS/2 + floppies however are in regular MSDOS format, so you don't need this + option in order to be able to read them. Read + <file:Documentation/filesystems/hpfs.txt>. + + To compile this file system support as a module, choose M here: the + module will be called hpfs. If unsure, say N. + + + +config QNX4FS_FS + tristate "QNX4 file system support (read only)" + help + This is the file system used by the real-time operating systems + QNX 4 and QNX 6 (the latter is also called QNX RTP). + Further information is available at <http://www.qnx.com/>. + Say Y if you intend to mount QNX hard disks or floppies. + Unless you say Y to "QNX4FS read-write support" below, you will + only be able to read these file systems. + + To compile this file system support as a module, choose M here: the + module will be called qnx4. + + If you don't know whether you need it, then you don't need it: + answer N. + +config QNX4FS_RW + bool "QNX4FS write support (DANGEROUS)" + depends on QNX4FS_FS && EXPERIMENTAL && BROKEN + help + Say Y if you want to test write support for QNX4 file systems. + + It's currently broken, so for now: + answer N. + + + +config SYSV_FS + tristate "System V/Xenix/V7/Coherent file system support" + help + SCO, Xenix and Coherent are commercial Unix systems for Intel + machines, and Version 7 was used on the DEC PDP-11. Saying Y + here would allow you to read from their floppies and hard disk + partitions. + + If you have floppies or hard disk partitions like that, it is likely + that they contain binaries from those other Unix systems; in order + to run these binaries, you will want to install linux-abi which is a + a set of kernel modules that lets you run SCO, Xenix, Wyse, + UnixWare, Dell Unix and System V programs under Linux. It is + available via FTP (user: ftp) from + <ftp://ftp.openlinux.org/pub/people/hch/linux-abi/>). + NOTE: that will work only for binaries from Intel-based systems; + PDP ones will have to wait until somebody ports Linux to -11 ;-) + + If you only intend to mount files from some other Unix over the + network using NFS, you don't need the System V file system support + (but you need NFS file system support obviously). + + Note that this option is generally not needed for floppies, since a + good portable way to transport files and directories between unixes + (and even other operating systems) is given by the tar program ("man + tar" or preferably "info tar"). Note also that this option has + nothing whatsoever to do with the option "System V IPC". Read about + the System V file system in + <file:Documentation/filesystems/sysv-fs.txt>. + Saying Y here will enlarge your kernel by about 27 KB. + + To compile this as a module, choose M here: the module will be called + sysv. + + If you haven't heard about all of this before, it's safe to say N. + + + +config UFS_FS + tristate "UFS file system support (read only)" + help + BSD and derivate versions of Unix (such as SunOS, FreeBSD, NetBSD, + OpenBSD and NeXTstep) use a file system called UFS. Some System V + Unixes can create and mount hard disk partitions and diskettes using + this file system as well. Saying Y here will allow you to read from + these partitions; if you also want to write to them, say Y to the + experimental "UFS file system write support", below. Please read the + file <file:Documentation/filesystems/ufs.txt> for more information. + + The recently released UFS2 variant (used in FreeBSD 5.x) is + READ-ONLY supported. + + If you only intend to mount files from some other Unix over the + network using NFS, you don't need the UFS file system support (but + you need NFS file system support obviously). + + Note that this option is generally not needed for floppies, since a + good portable way to transport files and directories between unixes + (and even other operating systems) is given by the tar program ("man + tar" or preferably "info tar"). + + When accessing NeXTstep files, you may need to convert them from the + NeXT character set to the Latin1 character set; use the program + recode ("info recode") for this purpose. + + To compile the UFS file system support as a module, choose M here: the + module will be called ufs. + + If you haven't heard about all of this before, it's safe to say N. + +config UFS_FS_WRITE + bool "UFS file system write support (DANGEROUS)" + depends on UFS_FS && EXPERIMENTAL + help + Say Y here if you want to try writing to UFS partitions. This is + experimental, so you should back up your UFS partitions beforehand. + +endmenu + +menu "Network File Systems" + depends on NET + +config NFS_FS + tristate "NFS file system support" + depends on INET + select LOCKD + select SUNRPC + help + If you are connected to some other (usually local) Unix computer + (using SLIP, PLIP, PPP or Ethernet) and want to mount files residing + on that computer (the NFS server) using the Network File Sharing + protocol, say Y. "Mounting files" means that the client can access + the files with usual UNIX commands as if they were sitting on the + client's hard disk. For this to work, the server must run the + programs nfsd and mountd (but does not need to have NFS file system + support enabled in its kernel). NFS is explained in the Network + Administrator's Guide, available from + <http://www.tldp.org/docs.html#guide>, on its man page: "man + nfs", and in the NFS-HOWTO. + + A superior but less widely used alternative to NFS is provided by + the Coda file system; see "Coda file system support" below. + + If you say Y here, you should have said Y to TCP/IP networking also. + This option would enlarge your kernel by about 27 KB. + + To compile this file system support as a module, choose M here: the + module will be called nfs. + + If you are configuring a diskless machine which will mount its root + file system over NFS at boot time, say Y here and to "Kernel + level IP autoconfiguration" above and to "Root file system on NFS" + below. You cannot compile this driver as a module in this case. + There are two packages designed for booting diskless machines over + the net: netboot, available from + <http://ftp1.sourceforge.net/netboot/>, and Etherboot, + available from <http://ftp1.sourceforge.net/etherboot/>. + + If you don't know what all this is about, say N. + +config NFS_V3 + bool "Provide NFSv3 client support" + depends on NFS_FS + help + Say Y here if you want your NFS client to be able to speak version + 3 of the NFS protocol. + + If unsure, say Y. + +config NFS_V4 + bool "Provide NFSv4 client support (EXPERIMENTAL)" + depends on NFS_FS && EXPERIMENTAL + select RPCSEC_GSS_KRB5 + help + Say Y here if you want your NFS client to be able to speak the newer + version 4 of the NFS protocol. + + Note: Requires auxiliary userspace daemons which may be found on + http://www.citi.umich.edu/projects/nfsv4/ + + If unsure, say N. + +config NFS_DIRECTIO + bool "Allow direct I/O on NFS files (EXPERIMENTAL)" + depends on NFS_FS && EXPERIMENTAL + help + This option enables applications to perform uncached I/O on files + in NFS file systems using the O_DIRECT open() flag. When O_DIRECT + is set for a file, its data is not cached in the system's page + cache. Data is moved to and from user-level application buffers + directly. Unlike local disk-based file systems, NFS O_DIRECT has + no alignment restrictions. + + Unless your program is designed to use O_DIRECT properly, you are + much better off allowing the NFS client to manage data caching for + you. Misusing O_DIRECT can cause poor server performance or network + storms. This kernel build option defaults OFF to avoid exposing + system administrators unwittingly to a potentially hazardous + feature. + + For more details on NFS O_DIRECT, see fs/nfs/direct.c. + + If unsure, say N. This reduces the size of the NFS client, and + causes open() to return EINVAL if a file residing in NFS is + opened with the O_DIRECT flag. + +config NFSD + tristate "NFS server support" + depends on INET + select LOCKD + select SUNRPC + select EXPORTFS + help + If you want your Linux box to act as an NFS *server*, so that other + computers on your local network which support NFS can access certain + directories on your box transparently, you have two options: you can + use the self-contained user space program nfsd, in which case you + should say N here, or you can say Y and use the kernel based NFS + server. The advantage of the kernel based solution is that it is + faster. + + In either case, you will need support software; the respective + locations are given in the file <file:Documentation/Changes> in the + NFS section. + + If you say Y here, you will get support for version 2 of the NFS + protocol (NFSv2). If you also want NFSv3, say Y to the next question + as well. + + Please read the NFS-HOWTO, available from + <http://www.tldp.org/docs.html#howto>. + + To compile the NFS server support as a module, choose M here: the + module will be called nfsd. If unsure, say N. + +config NFSD_V3 + bool "Provide NFSv3 server support" + depends on NFSD + help + If you would like to include the NFSv3 server as well as the NFSv2 + server, say Y here. If unsure, say Y. + +config NFSD_V4 + bool "Provide NFSv4 server support (EXPERIMENTAL)" + depends on NFSD_V3 && EXPERIMENTAL + select NFSD_TCP + help + If you would like to include the NFSv4 server as well as the NFSv2 + and NFSv3 servers, say Y here. This feature is experimental, and + should only be used if you are interested in helping to test NFSv4. + If unsure, say N. + +config NFSD_TCP + bool "Provide NFS server over TCP support" + depends on NFSD + default y + help + If you want your NFS server to support TCP connections, say Y here. + TCP connections usually perform better than the default UDP when + the network is lossy or congested. If unsure, say Y. + +config ROOT_NFS + bool "Root file system on NFS" + depends on NFS_FS=y && IP_PNP + help + If you want your Linux box to mount its whole root file system (the + one containing the directory /) from some other computer over the + net via NFS (presumably because your box doesn't have a hard disk), + say Y. Read <file:Documentation/nfsroot.txt> for details. It is + likely that in this case, you also want to say Y to "Kernel level IP + autoconfiguration" so that your box can discover its network address + at boot time. + + Most people say N here. + +config LOCKD + tristate + +config LOCKD_V4 + bool + depends on NFSD_V3 || NFS_V3 + default y + +config EXPORTFS + tristate + +config SUNRPC + tristate + +config SUNRPC_GSS + tristate + +config RPCSEC_GSS_KRB5 + tristate "Secure RPC: Kerberos V mechanism (EXPERIMENTAL)" + depends on SUNRPC && EXPERIMENTAL + select SUNRPC_GSS + select CRYPTO + select CRYPTO_MD5 + select CRYPTO_DES + help + Provides for secure RPC calls by means of a gss-api + mechanism based on Kerberos V5. This is required for + NFSv4. + + Note: Requires an auxiliary userspace daemon which may be found on + http://www.citi.umich.edu/projects/nfsv4/ + + If unsure, say N. + +config RPCSEC_GSS_SPKM3 + tristate "Secure RPC: SPKM3 mechanism (EXPERIMENTAL)" + depends on SUNRPC && EXPERIMENTAL + select SUNRPC_GSS + select CRYPTO + select CRYPTO_MD5 + select CRYPTO_DES + help + Provides for secure RPC calls by means of a gss-api + mechanism based on the SPKM3 public-key mechanism. + + Note: Requires an auxiliary userspace daemon which may be found on + http://www.citi.umich.edu/projects/nfsv4/ + + If unsure, say N. + +config SMB_FS + tristate "SMB file system support (to mount Windows shares etc.)" + depends on INET + select NLS + help + SMB (Server Message Block) is the protocol Windows for Workgroups + (WfW), Windows 95/98, Windows NT and OS/2 Lan Manager use to share + files and printers over local networks. Saying Y here allows you to + mount their file systems (often called "shares" in this context) and + access them just like any other Unix directory. Currently, this + works only if the Windows machines use TCP/IP as the underlying + transport protocol, and not NetBEUI. For details, read + <file:Documentation/filesystems/smbfs.txt> and the SMB-HOWTO, + available from <http://www.tldp.org/docs.html#howto>. + + Note: if you just want your box to act as an SMB *server* and make + files and printing services available to Windows clients (which need + to have a TCP/IP stack), you don't need to say Y here; you can use + the program SAMBA (available from <ftp://ftp.samba.org/pub/samba/>) + for that. + + General information about how to connect Linux, Windows machines and + Macs is on the WWW at <http://www.eats.com/linux_mac_win.html>. + + To compile the SMB support as a module, choose M here: the module will + be called smbfs. Most people say N, however. + +config SMB_NLS_DEFAULT + bool "Use a default NLS" + depends on SMB_FS + help + Enabling this will make smbfs use nls translations by default. You + need to specify the local charset (CONFIG_NLS_DEFAULT) in the nls + settings and you need to give the default nls for the SMB server as + CONFIG_SMB_NLS_REMOTE. + + The nls settings can be changed at mount time, if your smbmount + supports that, using the codepage and iocharset parameters. + + smbmount from samba 2.2.0 or later supports this. + +config SMB_NLS_REMOTE + string "Default Remote NLS Option" + depends on SMB_NLS_DEFAULT + default "cp437" + help + This setting allows you to specify a default value for which + codepage the server uses. If this field is left blank no + translations will be done by default. The local codepage/charset + default to CONFIG_NLS_DEFAULT. + + The nls settings can be changed at mount time, if your smbmount + supports that, using the codepage and iocharset parameters. + + smbmount from samba 2.2.0 or later supports this. + +config CIFS + tristate "CIFS support (advanced network filesystem for Samba, Window and other CIFS compliant servers)" + depends on INET + select NLS + help + This is the client VFS module for the Common Internet File System + (CIFS) protocol which is the successor to the Server Message Block + (SMB) protocol, the native file sharing mechanism for most early + PC operating systems. The CIFS protocol is fully supported by + file servers such as Windows 2000 (including Windows 2003, NT 4 + and Windows XP) as well by Samba (which provides excellent CIFS + server support for Linux and many other operating systems). Currently + you must use the smbfs client filesystem to access older SMB servers + such as Windows 9x and OS/2. + + The intent of the cifs module is to provide an advanced + network file system client for mounting to CIFS compliant servers, + including support for dfs (hierarchical name space), secure per-user + session establishment, safe distributed caching (oplock), optional + packet signing, Unicode and other internationalization improvements, + and optional Winbind (nsswitch) integration. You do not need to enable + cifs if running only a (Samba) server. It is possible to enable both + smbfs and cifs (e.g. if you are using CIFS for accessing Windows 2003 + and Samba 3 servers, and smbfs for accessing old servers). If you need + to mount to Samba or Windows 2003 servers from this machine, say Y. + +config CIFS_STATS + bool "CIFS statistics" + depends on CIFS + help + Enabling this option will cause statistics for each server share + mounted by the cifs client to be displayed in /proc/fs/cifs/Stats + +config CIFS_XATTR + bool "CIFS extended attributes (EXPERIMENTAL)" + depends on CIFS + help + Extended attributes are name:value pairs associated with inodes by + the kernel or by users (see the attr(5) manual page, or visit + <http://acl.bestbits.at/> for details). CIFS maps the name of + extended attributes beginning with the user namespace prefix + to SMB/CIFS EAs. EAs are stored on Windows servers without the + user namespace prefix, but their names are seen by Linux cifs clients + prefaced by the user namespace prefix. The system namespace + (used by some filesystems to store ACLs) is not supported at + this time. + + If unsure, say N. + +config CIFS_POSIX + bool "CIFS POSIX Extensions (EXPERIMENTAL)" + depends on CIFS_XATTR + help + Enabling this option will cause the cifs client to attempt to + negotiate a newer dialect with servers, such as Samba 3.0.5 + or later, that optionally can handle more POSIX like (rather + than Windows like) file behavior. It also enables + support for POSIX ACLs (getfacl and setfacl) to servers + (such as Samba 3.10 and later) which can negotiate + CIFS POSIX ACL support. If unsure, say N. + +config CIFS_EXPERIMENTAL + bool "CIFS Experimental Features (EXPERIMENTAL)" + depends on CIFS + help + Enables cifs features under testing. These features + are highly experimental. If unsure, say N. + +config NCP_FS + tristate "NCP file system support (to mount NetWare volumes)" + depends on IPX!=n || INET + help + NCP (NetWare Core Protocol) is a protocol that runs over IPX and is + used by Novell NetWare clients to talk to file servers. It is to + IPX what NFS is to TCP/IP, if that helps. Saying Y here allows you + to mount NetWare file server volumes and to access them just like + any other Unix directory. For details, please read the file + <file:Documentation/filesystems/ncpfs.txt> in the kernel source and + the IPX-HOWTO from <http://www.tldp.org/docs.html#howto>. + + You do not have to say Y here if you want your Linux box to act as a + file *server* for Novell NetWare clients. + + General information about how to connect Linux, Windows machines and + Macs is on the WWW at <http://www.eats.com/linux_mac_win.html>. + + To compile this as a module, choose M here: the module will be called + ncpfs. Say N unless you are connected to a Novell network. + +source "fs/ncpfs/Kconfig" + +config CODA_FS + tristate "Coda file system support (advanced network fs)" + depends on INET + help + Coda is an advanced network file system, similar to NFS in that it + enables you to mount file systems of a remote server and access them + with regular Unix commands as if they were sitting on your hard + disk. Coda has several advantages over NFS: support for + disconnected operation (e.g. for laptops), read/write server + replication, security model for authentication and encryption, + persistent client caches and write back caching. + + If you say Y here, your Linux box will be able to act as a Coda + *client*. You will need user level code as well, both for the + client and server. Servers are currently user level, i.e. they need + no kernel support. Please read + <file:Documentation/filesystems/coda.txt> and check out the Coda + home page <http://www.coda.cs.cmu.edu/>. + + To compile the coda client support as a module, choose M here: the + module will be called coda. + +config CODA_FS_OLD_API + bool "Use 96-bit Coda file identifiers" + depends on CODA_FS + help + A new kernel-userspace API had to be introduced for Coda v6.0 + to support larger 128-bit file identifiers as needed by the + new realms implementation. + + However this new API is not backward compatible with older + clients. If you really need to run the old Coda userspace + cache manager then say Y. + + For most cases you probably want to say N. + +config AFS_FS +# for fs/nls/Config.in + tristate "Andrew File System support (AFS) (Experimental)" + depends on INET && EXPERIMENTAL + select RXRPC + help + If you say Y here, you will get an experimental Andrew File System + driver. It currently only supports unsecured read-only AFS access. + + See <file:Documentation/filesystems/afs.txt> for more intormation. + + If unsure, say N. + +config RXRPC + tristate + +endmenu + +menu "Partition Types" + +source "fs/partitions/Kconfig" + +endmenu + +source "fs/nls/Kconfig" + +endmenu + diff --git a/fs/Kconfig.binfmt b/fs/Kconfig.binfmt new file mode 100644 index 00000000000..434c19d076a --- /dev/null +++ b/fs/Kconfig.binfmt @@ -0,0 +1,134 @@ +config BINFMT_ELF + bool "Kernel support for ELF binaries" + depends on MMU + default y + ---help--- + ELF (Executable and Linkable Format) is a format for libraries and + executables used across different architectures and operating + systems. Saying Y here will enable your kernel to run ELF binaries + and enlarge it by about 13 KB. ELF support under Linux has now all + but replaced the traditional Linux a.out formats (QMAGIC and ZMAGIC) + because it is portable (this does *not* mean that you will be able + to run executables from different architectures or operating systems + however) and makes building run-time libraries very easy. Many new + executables are distributed solely in ELF format. You definitely + want to say Y here. + + Information about ELF is contained in the ELF HOWTO available from + <http://www.tldp.org/docs.html#howto>. + + If you find that after upgrading from Linux kernel 1.2 and saying Y + here, you still can't run any ELF binaries (they just crash), then + you'll have to install the newest ELF runtime libraries, including + ld.so (check the file <file:Documentation/Changes> for location and + latest version). + +config BINFMT_ELF_FDPIC + bool "Kernel support for FDPIC ELF binaries" + default y + depends on FRV + help + ELF FDPIC binaries are based on ELF, but allow the individual load + segments of a binary to be located in memory independently of each + other. This makes this format ideal for use in environments where no + MMU is available as it still permits text segments to be shared, + even if data segments are not. + + It is also possible to run FDPIC ELF binaries on MMU linux also. + +config BINFMT_FLAT + tristate "Kernel support for flat binaries" + depends on !MMU || SUPERH + help + Support uClinux FLAT format binaries. + +config BINFMT_ZFLAT + bool "Enable ZFLAT support" + depends on BINFMT_FLAT + select ZLIB_INFLATE + help + Support FLAT format compressed binaries + +config BINFMT_SHARED_FLAT + bool "Enable shared FLAT support" + depends on BINFMT_FLAT + help + Support FLAT shared libraries + +config BINFMT_AOUT + tristate "Kernel support for a.out and ECOFF binaries" + depends on (X86 && !X86_64) || ALPHA || ARM || M68K || SPARC32 + ---help--- + A.out (Assembler.OUTput) is a set of formats for libraries and + executables used in the earliest versions of UNIX. Linux used + the a.out formats QMAGIC and ZMAGIC until they were replaced + with the ELF format. + + The conversion to ELF started in 1995. This option is primarily + provided for historical interest and for the benefit of those + who need to run binaries from that era. + + Most people should answer N here. If you think you may have + occasional use for this format, enable module support above + and answer M here to compile this support as a module called + binfmt_aout. + + If any crucial components of your system (such as /sbin/init + or /lib/ld.so) are still in a.out format, you will have to + say Y here. + +config OSF4_COMPAT + bool "OSF/1 v4 readv/writev compatibility" + depends on ALPHA && BINFMT_AOUT + help + Say Y if you are using OSF/1 binaries (like Netscape and Acrobat) + with v4 shared libraries freely available from Compaq. If you're + going to use shared libraries from Tru64 version 5.0 or later, say N. + +config BINFMT_EM86 + tristate "Kernel support for Linux/Intel ELF binaries" + depends on ALPHA + ---help--- + Say Y here if you want to be able to execute Linux/Intel ELF + binaries just like native Alpha binaries on your Alpha machine. For + this to work, you need to have the emulator /usr/bin/em86 in place. + + You can get the same functionality by saying N here and saying Y to + "Kernel support for MISC binaries". + + You may answer M to compile the emulation support as a module and + later load the module when you want to use a Linux/Intel binary. The + module will be called binfmt_em86. If unsure, say Y. + +config BINFMT_SOM + tristate "Kernel support for SOM binaries" + depends on PARISC && HPUX + help + SOM is a binary executable format inherited from HP/UX. Say + Y here to be able to load and execute SOM binaries directly. + +config BINFMT_MISC + tristate "Kernel support for MISC binaries" + ---help--- + If you say Y here, it will be possible to plug wrapper-driven binary + formats into the kernel. You will like this especially when you use + programs that need an interpreter to run like Java, Python, .NET or + Emacs-Lisp. It's also useful if you often run DOS executables under + the Linux DOS emulator DOSEMU (read the DOSEMU-HOWTO, available from + <http://www.tldp.org/docs.html#howto>). Once you have + registered such a binary class with the kernel, you can start one of + those programs simply by typing in its name at a shell prompt; Linux + will automatically feed it to the correct interpreter. + + You can do other nice things, too. Read the file + <file:Documentation/binfmt_misc.txt> to learn how to use this + feature, <file:Documentation/java.txt> for information about how + to include Java support. and <file:Documentation/mono.txt> for + information about how to include Mono-based .NET support. + + To use binfmt_misc, you will need to mount it: + mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc + + You may say M here for module support and later load the module when + you have use for it; the module is called binfmt_misc. If you + don't know what to answer at this point, say Y. diff --git a/fs/Makefile b/fs/Makefile new file mode 100644 index 00000000000..443f2bc56cc --- /dev/null +++ b/fs/Makefile @@ -0,0 +1,97 @@ +# +# Makefile for the Linux filesystems. +# +# 14 Sep 2000, Christoph Hellwig <hch@infradead.org> +# Rewritten to use lists instead of if-statements. +# + +obj-y := open.o read_write.o file_table.o buffer.o bio.o super.o \ + block_dev.o char_dev.o stat.o exec.o pipe.o namei.o fcntl.o \ + ioctl.o readdir.o select.o fifo.o locks.o dcache.o inode.o \ + attr.o bad_inode.o file.o filesystems.o namespace.o aio.o \ + seq_file.o xattr.o libfs.o fs-writeback.o mpage.o direct-io.o \ + +obj-$(CONFIG_EPOLL) += eventpoll.o +obj-$(CONFIG_COMPAT) += compat.o + +nfsd-$(CONFIG_NFSD) := nfsctl.o +obj-y += $(nfsd-y) $(nfsd-m) + +obj-$(CONFIG_BINFMT_AOUT) += binfmt_aout.o +obj-$(CONFIG_BINFMT_EM86) += binfmt_em86.o +obj-$(CONFIG_BINFMT_MISC) += binfmt_misc.o + +# binfmt_script is always there +obj-y += binfmt_script.o + +obj-$(CONFIG_BINFMT_ELF) += binfmt_elf.o +obj-$(CONFIG_BINFMT_ELF_FDPIC) += binfmt_elf_fdpic.o +obj-$(CONFIG_BINFMT_SOM) += binfmt_som.o +obj-$(CONFIG_BINFMT_FLAT) += binfmt_flat.o + +obj-$(CONFIG_FS_MBCACHE) += mbcache.o +obj-$(CONFIG_FS_POSIX_ACL) += posix_acl.o xattr_acl.o + +obj-$(CONFIG_QUOTA) += dquot.o +obj-$(CONFIG_QFMT_V1) += quota_v1.o +obj-$(CONFIG_QFMT_V2) += quota_v2.o +obj-$(CONFIG_QUOTACTL) += quota.o + +obj-$(CONFIG_DNOTIFY) += dnotify.o + +obj-$(CONFIG_PROC_FS) += proc/ +obj-y += partitions/ +obj-$(CONFIG_SYSFS) += sysfs/ +obj-y += devpts/ + +obj-$(CONFIG_PROFILING) += dcookies.o + +# Do not add any filesystems before this line +obj-$(CONFIG_REISERFS_FS) += reiserfs/ +obj-$(CONFIG_EXT3_FS) += ext3/ # Before ext2 so root fs can be ext3 +obj-$(CONFIG_JBD) += jbd/ +obj-$(CONFIG_EXT2_FS) += ext2/ +obj-$(CONFIG_CRAMFS) += cramfs/ +obj-$(CONFIG_RAMFS) += ramfs/ +obj-$(CONFIG_HUGETLBFS) += hugetlbfs/ +obj-$(CONFIG_CODA_FS) += coda/ +obj-$(CONFIG_MINIX_FS) += minix/ +obj-$(CONFIG_FAT_FS) += fat/ +obj-$(CONFIG_MSDOS_FS) += msdos/ +obj-$(CONFIG_VFAT_FS) += vfat/ +obj-$(CONFIG_BFS_FS) += bfs/ +obj-$(CONFIG_ISO9660_FS) += isofs/ +obj-$(CONFIG_DEVFS_FS) += devfs/ +obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+ +obj-$(CONFIG_HFS_FS) += hfs/ +obj-$(CONFIG_VXFS_FS) += freevxfs/ +obj-$(CONFIG_NFS_FS) += nfs/ +obj-$(CONFIG_EXPORTFS) += exportfs/ +obj-$(CONFIG_NFSD) += nfsd/ +obj-$(CONFIG_LOCKD) += lockd/ +obj-$(CONFIG_NLS) += nls/ +obj-$(CONFIG_SYSV_FS) += sysv/ +obj-$(CONFIG_SMB_FS) += smbfs/ +obj-$(CONFIG_CIFS) += cifs/ +obj-$(CONFIG_NCP_FS) += ncpfs/ +obj-$(CONFIG_HPFS_FS) += hpfs/ +obj-$(CONFIG_NTFS_FS) += ntfs/ +obj-$(CONFIG_UFS_FS) += ufs/ +obj-$(CONFIG_EFS_FS) += efs/ +obj-$(CONFIG_JFFS_FS) += jffs/ +obj-$(CONFIG_JFFS2_FS) += jffs2/ +obj-$(CONFIG_AFFS_FS) += affs/ +obj-$(CONFIG_ROMFS_FS) += romfs/ +obj-$(CONFIG_QNX4FS_FS) += qnx4/ +obj-$(CONFIG_AUTOFS_FS) += autofs/ +obj-$(CONFIG_AUTOFS4_FS) += autofs4/ +obj-$(CONFIG_ADFS_FS) += adfs/ +obj-$(CONFIG_UDF_FS) += udf/ +obj-$(CONFIG_SUN_OPENPROMFS) += openpromfs/ +obj-$(CONFIG_JFS_FS) += jfs/ +obj-$(CONFIG_XFS_FS) += xfs/ +obj-$(CONFIG_AFS_FS) += afs/ +obj-$(CONFIG_BEFS_FS) += befs/ +obj-$(CONFIG_HOSTFS) += hostfs/ +obj-$(CONFIG_HPPFS) += hppfs/ +obj-$(CONFIG_DEBUG_FS) += debugfs/ diff --git a/fs/adfs/Makefile b/fs/adfs/Makefile new file mode 100644 index 00000000000..9b2d71a9a35 --- /dev/null +++ b/fs/adfs/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the linux adfs filesystem routines. +# + +obj-$(CONFIG_ADFS_FS) += adfs.o + +adfs-objs := dir.o dir_f.o dir_fplus.o file.o inode.o map.o super.o diff --git a/fs/adfs/adfs.h b/fs/adfs/adfs.h new file mode 100644 index 00000000000..63f5df9afb7 --- /dev/null +++ b/fs/adfs/adfs.h @@ -0,0 +1,127 @@ +/* Internal data structures for ADFS */ + +#define ADFS_FREE_FRAG 0 +#define ADFS_BAD_FRAG 1 +#define ADFS_ROOT_FRAG 2 + +#define ADFS_NDA_OWNER_READ (1 << 0) +#define ADFS_NDA_OWNER_WRITE (1 << 1) +#define ADFS_NDA_LOCKED (1 << 2) +#define ADFS_NDA_DIRECTORY (1 << 3) +#define ADFS_NDA_EXECUTE (1 << 4) +#define ADFS_NDA_PUBLIC_READ (1 << 5) +#define ADFS_NDA_PUBLIC_WRITE (1 << 6) + +#include <linux/version.h> +#include "dir_f.h" + +struct buffer_head; + +/* + * Directory handling + */ +struct adfs_dir { + struct super_block *sb; + + int nr_buffers; + struct buffer_head *bh[4]; + unsigned int pos; + unsigned int parent_id; + + struct adfs_dirheader dirhead; + union adfs_dirtail dirtail; +}; + +/* + * This is the overall maximum name length + */ +#define ADFS_MAX_NAME_LEN 256 +struct object_info { + __u32 parent_id; /* parent object id */ + __u32 file_id; /* object id */ + __u32 loadaddr; /* load address */ + __u32 execaddr; /* execution address */ + __u32 size; /* size */ + __u8 attr; /* RISC OS attributes */ + unsigned char name_len; /* name length */ + char name[ADFS_MAX_NAME_LEN];/* file name */ +}; + +struct adfs_dir_ops { + int (*read)(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir); + int (*setpos)(struct adfs_dir *dir, unsigned int fpos); + int (*getnext)(struct adfs_dir *dir, struct object_info *obj); + int (*update)(struct adfs_dir *dir, struct object_info *obj); + int (*create)(struct adfs_dir *dir, struct object_info *obj); + int (*remove)(struct adfs_dir *dir, struct object_info *obj); + void (*free)(struct adfs_dir *dir); +}; + +struct adfs_discmap { + struct buffer_head *dm_bh; + __u32 dm_startblk; + unsigned int dm_startbit; + unsigned int dm_endbit; +}; + +/* Inode stuff */ +struct inode *adfs_iget(struct super_block *sb, struct object_info *obj); +int adfs_write_inode(struct inode *inode,int unused); +int adfs_notify_change(struct dentry *dentry, struct iattr *attr); + +/* map.c */ +extern int adfs_map_lookup(struct super_block *sb, unsigned int frag_id, unsigned int offset); +extern unsigned int adfs_map_free(struct super_block *sb); + +/* Misc */ +void __adfs_error(struct super_block *sb, const char *function, + const char *fmt, ...); +#define adfs_error(sb, fmt...) __adfs_error(sb, __FUNCTION__, fmt) + +/* super.c */ + +/* + * Inodes and file operations + */ + +/* dir_*.c */ +extern struct inode_operations adfs_dir_inode_operations; +extern struct file_operations adfs_dir_operations; +extern struct dentry_operations adfs_dentry_operations; +extern struct adfs_dir_ops adfs_f_dir_ops; +extern struct adfs_dir_ops adfs_fplus_dir_ops; + +extern int adfs_dir_update(struct super_block *sb, struct object_info *obj); + +/* file.c */ +extern struct inode_operations adfs_file_inode_operations; +extern struct file_operations adfs_file_operations; + +extern inline __u32 signed_asl(__u32 val, signed int shift) +{ + if (shift >= 0) + val <<= shift; + else + val >>= -shift; + return val; +} + +/* + * Calculate the address of a block in an object given the block offset + * and the object identity. + * + * The root directory ID should always be looked up in the map [3.4] + */ +extern inline int +__adfs_block_map(struct super_block *sb, unsigned int object_id, + unsigned int block) +{ + if (object_id & 255) { + unsigned int off; + + off = (object_id & 255) - 1; + block += off << ADFS_SB(sb)->s_log2sharesize; + } + + return adfs_map_lookup(sb, object_id >> 8, block); +} diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c new file mode 100644 index 00000000000..0b4c3a02807 --- /dev/null +++ b/fs/adfs/dir.c @@ -0,0 +1,302 @@ +/* + * linux/fs/adfs/dir.c + * + * Copyright (C) 1999-2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Common directory handling for ADFS + */ +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/adfs_fs.h> +#include <linux/time.h> +#include <linux/stat.h> +#include <linux/spinlock.h> +#include <linux/smp_lock.h> +#include <linux/buffer_head.h> /* for file_fsync() */ + +#include "adfs.h" + +/* + * For future. This should probably be per-directory. + */ +static DEFINE_RWLOCK(adfs_dir_lock); + +static int +adfs_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct inode *inode = filp->f_dentry->d_inode; + struct super_block *sb = inode->i_sb; + struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; + struct object_info obj; + struct adfs_dir dir; + int ret = 0; + + lock_kernel(); + + if (filp->f_pos >> 32) + goto out; + + ret = ops->read(sb, inode->i_ino, inode->i_size, &dir); + if (ret) + goto out; + + switch ((unsigned long)filp->f_pos) { + case 0: + if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR) < 0) + goto free_out; + filp->f_pos += 1; + + case 1: + if (filldir(dirent, "..", 2, 1, dir.parent_id, DT_DIR) < 0) + goto free_out; + filp->f_pos += 1; + + default: + break; + } + + read_lock(&adfs_dir_lock); + + ret = ops->setpos(&dir, filp->f_pos - 2); + if (ret) + goto unlock_out; + while (ops->getnext(&dir, &obj) == 0) { + if (filldir(dirent, obj.name, obj.name_len, + filp->f_pos, obj.file_id, DT_UNKNOWN) < 0) + goto unlock_out; + filp->f_pos += 1; + } + +unlock_out: + read_unlock(&adfs_dir_lock); + +free_out: + ops->free(&dir); + +out: + unlock_kernel(); + return ret; +} + +int +adfs_dir_update(struct super_block *sb, struct object_info *obj) +{ + int ret = -EINVAL; +#ifdef CONFIG_ADFS_FS_RW + struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; + struct adfs_dir dir; + + printk(KERN_INFO "adfs_dir_update: object %06X in dir %06X\n", + obj->file_id, obj->parent_id); + + if (!ops->update) { + ret = -EINVAL; + goto out; + } + + ret = ops->read(sb, obj->parent_id, 0, &dir); + if (ret) + goto out; + + write_lock(&adfs_dir_lock); + ret = ops->update(&dir, obj); + write_unlock(&adfs_dir_lock); + + ops->free(&dir); +out: +#endif + return ret; +} + +static int +adfs_match(struct qstr *name, struct object_info *obj) +{ + int i; + + if (name->len != obj->name_len) + return 0; + + for (i = 0; i < name->len; i++) { + char c1, c2; + + c1 = name->name[i]; + c2 = obj->name[i]; + + if (c1 >= 'A' && c1 <= 'Z') + c1 += 'a' - 'A'; + if (c2 >= 'A' && c2 <= 'Z') + c2 += 'a' - 'A'; + + if (c1 != c2) + return 0; + } + return 1; +} + +static int +adfs_dir_lookup_byname(struct inode *inode, struct qstr *name, struct object_info *obj) +{ + struct super_block *sb = inode->i_sb; + struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; + struct adfs_dir dir; + int ret; + + ret = ops->read(sb, inode->i_ino, inode->i_size, &dir); + if (ret) + goto out; + + if (ADFS_I(inode)->parent_id != dir.parent_id) { + adfs_error(sb, "parent directory changed under me! (%lx but got %lx)\n", + ADFS_I(inode)->parent_id, dir.parent_id); + ret = -EIO; + goto free_out; + } + + obj->parent_id = inode->i_ino; + + /* + * '.' is handled by reserved_lookup() in fs/namei.c + */ + if (name->len == 2 && name->name[0] == '.' && name->name[1] == '.') { + /* + * Currently unable to fill in the rest of 'obj', + * but this is better than nothing. We need to + * ascend one level to find it's parent. + */ + obj->name_len = 0; + obj->file_id = obj->parent_id; + goto free_out; + } + + read_lock(&adfs_dir_lock); + + ret = ops->setpos(&dir, 0); + if (ret) + goto unlock_out; + + ret = -ENOENT; + while (ops->getnext(&dir, obj) == 0) { + if (adfs_match(name, obj)) { + ret = 0; + break; + } + } + +unlock_out: + read_unlock(&adfs_dir_lock); + +free_out: + ops->free(&dir); +out: + return ret; +} + +struct file_operations adfs_dir_operations = { + .read = generic_read_dir, + .readdir = adfs_readdir, + .fsync = file_fsync, +}; + +static int +adfs_hash(struct dentry *parent, struct qstr *qstr) +{ + const unsigned int name_len = ADFS_SB(parent->d_sb)->s_namelen; + const unsigned char *name; + unsigned long hash; + int i; + + if (qstr->len < name_len) + return 0; + + /* + * Truncate the name in place, avoids + * having to define a compare function. + */ + qstr->len = i = name_len; + name = qstr->name; + hash = init_name_hash(); + while (i--) { + char c; + + c = *name++; + if (c >= 'A' && c <= 'Z') + c += 'a' - 'A'; + + hash = partial_name_hash(c, hash); + } + qstr->hash = end_name_hash(hash); + + return 0; +} + +/* + * Compare two names, taking note of the name length + * requirements of the underlying filesystem. + */ +static int +adfs_compare(struct dentry *parent, struct qstr *entry, struct qstr *name) +{ + int i; + + if (entry->len != name->len) + return 1; + + for (i = 0; i < name->len; i++) { + char a, b; + + a = entry->name[i]; + b = name->name[i]; + + if (a >= 'A' && a <= 'Z') + a += 'a' - 'A'; + if (b >= 'A' && b <= 'Z') + b += 'a' - 'A'; + + if (a != b) + return 1; + } + return 0; +} + +struct dentry_operations adfs_dentry_operations = { + .d_hash = adfs_hash, + .d_compare = adfs_compare, +}; + +static struct dentry * +adfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) +{ + struct inode *inode = NULL; + struct object_info obj; + int error; + + dentry->d_op = &adfs_dentry_operations; + lock_kernel(); + error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj); + if (error == 0) { + error = -EACCES; + /* + * This only returns NULL if get_empty_inode + * fails. + */ + inode = adfs_iget(dir->i_sb, &obj); + if (inode) + error = 0; + } + unlock_kernel(); + d_add(dentry, inode); + return ERR_PTR(error); +} + +/* + * directories can handle most operations... + */ +struct inode_operations adfs_dir_inode_operations = { + .lookup = adfs_lookup, + .setattr = adfs_notify_change, +}; diff --git a/fs/adfs/dir_f.c b/fs/adfs/dir_f.c new file mode 100644 index 00000000000..bbfc8625927 --- /dev/null +++ b/fs/adfs/dir_f.c @@ -0,0 +1,460 @@ +/* + * linux/fs/adfs/dir_f.c + * + * Copyright (C) 1997-1999 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * E and F format directory handling + */ +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/adfs_fs.h> +#include <linux/time.h> +#include <linux/stat.h> +#include <linux/spinlock.h> +#include <linux/buffer_head.h> +#include <linux/string.h> + +#include "adfs.h" +#include "dir_f.h" + +static void adfs_f_free(struct adfs_dir *dir); + +/* + * Read an (unaligned) value of length 1..4 bytes + */ +static inline unsigned int adfs_readval(unsigned char *p, int len) +{ + unsigned int val = 0; + + switch (len) { + case 4: val |= p[3] << 24; + case 3: val |= p[2] << 16; + case 2: val |= p[1] << 8; + default: val |= p[0]; + } + return val; +} + +static inline void adfs_writeval(unsigned char *p, int len, unsigned int val) +{ + switch (len) { + case 4: p[3] = val >> 24; + case 3: p[2] = val >> 16; + case 2: p[1] = val >> 8; + default: p[0] = val; + } +} + +static inline int adfs_readname(char *buf, char *ptr, int maxlen) +{ + char *old_buf = buf; + + while (*ptr >= ' ' && maxlen--) { + if (*ptr == '/') + *buf++ = '.'; + else + *buf++ = *ptr; + ptr++; + } + *buf = '\0'; + + return buf - old_buf; +} + +#define ror13(v) ((v >> 13) | (v << 19)) + +#define dir_u8(idx) \ + ({ int _buf = idx >> blocksize_bits; \ + int _off = idx - (_buf << blocksize_bits);\ + *(u8 *)(bh[_buf]->b_data + _off); \ + }) + +#define dir_u32(idx) \ + ({ int _buf = idx >> blocksize_bits; \ + int _off = idx - (_buf << blocksize_bits);\ + *(__le32 *)(bh[_buf]->b_data + _off); \ + }) + +#define bufoff(_bh,_idx) \ + ({ int _buf = _idx >> blocksize_bits; \ + int _off = _idx - (_buf << blocksize_bits);\ + (u8 *)(_bh[_buf]->b_data + _off); \ + }) + +/* + * There are some algorithms that are nice in + * assembler, but a bitch in C... This is one + * of them. + */ +static u8 +adfs_dir_checkbyte(const struct adfs_dir *dir) +{ + struct buffer_head * const *bh = dir->bh; + const int blocksize_bits = dir->sb->s_blocksize_bits; + union { __le32 *ptr32; u8 *ptr8; } ptr, end; + u32 dircheck = 0; + int last = 5 - 26; + int i = 0; + + /* + * Accumulate each word up to the last whole + * word of the last directory entry. This + * can spread across several buffer heads. + */ + do { + last += 26; + do { + dircheck = le32_to_cpu(dir_u32(i)) ^ ror13(dircheck); + + i += sizeof(u32); + } while (i < (last & ~3)); + } while (dir_u8(last) != 0); + + /* + * Accumulate the last few bytes. These + * bytes will be within the same bh. + */ + if (i != last) { + ptr.ptr8 = bufoff(bh, i); + end.ptr8 = ptr.ptr8 + last - i; + + do + dircheck = *ptr.ptr8++ ^ ror13(dircheck); + while (ptr.ptr8 < end.ptr8); + } + + /* + * The directory tail is in the final bh + * Note that contary to the RISC OS PRMs, + * the first few bytes are NOT included + * in the check. All bytes are in the + * same bh. + */ + ptr.ptr8 = bufoff(bh, 2008); + end.ptr8 = ptr.ptr8 + 36; + + do { + __le32 v = *ptr.ptr32++; + dircheck = le32_to_cpu(v) ^ ror13(dircheck); + } while (ptr.ptr32 < end.ptr32); + + return (dircheck ^ (dircheck >> 8) ^ (dircheck >> 16) ^ (dircheck >> 24)) & 0xff; +} + +/* + * Read and check that a directory is valid + */ +static int +adfs_dir_read(struct super_block *sb, unsigned long object_id, + unsigned int size, struct adfs_dir *dir) +{ + const unsigned int blocksize_bits = sb->s_blocksize_bits; + int blk = 0; + + /* + * Directories which are not a multiple of 2048 bytes + * are considered bad v2 [3.6] + */ + if (size & 2047) + goto bad_dir; + + size >>= blocksize_bits; + + dir->nr_buffers = 0; + dir->sb = sb; + + for (blk = 0; blk < size; blk++) { + int phys; + + phys = __adfs_block_map(sb, object_id, blk); + if (!phys) { + adfs_error(sb, "dir object %lX has a hole at offset %d", + object_id, blk); + goto release_buffers; + } + + dir->bh[blk] = sb_bread(sb, phys); + if (!dir->bh[blk]) + goto release_buffers; + } + + memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead)); + memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail)); + + if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq || + memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4)) + goto bad_dir; + + if (memcmp(&dir->dirhead.startname, "Nick", 4) && + memcmp(&dir->dirhead.startname, "Hugo", 4)) + goto bad_dir; + + if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte) + goto bad_dir; + + dir->nr_buffers = blk; + + return 0; + +bad_dir: + adfs_error(sb, "corrupted directory fragment %lX", + object_id); +release_buffers: + for (blk -= 1; blk >= 0; blk -= 1) + brelse(dir->bh[blk]); + + dir->sb = NULL; + + return -EIO; +} + +/* + * convert a disk-based directory entry to a Linux ADFS directory entry + */ +static inline void +adfs_dir2obj(struct object_info *obj, struct adfs_direntry *de) +{ + obj->name_len = adfs_readname(obj->name, de->dirobname, ADFS_F_NAME_LEN); + obj->file_id = adfs_readval(de->dirinddiscadd, 3); + obj->loadaddr = adfs_readval(de->dirload, 4); + obj->execaddr = adfs_readval(de->direxec, 4); + obj->size = adfs_readval(de->dirlen, 4); + obj->attr = de->newdiratts; +} + +/* + * convert a Linux ADFS directory entry to a disk-based directory entry + */ +static inline void +adfs_obj2dir(struct adfs_direntry *de, struct object_info *obj) +{ + adfs_writeval(de->dirinddiscadd, 3, obj->file_id); + adfs_writeval(de->dirload, 4, obj->loadaddr); + adfs_writeval(de->direxec, 4, obj->execaddr); + adfs_writeval(de->dirlen, 4, obj->size); + de->newdiratts = obj->attr; +} + +/* + * get a directory entry. Note that the caller is responsible + * for holding the relevant locks. + */ +static int +__adfs_dir_get(struct adfs_dir *dir, int pos, struct object_info *obj) +{ + struct super_block *sb = dir->sb; + struct adfs_direntry de; + int thissize, buffer, offset; + + buffer = pos >> sb->s_blocksize_bits; + + if (buffer > dir->nr_buffers) + return -EINVAL; + + offset = pos & (sb->s_blocksize - 1); + thissize = sb->s_blocksize - offset; + if (thissize > 26) + thissize = 26; + + memcpy(&de, dir->bh[buffer]->b_data + offset, thissize); + if (thissize != 26) + memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data, + 26 - thissize); + + if (!de.dirobname[0]) + return -ENOENT; + + adfs_dir2obj(obj, &de); + + return 0; +} + +static int +__adfs_dir_put(struct adfs_dir *dir, int pos, struct object_info *obj) +{ + struct super_block *sb = dir->sb; + struct adfs_direntry de; + int thissize, buffer, offset; + + buffer = pos >> sb->s_blocksize_bits; + + if (buffer > dir->nr_buffers) + return -EINVAL; + + offset = pos & (sb->s_blocksize - 1); + thissize = sb->s_blocksize - offset; + if (thissize > 26) + thissize = 26; + + /* + * Get the entry in total + */ + memcpy(&de, dir->bh[buffer]->b_data + offset, thissize); + if (thissize != 26) + memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data, + 26 - thissize); + + /* + * update it + */ + adfs_obj2dir(&de, obj); + + /* + * Put the new entry back + */ + memcpy(dir->bh[buffer]->b_data + offset, &de, thissize); + if (thissize != 26) + memcpy(dir->bh[buffer + 1]->b_data, ((char *)&de) + thissize, + 26 - thissize); + + return 0; +} + +/* + * the caller is responsible for holding the necessary + * locks. + */ +static int +adfs_dir_find_entry(struct adfs_dir *dir, unsigned long object_id) +{ + int pos, ret; + + ret = -ENOENT; + + for (pos = 5; pos < ADFS_NUM_DIR_ENTRIES * 26 + 5; pos += 26) { + struct object_info obj; + + if (!__adfs_dir_get(dir, pos, &obj)) + break; + + if (obj.file_id == object_id) { + ret = pos; + break; + } + } + + return ret; +} + +static int +adfs_f_read(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir) +{ + int ret; + + if (sz != ADFS_NEWDIR_SIZE) + return -EIO; + + ret = adfs_dir_read(sb, id, sz, dir); + if (ret) + adfs_error(sb, "unable to read directory"); + else + dir->parent_id = adfs_readval(dir->dirtail.new.dirparent, 3); + + return ret; +} + +static int +adfs_f_setpos(struct adfs_dir *dir, unsigned int fpos) +{ + if (fpos >= ADFS_NUM_DIR_ENTRIES) + return -ENOENT; + + dir->pos = 5 + fpos * 26; + return 0; +} + +static int +adfs_f_getnext(struct adfs_dir *dir, struct object_info *obj) +{ + unsigned int ret; + + ret = __adfs_dir_get(dir, dir->pos, obj); + if (ret == 0) + dir->pos += 26; + + return ret; +} + +static int +adfs_f_update(struct adfs_dir *dir, struct object_info *obj) +{ + struct super_block *sb = dir->sb; + int ret, i; + + ret = adfs_dir_find_entry(dir, obj->file_id); + if (ret < 0) { + adfs_error(dir->sb, "unable to locate entry to update"); + goto out; + } + + __adfs_dir_put(dir, ret, obj); + + /* + * Increment directory sequence number + */ + dir->bh[0]->b_data[0] += 1; + dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 6] += 1; + + ret = adfs_dir_checkbyte(dir); + /* + * Update directory check byte + */ + dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 1] = ret; + +#if 1 + { + const unsigned int blocksize_bits = sb->s_blocksize_bits; + + memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead)); + memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail)); + + if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq || + memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4)) + goto bad_dir; + + if (memcmp(&dir->dirhead.startname, "Nick", 4) && + memcmp(&dir->dirhead.startname, "Hugo", 4)) + goto bad_dir; + + if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte) + goto bad_dir; + } +#endif + for (i = dir->nr_buffers - 1; i >= 0; i--) + mark_buffer_dirty(dir->bh[i]); + + ret = 0; +out: + return ret; +#if 1 +bad_dir: + adfs_error(dir->sb, "whoops! I broke a directory!"); + return -EIO; +#endif +} + +static void +adfs_f_free(struct adfs_dir *dir) +{ + int i; + + for (i = dir->nr_buffers - 1; i >= 0; i--) { + brelse(dir->bh[i]); + dir->bh[i] = NULL; + } + + dir->nr_buffers = 0; + dir->sb = NULL; +} + +struct adfs_dir_ops adfs_f_dir_ops = { + .read = adfs_f_read, + .setpos = adfs_f_setpos, + .getnext = adfs_f_getnext, + .update = adfs_f_update, + .free = adfs_f_free +}; diff --git a/fs/adfs/dir_f.h b/fs/adfs/dir_f.h new file mode 100644 index 00000000000..e4713404096 --- /dev/null +++ b/fs/adfs/dir_f.h @@ -0,0 +1,65 @@ +/* + * linux/fs/adfs/dir_f.h + * + * Copyright (C) 1999 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Structures of directories on the F format disk + */ +#ifndef ADFS_DIR_F_H +#define ADFS_DIR_F_H + +/* + * Directory header + */ +struct adfs_dirheader { + unsigned char startmasseq; + unsigned char startname[4]; +}; + +#define ADFS_NEWDIR_SIZE 2048 +#define ADFS_NUM_DIR_ENTRIES 77 + +/* + * Directory entries + */ +struct adfs_direntry { +#define ADFS_F_NAME_LEN 10 + char dirobname[ADFS_F_NAME_LEN]; + __u8 dirload[4]; + __u8 direxec[4]; + __u8 dirlen[4]; + __u8 dirinddiscadd[3]; + __u8 newdiratts; +}; + +/* + * Directory tail + */ +union adfs_dirtail { + struct { + unsigned char dirlastmask; + char dirname[10]; + unsigned char dirparent[3]; + char dirtitle[19]; + unsigned char reserved[14]; + unsigned char endmasseq; + unsigned char endname[4]; + unsigned char dircheckbyte; + } old; + struct { + unsigned char dirlastmask; + unsigned char reserved[2]; + unsigned char dirparent[3]; + char dirtitle[19]; + char dirname[10]; + unsigned char endmasseq; + unsigned char endname[4]; + unsigned char dircheckbyte; + } new; +}; + +#endif diff --git a/fs/adfs/dir_fplus.c b/fs/adfs/dir_fplus.c new file mode 100644 index 00000000000..1ec644e32df --- /dev/null +++ b/fs/adfs/dir_fplus.c @@ -0,0 +1,179 @@ +/* + * linux/fs/adfs/dir_fplus.c + * + * Copyright (C) 1997-1999 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/adfs_fs.h> +#include <linux/time.h> +#include <linux/stat.h> +#include <linux/spinlock.h> +#include <linux/buffer_head.h> +#include <linux/string.h> + +#include "adfs.h" +#include "dir_fplus.h" + +static int +adfs_fplus_read(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir) +{ + struct adfs_bigdirheader *h; + struct adfs_bigdirtail *t; + unsigned long block; + unsigned int blk, size; + int i, ret = -EIO; + + dir->nr_buffers = 0; + + block = __adfs_block_map(sb, id, 0); + if (!block) { + adfs_error(sb, "dir object %X has a hole at offset 0", id); + goto out; + } + + dir->bh[0] = sb_bread(sb, block); + if (!dir->bh[0]) + goto out; + dir->nr_buffers += 1; + + h = (struct adfs_bigdirheader *)dir->bh[0]->b_data; + size = le32_to_cpu(h->bigdirsize); + if (size != sz) { + printk(KERN_WARNING "adfs: adfs_fplus_read: directory header size\n" + " does not match directory size\n"); + } + + if (h->bigdirversion[0] != 0 || h->bigdirversion[1] != 0 || + h->bigdirversion[2] != 0 || size & 2047 || + h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME)) + goto out; + + size >>= sb->s_blocksize_bits; + for (blk = 1; blk < size; blk++) { + block = __adfs_block_map(sb, id, blk); + if (!block) { + adfs_error(sb, "dir object %X has a hole at offset %d", id, blk); + goto out; + } + + dir->bh[blk] = sb_bread(sb, block); + if (!dir->bh[blk]) + goto out; + dir->nr_buffers = blk; + } + + t = (struct adfs_bigdirtail *)(dir->bh[size - 1]->b_data + (sb->s_blocksize - 8)); + + if (t->bigdirendname != cpu_to_le32(BIGDIRENDNAME) || + t->bigdirendmasseq != h->startmasseq || + t->reserved[0] != 0 || t->reserved[1] != 0) + goto out; + + dir->parent_id = le32_to_cpu(h->bigdirparent); + dir->sb = sb; + return 0; +out: + for (i = 0; i < dir->nr_buffers; i++) + brelse(dir->bh[i]); + dir->sb = NULL; + return ret; +} + +static int +adfs_fplus_setpos(struct adfs_dir *dir, unsigned int fpos) +{ + struct adfs_bigdirheader *h = (struct adfs_bigdirheader *)dir->bh[0]->b_data; + int ret = -ENOENT; + + if (fpos <= le32_to_cpu(h->bigdirentries)) { + dir->pos = fpos; + ret = 0; + } + + return ret; +} + +static void +dir_memcpy(struct adfs_dir *dir, unsigned int offset, void *to, int len) +{ + struct super_block *sb = dir->sb; + unsigned int buffer, partial, remainder; + + buffer = offset >> sb->s_blocksize_bits; + offset &= sb->s_blocksize - 1; + + partial = sb->s_blocksize - offset; + + if (partial >= len) + memcpy(to, dir->bh[buffer]->b_data + offset, len); + else { + char *c = (char *)to; + + remainder = len - partial; + + memcpy(c, dir->bh[buffer]->b_data + offset, partial); + memcpy(c + partial, dir->bh[buffer + 1]->b_data, remainder); + } +} + +static int +adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj) +{ + struct adfs_bigdirheader *h = (struct adfs_bigdirheader *)dir->bh[0]->b_data; + struct adfs_bigdirentry bde; + unsigned int offset; + int i, ret = -ENOENT; + + if (dir->pos >= le32_to_cpu(h->bigdirentries)) + goto out; + + offset = offsetof(struct adfs_bigdirheader, bigdirname); + offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3); + offset += dir->pos * sizeof(struct adfs_bigdirentry); + + dir_memcpy(dir, offset, &bde, sizeof(struct adfs_bigdirentry)); + + obj->loadaddr = le32_to_cpu(bde.bigdirload); + obj->execaddr = le32_to_cpu(bde.bigdirexec); + obj->size = le32_to_cpu(bde.bigdirlen); + obj->file_id = le32_to_cpu(bde.bigdirindaddr); + obj->attr = le32_to_cpu(bde.bigdirattr); + obj->name_len = le32_to_cpu(bde.bigdirobnamelen); + + offset = offsetof(struct adfs_bigdirheader, bigdirname); + offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3); + offset += le32_to_cpu(h->bigdirentries) * sizeof(struct adfs_bigdirentry); + offset += le32_to_cpu(bde.bigdirobnameptr); + + dir_memcpy(dir, offset, obj->name, obj->name_len); + for (i = 0; i < obj->name_len; i++) + if (obj->name[i] == '/') + obj->name[i] = '.'; + + dir->pos += 1; + ret = 0; +out: + return ret; +} + +static void +adfs_fplus_free(struct adfs_dir *dir) +{ + int i; + + for (i = 0; i < dir->nr_buffers; i++) + brelse(dir->bh[i]); + dir->sb = NULL; +} + +struct adfs_dir_ops adfs_fplus_dir_ops = { + .read = adfs_fplus_read, + .setpos = adfs_fplus_setpos, + .getnext = adfs_fplus_getnext, + .free = adfs_fplus_free +}; diff --git a/fs/adfs/dir_fplus.h b/fs/adfs/dir_fplus.h new file mode 100644 index 00000000000..b55aa41a68f --- /dev/null +++ b/fs/adfs/dir_fplus.h @@ -0,0 +1,45 @@ +/* + * linux/fs/adfs/dir_fplus.h + * + * Copyright (C) 1999 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Structures of directories on the F+ format disk + */ + +#define ADFS_FPLUS_NAME_LEN 255 + +#define BIGDIRSTARTNAME ('S' | 'B' << 8 | 'P' << 16 | 'r' << 24) +#define BIGDIRENDNAME ('o' | 'v' << 8 | 'e' << 16 | 'n' << 24) + +struct adfs_bigdirheader { + __u8 startmasseq; + __u8 bigdirversion[3]; + __le32 bigdirstartname; + __le32 bigdirnamelen; + __le32 bigdirsize; + __le32 bigdirentries; + __le32 bigdirnamesize; + __le32 bigdirparent; + char bigdirname[1]; +}; + +struct adfs_bigdirentry { + __le32 bigdirload; + __le32 bigdirexec; + __le32 bigdirlen; + __le32 bigdirindaddr; + __le32 bigdirattr; + __le32 bigdirobnamelen; + __le32 bigdirobnameptr; +}; + +struct adfs_bigdirtail { + __le32 bigdirendname; + __u8 bigdirendmasseq; + __u8 reserved[2]; + __u8 bigdircheckbyte; +}; diff --git a/fs/adfs/file.c b/fs/adfs/file.c new file mode 100644 index 00000000000..afebbfde696 --- /dev/null +++ b/fs/adfs/file.c @@ -0,0 +1,43 @@ +/* + * linux/fs/adfs/file.c + * + * Copyright (C) 1997-1999 Russell King + * from: + * + * linux/fs/ext2/file.c + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/fs/minix/file.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * adfs regular file handling primitives + */ +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/fcntl.h> +#include <linux/time.h> +#include <linux/stat.h> +#include <linux/buffer_head.h> /* for file_fsync() */ +#include <linux/adfs_fs.h> + +#include "adfs.h" + +struct file_operations adfs_file_operations = { + .llseek = generic_file_llseek, + .read = generic_file_read, + .mmap = generic_file_mmap, + .fsync = file_fsync, + .write = generic_file_write, + .sendfile = generic_file_sendfile, +}; + +struct inode_operations adfs_file_inode_operations = { + .setattr = adfs_notify_change, +}; diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c new file mode 100644 index 00000000000..a02802a3079 --- /dev/null +++ b/fs/adfs/inode.c @@ -0,0 +1,395 @@ +/* + * linux/fs/adfs/inode.c + * + * Copyright (C) 1997-1999 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/adfs_fs.h> +#include <linux/time.h> +#include <linux/stat.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/smp_lock.h> +#include <linux/module.h> +#include <linux/buffer_head.h> + +#include "adfs.h" + +/* + * Lookup/Create a block at offset 'block' into 'inode'. We currently do + * not support creation of new blocks, so we return -EIO for this case. + */ +static int +adfs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh, + int create) +{ + if (block < 0) + goto abort_negative; + + if (!create) { + if (block >= inode->i_blocks) + goto abort_toobig; + + block = __adfs_block_map(inode->i_sb, inode->i_ino, block); + if (block) + map_bh(bh, inode->i_sb, block); + return 0; + } + /* don't support allocation of blocks yet */ + return -EIO; + +abort_negative: + adfs_error(inode->i_sb, "block %d < 0", block); + return -EIO; + +abort_toobig: + return 0; +} + +static int adfs_writepage(struct page *page, struct writeback_control *wbc) +{ + return block_write_full_page(page, adfs_get_block, wbc); +} + +static int adfs_readpage(struct file *file, struct page *page) +{ + return block_read_full_page(page, adfs_get_block); +} + +static int adfs_prepare_write(struct file *file, struct page *page, unsigned int from, unsigned int to) +{ + return cont_prepare_write(page, from, to, adfs_get_block, + &ADFS_I(page->mapping->host)->mmu_private); +} + +static sector_t _adfs_bmap(struct address_space *mapping, sector_t block) +{ + return generic_block_bmap(mapping, block, adfs_get_block); +} + +static struct address_space_operations adfs_aops = { + .readpage = adfs_readpage, + .writepage = adfs_writepage, + .sync_page = block_sync_page, + .prepare_write = adfs_prepare_write, + .commit_write = generic_commit_write, + .bmap = _adfs_bmap +}; + +static inline unsigned int +adfs_filetype(struct inode *inode) +{ + unsigned int type; + + if (ADFS_I(inode)->stamped) + type = (ADFS_I(inode)->loadaddr >> 8) & 0xfff; + else + type = (unsigned int) -1; + + return type; +} + +/* + * Convert ADFS attributes and filetype to Linux permission. + */ +static umode_t +adfs_atts2mode(struct super_block *sb, struct inode *inode) +{ + unsigned int filetype, attr = ADFS_I(inode)->attr; + umode_t mode, rmask; + struct adfs_sb_info *asb = ADFS_SB(sb); + + if (attr & ADFS_NDA_DIRECTORY) { + mode = S_IRUGO & asb->s_owner_mask; + return S_IFDIR | S_IXUGO | mode; + } + + filetype = adfs_filetype(inode); + + switch (filetype) { + case 0xfc0: /* LinkFS */ + return S_IFLNK|S_IRWXUGO; + + case 0xfe6: /* UnixExec */ + rmask = S_IRUGO | S_IXUGO; + break; + + default: + rmask = S_IRUGO; + } + + mode = S_IFREG; + + if (attr & ADFS_NDA_OWNER_READ) + mode |= rmask & asb->s_owner_mask; + + if (attr & ADFS_NDA_OWNER_WRITE) + mode |= S_IWUGO & asb->s_owner_mask; + + if (attr & ADFS_NDA_PUBLIC_READ) + mode |= rmask & asb->s_other_mask; + + if (attr & ADFS_NDA_PUBLIC_WRITE) + mode |= S_IWUGO & asb->s_other_mask; + return mode; +} + +/* + * Convert Linux permission to ADFS attribute. We try to do the reverse + * of atts2mode, but there is not a 1:1 translation. + */ +static int +adfs_mode2atts(struct super_block *sb, struct inode *inode) +{ + umode_t mode; + int attr; + struct adfs_sb_info *asb = ADFS_SB(sb); + + /* FIXME: should we be able to alter a link? */ + if (S_ISLNK(inode->i_mode)) + return ADFS_I(inode)->attr; + + if (S_ISDIR(inode->i_mode)) + attr = ADFS_NDA_DIRECTORY; + else + attr = 0; + + mode = inode->i_mode & asb->s_owner_mask; + if (mode & S_IRUGO) + attr |= ADFS_NDA_OWNER_READ; + if (mode & S_IWUGO) + attr |= ADFS_NDA_OWNER_WRITE; + + mode = inode->i_mode & asb->s_other_mask; + mode &= ~asb->s_owner_mask; + if (mode & S_IRUGO) + attr |= ADFS_NDA_PUBLIC_READ; + if (mode & S_IWUGO) + attr |= ADFS_NDA_PUBLIC_WRITE; + + return attr; +} + +/* + * Convert an ADFS time to Unix time. ADFS has a 40-bit centi-second time + * referenced to 1 Jan 1900 (til 2248) + */ +static void +adfs_adfs2unix_time(struct timespec *tv, struct inode *inode) +{ + unsigned int high, low; + + if (ADFS_I(inode)->stamped == 0) + goto cur_time; + + high = ADFS_I(inode)->loadaddr << 24; + low = ADFS_I(inode)->execaddr; + + high |= low >> 8; + low &= 255; + + /* Files dated pre 01 Jan 1970 00:00:00. */ + if (high < 0x336e996a) + goto too_early; + + /* Files dated post 18 Jan 2038 03:14:05. */ + if (high >= 0x656e9969) + goto too_late; + + /* discard 2208988800 (0x336e996a00) seconds of time */ + high -= 0x336e996a; + + /* convert 40-bit centi-seconds to 32-bit seconds */ + tv->tv_sec = (((high % 100) << 8) + low) / 100 + (high / 100 << 8); + tv->tv_nsec = 0; + return; + + cur_time: + *tv = CURRENT_TIME_SEC; + return; + + too_early: + tv->tv_sec = tv->tv_nsec = 0; + return; + + too_late: + tv->tv_sec = 0x7ffffffd; + tv->tv_nsec = 0; + return; +} + +/* + * Convert an Unix time to ADFS time. We only do this if the entry has a + * time/date stamp already. + */ +static void +adfs_unix2adfs_time(struct inode *inode, unsigned int secs) +{ + unsigned int high, low; + + if (ADFS_I(inode)->stamped) { + /* convert 32-bit seconds to 40-bit centi-seconds */ + low = (secs & 255) * 100; + high = (secs / 256) * 100 + (low >> 8) + 0x336e996a; + + ADFS_I(inode)->loadaddr = (high >> 24) | + (ADFS_I(inode)->loadaddr & ~0xff); + ADFS_I(inode)->execaddr = (low & 255) | (high << 8); + } +} + +/* + * Fill in the inode information from the object information. + * + * Note that this is an inode-less filesystem, so we can't use the inode + * number to reference the metadata on the media. Instead, we use the + * inode number to hold the object ID, which in turn will tell us where + * the data is held. We also save the parent object ID, and with these + * two, we can locate the metadata. + * + * This does mean that we rely on an objects parent remaining the same at + * all times - we cannot cope with a cross-directory rename (yet). + */ +struct inode * +adfs_iget(struct super_block *sb, struct object_info *obj) +{ + struct inode *inode; + + inode = new_inode(sb); + if (!inode) + goto out; + + inode->i_uid = ADFS_SB(sb)->s_uid; + inode->i_gid = ADFS_SB(sb)->s_gid; + inode->i_ino = obj->file_id; + inode->i_size = obj->size; + inode->i_nlink = 2; + inode->i_blksize = PAGE_SIZE; + inode->i_blocks = (inode->i_size + sb->s_blocksize - 1) >> + sb->s_blocksize_bits; + + /* + * we need to save the parent directory ID so that + * write_inode can update the directory information + * for this file. This will need special handling + * for cross-directory renames. + */ + ADFS_I(inode)->parent_id = obj->parent_id; + ADFS_I(inode)->loadaddr = obj->loadaddr; + ADFS_I(inode)->execaddr = obj->execaddr; + ADFS_I(inode)->attr = obj->attr; + ADFS_I(inode)->stamped = ((obj->loadaddr & 0xfff00000) == 0xfff00000); + + inode->i_mode = adfs_atts2mode(sb, inode); + adfs_adfs2unix_time(&inode->i_mtime, inode); + inode->i_atime = inode->i_mtime; + inode->i_ctime = inode->i_mtime; + + if (S_ISDIR(inode->i_mode)) { + inode->i_op = &adfs_dir_inode_operations; + inode->i_fop = &adfs_dir_operations; + } else if (S_ISREG(inode->i_mode)) { + inode->i_op = &adfs_file_inode_operations; + inode->i_fop = &adfs_file_operations; + inode->i_mapping->a_ops = &adfs_aops; + ADFS_I(inode)->mmu_private = inode->i_size; + } + + insert_inode_hash(inode); + +out: + return inode; +} + +/* + * Validate and convert a changed access mode/time to their ADFS equivalents. + * adfs_write_inode will actually write the information back to the directory + * later. + */ +int +adfs_notify_change(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + struct super_block *sb = inode->i_sb; + unsigned int ia_valid = attr->ia_valid; + int error; + + lock_kernel(); + + error = inode_change_ok(inode, attr); + + /* + * we can't change the UID or GID of any file - + * we have a global UID/GID in the superblock + */ + if ((ia_valid & ATTR_UID && attr->ia_uid != ADFS_SB(sb)->s_uid) || + (ia_valid & ATTR_GID && attr->ia_gid != ADFS_SB(sb)->s_gid)) + error = -EPERM; + + if (error) + goto out; + + if (ia_valid & ATTR_SIZE) + error = vmtruncate(inode, attr->ia_size); + + if (error) + goto out; + + if (ia_valid & ATTR_MTIME) { + inode->i_mtime = attr->ia_mtime; + adfs_unix2adfs_time(inode, attr->ia_mtime.tv_sec); + } + /* + * FIXME: should we make these == to i_mtime since we don't + * have the ability to represent them in our filesystem? + */ + if (ia_valid & ATTR_ATIME) + inode->i_atime = attr->ia_atime; + if (ia_valid & ATTR_CTIME) + inode->i_ctime = attr->ia_ctime; + if (ia_valid & ATTR_MODE) { + ADFS_I(inode)->attr = adfs_mode2atts(sb, inode); + inode->i_mode = adfs_atts2mode(sb, inode); + } + + /* + * FIXME: should we be marking this inode dirty even if + * we don't have any metadata to write back? + */ + if (ia_valid & (ATTR_SIZE | ATTR_MTIME | ATTR_MODE)) + mark_inode_dirty(inode); +out: + unlock_kernel(); + return error; +} + +/* + * write an existing inode back to the directory, and therefore the disk. + * The adfs-specific inode data has already been updated by + * adfs_notify_change() + */ +int adfs_write_inode(struct inode *inode, int unused) +{ + struct super_block *sb = inode->i_sb; + struct object_info obj; + int ret; + + lock_kernel(); + obj.file_id = inode->i_ino; + obj.name_len = 0; + obj.parent_id = ADFS_I(inode)->parent_id; + obj.loadaddr = ADFS_I(inode)->loadaddr; + obj.execaddr = ADFS_I(inode)->execaddr; + obj.attr = ADFS_I(inode)->attr; + obj.size = inode->i_size; + + ret = adfs_dir_update(sb, &obj); + unlock_kernel(); + return ret; +} +MODULE_LICENSE("GPL"); diff --git a/fs/adfs/map.c b/fs/adfs/map.c new file mode 100644 index 00000000000..92ab4fbc203 --- /dev/null +++ b/fs/adfs/map.c @@ -0,0 +1,296 @@ +/* + * linux/fs/adfs/map.c + * + * Copyright (C) 1997-2002 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/adfs_fs.h> +#include <linux/spinlock.h> +#include <linux/buffer_head.h> + +#include <asm/unaligned.h> + +#include "adfs.h" + +/* + * The ADFS map is basically a set of sectors. Each sector is called a + * zone which contains a bitstream made up of variable sized fragments. + * Each bit refers to a set of bytes in the filesystem, defined by + * log2bpmb. This may be larger or smaller than the sector size, but + * the overall size it describes will always be a round number of + * sectors. A fragment id is always idlen bits long. + * + * < idlen > < n > <1> + * +---------+-------//---------+---+ + * | frag id | 0000....000000 | 1 | + * +---------+-------//---------+---+ + * + * The physical disk space used by a fragment is taken from the start of + * the fragment id up to and including the '1' bit - ie, idlen + n + 1 + * bits. + * + * A fragment id can be repeated multiple times in the whole map for + * large or fragmented files. The first map zone a fragment starts in + * is given by fragment id / ids_per_zone - this allows objects to start + * from any zone on the disk. + * + * Free space is described by a linked list of fragments. Each free + * fragment describes free space in the same way as the other fragments, + * however, the frag id specifies an offset (in map bits) from the end + * of this fragment to the start of the next free fragment. + * + * Objects stored on the disk are allocated object ids (we use these as + * our inode numbers.) Object ids contain a fragment id and an optional + * offset. This allows a directory fragment to contain small files + * associated with that directory. + */ + +/* + * For the future... + */ +static DEFINE_RWLOCK(adfs_map_lock); + +/* + * This is fun. We need to load up to 19 bits from the map at an + * arbitary bit alignment. (We're limited to 19 bits by F+ version 2). + */ +#define GET_FRAG_ID(_map,_start,_idmask) \ + ({ \ + unsigned char *_m = _map + (_start >> 3); \ + u32 _frag = get_unaligned((u32 *)_m); \ + _frag >>= (_start & 7); \ + _frag & _idmask; \ + }) + +/* + * return the map bit offset of the fragment frag_id in the zone dm. + * Note that the loop is optimised for best asm code - look at the + * output of: + * gcc -D__KERNEL__ -O2 -I../../include -o - -S map.c + */ +static int +lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen, + const unsigned int frag_id, unsigned int *offset) +{ + const unsigned int mapsize = dm->dm_endbit; + const u32 idmask = (1 << idlen) - 1; + unsigned char *map = dm->dm_bh->b_data + 4; + unsigned int start = dm->dm_startbit; + unsigned int mapptr; + u32 frag; + + do { + frag = GET_FRAG_ID(map, start, idmask); + mapptr = start + idlen; + + /* + * find end of fragment + */ + { + __le32 *_map = (__le32 *)map; + u32 v = le32_to_cpu(_map[mapptr >> 5]) >> (mapptr & 31); + while (v == 0) { + mapptr = (mapptr & ~31) + 32; + if (mapptr >= mapsize) + goto error; + v = le32_to_cpu(_map[mapptr >> 5]); + } + + mapptr += 1 + ffz(~v); + } + + if (frag == frag_id) + goto found; +again: + start = mapptr; + } while (mapptr < mapsize); + return -1; + +error: + printk(KERN_ERR "adfs: oversized fragment 0x%x at 0x%x-0x%x\n", + frag, start, mapptr); + return -1; + +found: + { + int length = mapptr - start; + if (*offset >= length) { + *offset -= length; + goto again; + } + } + return start + *offset; +} + +/* + * Scan the free space map, for this zone, calculating the total + * number of map bits in each free space fragment. + * + * Note: idmask is limited to 15 bits [3.2] + */ +static unsigned int +scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm) +{ + const unsigned int mapsize = dm->dm_endbit + 32; + const unsigned int idlen = asb->s_idlen; + const unsigned int frag_idlen = idlen <= 15 ? idlen : 15; + const u32 idmask = (1 << frag_idlen) - 1; + unsigned char *map = dm->dm_bh->b_data; + unsigned int start = 8, mapptr; + u32 frag; + unsigned long total = 0; + + /* + * get fragment id + */ + frag = GET_FRAG_ID(map, start, idmask); + + /* + * If the freelink is null, then no free fragments + * exist in this zone. + */ + if (frag == 0) + return 0; + + do { + start += frag; + + /* + * get fragment id + */ + frag = GET_FRAG_ID(map, start, idmask); + mapptr = start + idlen; + + /* + * find end of fragment + */ + { + __le32 *_map = (__le32 *)map; + u32 v = le32_to_cpu(_map[mapptr >> 5]) >> (mapptr & 31); + while (v == 0) { + mapptr = (mapptr & ~31) + 32; + if (mapptr >= mapsize) + goto error; + v = le32_to_cpu(_map[mapptr >> 5]); + } + + mapptr += 1 + ffz(~v); + } + + total += mapptr - start; + } while (frag >= idlen + 1); + + if (frag != 0) + printk(KERN_ERR "adfs: undersized free fragment\n"); + + return total; +error: + printk(KERN_ERR "adfs: oversized free fragment\n"); + return 0; +} + +static int +scan_map(struct adfs_sb_info *asb, unsigned int zone, + const unsigned int frag_id, unsigned int mapoff) +{ + const unsigned int idlen = asb->s_idlen; + struct adfs_discmap *dm, *dm_end; + int result; + + dm = asb->s_map + zone; + zone = asb->s_map_size; + dm_end = asb->s_map + zone; + + do { + result = lookup_zone(dm, idlen, frag_id, &mapoff); + + if (result != -1) + goto found; + + dm ++; + if (dm == dm_end) + dm = asb->s_map; + } while (--zone > 0); + + return -1; +found: + result -= dm->dm_startbit; + result += dm->dm_startblk; + + return result; +} + +/* + * calculate the amount of free blocks in the map. + * + * n=1 + * total_free = E(free_in_zone_n) + * nzones + */ +unsigned int +adfs_map_free(struct super_block *sb) +{ + struct adfs_sb_info *asb = ADFS_SB(sb); + struct adfs_discmap *dm; + unsigned int total = 0; + unsigned int zone; + + dm = asb->s_map; + zone = asb->s_map_size; + + do { + total += scan_free_map(asb, dm++); + } while (--zone > 0); + + return signed_asl(total, asb->s_map2blk); +} + +int +adfs_map_lookup(struct super_block *sb, unsigned int frag_id, + unsigned int offset) +{ + struct adfs_sb_info *asb = ADFS_SB(sb); + unsigned int zone, mapoff; + int result; + + /* + * map & root fragment is special - it starts in the center of the + * disk. The other fragments start at zone (frag / ids_per_zone) + */ + if (frag_id == ADFS_ROOT_FRAG) + zone = asb->s_map_size >> 1; + else + zone = frag_id / asb->s_ids_per_zone; + + if (zone >= asb->s_map_size) + goto bad_fragment; + + /* Convert sector offset to map offset */ + mapoff = signed_asl(offset, -asb->s_map2blk); + + read_lock(&adfs_map_lock); + result = scan_map(asb, zone, frag_id, mapoff); + read_unlock(&adfs_map_lock); + + if (result > 0) { + unsigned int secoff; + + /* Calculate sector offset into map block */ + secoff = offset - signed_asl(mapoff, asb->s_map2blk); + return secoff + signed_asl(result, asb->s_map2blk); + } + + adfs_error(sb, "fragment 0x%04x at offset %d not found in map", + frag_id, offset); + return 0; + +bad_fragment: + adfs_error(sb, "invalid fragment 0x%04x (zone = %d, max = %d)", + frag_id, zone, asb->s_map_size); + return 0; +} diff --git a/fs/adfs/super.c b/fs/adfs/super.c new file mode 100644 index 00000000000..243963228d1 --- /dev/null +++ b/fs/adfs/super.c @@ -0,0 +1,508 @@ +/* + * linux/fs/adfs/super.c + * + * Copyright (C) 1997-1999 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/adfs_fs.h> +#include <linux/slab.h> +#include <linux/time.h> +#include <linux/stat.h> +#include <linux/string.h> +#include <linux/init.h> +#include <linux/buffer_head.h> +#include <linux/vfs.h> +#include <linux/parser.h> +#include <linux/bitops.h> + +#include <asm/uaccess.h> +#include <asm/system.h> + +#include <stdarg.h> + +#include "adfs.h" +#include "dir_f.h" +#include "dir_fplus.h" + +void __adfs_error(struct super_block *sb, const char *function, const char *fmt, ...) +{ + char error_buf[128]; + va_list args; + + va_start(args, fmt); + vsprintf(error_buf, fmt, args); + va_end(args); + + printk(KERN_CRIT "ADFS-fs error (device %s)%s%s: %s\n", + sb->s_id, function ? ": " : "", + function ? function : "", error_buf); +} + +static int adfs_checkdiscrecord(struct adfs_discrecord *dr) +{ + int i; + + /* sector size must be 256, 512 or 1024 bytes */ + if (dr->log2secsize != 8 && + dr->log2secsize != 9 && + dr->log2secsize != 10) + return 1; + + /* idlen must be at least log2secsize + 3 */ + if (dr->idlen < dr->log2secsize + 3) + return 1; + + /* we cannot have such a large disc that we + * are unable to represent sector offsets in + * 32 bits. This works out at 2.0 TB. + */ + if (le32_to_cpu(dr->disc_size_high) >> dr->log2secsize) + return 1; + + /* idlen must be no greater than 19 v2 [1.0] */ + if (dr->idlen > 19) + return 1; + + /* reserved bytes should be zero */ + for (i = 0; i < sizeof(dr->unused52); i++) + if (dr->unused52[i] != 0) + return 1; + + return 0; +} + +static unsigned char adfs_calczonecheck(struct super_block *sb, unsigned char *map) +{ + unsigned int v0, v1, v2, v3; + int i; + + v0 = v1 = v2 = v3 = 0; + for (i = sb->s_blocksize - 4; i; i -= 4) { + v0 += map[i] + (v3 >> 8); + v3 &= 0xff; + v1 += map[i + 1] + (v0 >> 8); + v0 &= 0xff; + v2 += map[i + 2] + (v1 >> 8); + v1 &= 0xff; + v3 += map[i + 3] + (v2 >> 8); + v2 &= 0xff; + } + v0 += v3 >> 8; + v1 += map[1] + (v0 >> 8); + v2 += map[2] + (v1 >> 8); + v3 += map[3] + (v2 >> 8); + + return v0 ^ v1 ^ v2 ^ v3; +} + +static int adfs_checkmap(struct super_block *sb, struct adfs_discmap *dm) +{ + unsigned char crosscheck = 0, zonecheck = 1; + int i; + + for (i = 0; i < ADFS_SB(sb)->s_map_size; i++) { + unsigned char *map; + + map = dm[i].dm_bh->b_data; + + if (adfs_calczonecheck(sb, map) != map[0]) { + adfs_error(sb, "zone %d fails zonecheck", i); + zonecheck = 0; + } + crosscheck ^= map[3]; + } + if (crosscheck != 0xff) + adfs_error(sb, "crosscheck != 0xff"); + return crosscheck == 0xff && zonecheck; +} + +static void adfs_put_super(struct super_block *sb) +{ + int i; + struct adfs_sb_info *asb = ADFS_SB(sb); + + for (i = 0; i < asb->s_map_size; i++) + brelse(asb->s_map[i].dm_bh); + kfree(asb->s_map); + kfree(asb); + sb->s_fs_info = NULL; +} + +enum {Opt_uid, Opt_gid, Opt_ownmask, Opt_othmask, Opt_err}; + +static match_table_t tokens = { + {Opt_uid, "uid=%u"}, + {Opt_gid, "gid=%u"}, + {Opt_ownmask, "ownmask=%o"}, + {Opt_othmask, "othmask=%o"}, + {Opt_err, NULL} +}; + +static int parse_options(struct super_block *sb, char *options) +{ + char *p; + struct adfs_sb_info *asb = ADFS_SB(sb); + int option; + + if (!options) + return 0; + + while ((p = strsep(&options, ",")) != NULL) { + substring_t args[MAX_OPT_ARGS]; + int token; + if (!*p) + continue; + + token = match_token(p, tokens, args); + switch (token) { + case Opt_uid: + if (match_int(args, &option)) + return -EINVAL; + asb->s_uid = option; + break; + case Opt_gid: + if (match_int(args, &option)) + return -EINVAL; + asb->s_gid = option; + break; + case Opt_ownmask: + if (match_octal(args, &option)) + return -EINVAL; + asb->s_owner_mask = option; + break; + case Opt_othmask: + if (match_octal(args, &option)) + return -EINVAL; + asb->s_other_mask = option; + break; + default: + printk("ADFS-fs: unrecognised mount option \"%s\" " + "or missing value\n", p); + return -EINVAL; + } + } + return 0; +} + +static int adfs_remount(struct super_block *sb, int *flags, char *data) +{ + *flags |= MS_NODIRATIME; + return parse_options(sb, data); +} + +static int adfs_statfs(struct super_block *sb, struct kstatfs *buf) +{ + struct adfs_sb_info *asb = ADFS_SB(sb); + + buf->f_type = ADFS_SUPER_MAGIC; + buf->f_namelen = asb->s_namelen; + buf->f_bsize = sb->s_blocksize; + buf->f_blocks = asb->s_size; + buf->f_files = asb->s_ids_per_zone * asb->s_map_size; + buf->f_bavail = + buf->f_bfree = adfs_map_free(sb); + buf->f_ffree = (long)(buf->f_bfree * buf->f_files) / (long)buf->f_blocks; + + return 0; +} + +static kmem_cache_t *adfs_inode_cachep; + +static struct inode *adfs_alloc_inode(struct super_block *sb) +{ + struct adfs_inode_info *ei; + ei = (struct adfs_inode_info *)kmem_cache_alloc(adfs_inode_cachep, SLAB_KERNEL); + if (!ei) + return NULL; + return &ei->vfs_inode; +} + +static void adfs_destroy_inode(struct inode *inode) +{ + kmem_cache_free(adfs_inode_cachep, ADFS_I(inode)); +} + +static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) +{ + struct adfs_inode_info *ei = (struct adfs_inode_info *) foo; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) + inode_init_once(&ei->vfs_inode); +} + +static int init_inodecache(void) +{ + adfs_inode_cachep = kmem_cache_create("adfs_inode_cache", + sizeof(struct adfs_inode_info), + 0, SLAB_RECLAIM_ACCOUNT, + init_once, NULL); + if (adfs_inode_cachep == NULL) + return -ENOMEM; + return 0; +} + +static void destroy_inodecache(void) +{ + if (kmem_cache_destroy(adfs_inode_cachep)) + printk(KERN_INFO "adfs_inode_cache: not all structures were freed\n"); +} + +static struct super_operations adfs_sops = { + .alloc_inode = adfs_alloc_inode, + .destroy_inode = adfs_destroy_inode, + .write_inode = adfs_write_inode, + .put_super = adfs_put_super, + .statfs = adfs_statfs, + .remount_fs = adfs_remount, +}; + +static struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_discrecord *dr) +{ + struct adfs_discmap *dm; + unsigned int map_addr, zone_size, nzones; + int i, zone; + struct adfs_sb_info *asb = ADFS_SB(sb); + + nzones = asb->s_map_size; + zone_size = (8 << dr->log2secsize) - le16_to_cpu(dr->zone_spare); + map_addr = (nzones >> 1) * zone_size - + ((nzones > 1) ? ADFS_DR_SIZE_BITS : 0); + map_addr = signed_asl(map_addr, asb->s_map2blk); + + asb->s_ids_per_zone = zone_size / (asb->s_idlen + 1); + + dm = kmalloc(nzones * sizeof(*dm), GFP_KERNEL); + if (dm == NULL) { + adfs_error(sb, "not enough memory"); + return NULL; + } + + for (zone = 0; zone < nzones; zone++, map_addr++) { + dm[zone].dm_startbit = 0; + dm[zone].dm_endbit = zone_size; + dm[zone].dm_startblk = zone * zone_size - ADFS_DR_SIZE_BITS; + dm[zone].dm_bh = sb_bread(sb, map_addr); + + if (!dm[zone].dm_bh) { + adfs_error(sb, "unable to read map"); + goto error_free; + } + } + + /* adjust the limits for the first and last map zones */ + i = zone - 1; + dm[0].dm_startblk = 0; + dm[0].dm_startbit = ADFS_DR_SIZE_BITS; + dm[i].dm_endbit = (le32_to_cpu(dr->disc_size_high) << (32 - dr->log2bpmb)) + + (le32_to_cpu(dr->disc_size) >> dr->log2bpmb) + + (ADFS_DR_SIZE_BITS - i * zone_size); + + if (adfs_checkmap(sb, dm)) + return dm; + + adfs_error(sb, NULL, "map corrupted"); + +error_free: + while (--zone >= 0) + brelse(dm[zone].dm_bh); + + kfree(dm); + return NULL; +} + +static inline unsigned long adfs_discsize(struct adfs_discrecord *dr, int block_bits) +{ + unsigned long discsize; + + discsize = le32_to_cpu(dr->disc_size_high) << (32 - block_bits); + discsize |= le32_to_cpu(dr->disc_size) >> block_bits; + + return discsize; +} + +static int adfs_fill_super(struct super_block *sb, void *data, int silent) +{ + struct adfs_discrecord *dr; + struct buffer_head *bh; + struct object_info root_obj; + unsigned char *b_data; + struct adfs_sb_info *asb; + struct inode *root; + + sb->s_flags |= MS_NODIRATIME; + + asb = kmalloc(sizeof(*asb), GFP_KERNEL); + if (!asb) + return -ENOMEM; + sb->s_fs_info = asb; + memset(asb, 0, sizeof(*asb)); + + /* set default options */ + asb->s_uid = 0; + asb->s_gid = 0; + asb->s_owner_mask = S_IRWXU; + asb->s_other_mask = S_IRWXG | S_IRWXO; + + if (parse_options(sb, data)) + goto error; + + sb_set_blocksize(sb, BLOCK_SIZE); + if (!(bh = sb_bread(sb, ADFS_DISCRECORD / BLOCK_SIZE))) { + adfs_error(sb, "unable to read superblock"); + goto error; + } + + b_data = bh->b_data + (ADFS_DISCRECORD % BLOCK_SIZE); + + if (adfs_checkbblk(b_data)) { + if (!silent) + printk("VFS: Can't find an adfs filesystem on dev " + "%s.\n", sb->s_id); + goto error_free_bh; + } + + dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET); + + /* + * Do some sanity checks on the ADFS disc record + */ + if (adfs_checkdiscrecord(dr)) { + if (!silent) + printk("VPS: Can't find an adfs filesystem on dev " + "%s.\n", sb->s_id); + goto error_free_bh; + } + + brelse(bh); + if (sb_set_blocksize(sb, 1 << dr->log2secsize)) { + bh = sb_bread(sb, ADFS_DISCRECORD / sb->s_blocksize); + if (!bh) { + adfs_error(sb, "couldn't read superblock on " + "2nd try."); + goto error; + } + b_data = bh->b_data + (ADFS_DISCRECORD % sb->s_blocksize); + if (adfs_checkbblk(b_data)) { + adfs_error(sb, "disc record mismatch, very weird!"); + goto error_free_bh; + } + dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET); + } else { + if (!silent) + printk(KERN_ERR "VFS: Unsupported blocksize on dev " + "%s.\n", sb->s_id); + goto error; + } + + /* + * blocksize on this device should now be set to the ADFS log2secsize + */ + + sb->s_magic = ADFS_SUPER_MAGIC; + asb->s_idlen = dr->idlen; + asb->s_map_size = dr->nzones | (dr->nzones_high << 8); + asb->s_map2blk = dr->log2bpmb - dr->log2secsize; + asb->s_size = adfs_discsize(dr, sb->s_blocksize_bits); + asb->s_version = dr->format_version; + asb->s_log2sharesize = dr->log2sharesize; + + asb->s_map = adfs_read_map(sb, dr); + if (!asb->s_map) + goto error_free_bh; + + brelse(bh); + + /* + * set up enough so that we can read an inode + */ + sb->s_op = &adfs_sops; + + dr = (struct adfs_discrecord *)(asb->s_map[0].dm_bh->b_data + 4); + + root_obj.parent_id = root_obj.file_id = le32_to_cpu(dr->root); + root_obj.name_len = 0; + root_obj.loadaddr = 0; + root_obj.execaddr = 0; + root_obj.size = ADFS_NEWDIR_SIZE; + root_obj.attr = ADFS_NDA_DIRECTORY | ADFS_NDA_OWNER_READ | + ADFS_NDA_OWNER_WRITE | ADFS_NDA_PUBLIC_READ; + + /* + * If this is a F+ disk with variable length directories, + * get the root_size from the disc record. + */ + if (asb->s_version) { + root_obj.size = le32_to_cpu(dr->root_size); + asb->s_dir = &adfs_fplus_dir_ops; + asb->s_namelen = ADFS_FPLUS_NAME_LEN; + } else { + asb->s_dir = &adfs_f_dir_ops; + asb->s_namelen = ADFS_F_NAME_LEN; + } + + root = adfs_iget(sb, &root_obj); + sb->s_root = d_alloc_root(root); + if (!sb->s_root) { + int i; + iput(root); + for (i = 0; i < asb->s_map_size; i++) + brelse(asb->s_map[i].dm_bh); + kfree(asb->s_map); + adfs_error(sb, "get root inode failed\n"); + goto error; + } else + sb->s_root->d_op = &adfs_dentry_operations; + return 0; + +error_free_bh: + brelse(bh); +error: + sb->s_fs_info = NULL; + kfree(asb); + return -EINVAL; +} + +static struct super_block *adfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) +{ + return get_sb_bdev(fs_type, flags, dev_name, data, adfs_fill_super); +} + +static struct file_system_type adfs_fs_type = { + .owner = THIS_MODULE, + .name = "adfs", + .get_sb = adfs_get_sb, + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, +}; + +static int __init init_adfs_fs(void) +{ + int err = init_inodecache(); + if (err) + goto out1; + err = register_filesystem(&adfs_fs_type); + if (err) + goto out; + return 0; +out: + destroy_inodecache(); +out1: + return err; +} + +static void __exit exit_adfs_fs(void) +{ + unregister_filesystem(&adfs_fs_type); + destroy_inodecache(); +} + +module_init(init_adfs_fs) +module_exit(exit_adfs_fs) diff --git a/fs/affs/Changes b/fs/affs/Changes new file mode 100644 index 00000000000..a29409c1ffe --- /dev/null +++ b/fs/affs/Changes @@ -0,0 +1,343 @@ +(Note: I consider version numbers as cheap. That means +that I do not like numbers like 0.1 and the like for +things that can be used since quite some time. But +then, 3.1 doesn't mean 'perfectly stable', too.) + +Known bugs: +----------- + +- Doesn't work on the alpha. The only 64/32-bit + problem that I'm aware of (pointer/int conversion + in readdir()) gives compiler warnings but is + apparently not causing the failure, as directory + reads basically work (but all files are of size 0). + Alas, I've got no alpha to debug. :-( + +- The partition checker (drivers/block/genhd.c) + doesn't work with devices which have 256 byte + blocks (some very old SCSI drives). + +- The feature to automatically make the fs clean + might leave a trashed file system with the + bitmap flag set valid. + +- When a file is truncated to a size that is not + a multiple of the blocksize, the rest of the + last allocated block is not cleared. Well, + this fs never claimed to be Posix conformant. + +Please direct bug reports to: zippel@linux-m68k.org + +Version 3.20 +------------ +- kill kernel lock +- fix for a possible bitmap corruption + +Version 3.19 +------------ + +- sizeof changes from Kernel Janitor Project +- several bug fixes found with fsx + +Version 3.18 +------------ + +- change to global min macro + warning fixes +- add module tags + +Version 3.17 +------------ + +- locking fixes +- wrong sign in __affs_hash_dentry +- remove unnecessary check in affs_new_inode +- enable international mode for dircache fs + +Version 3.16 +------------ + +- use mark_buffer_dirty_inode instead of mark_buffer_dirty. +- introduce affs_lock_{link|dir|ext}. + +Version 3.15 +------------ + +- disable link to directories until we can properly support them. +- locking fixes for link creation/removal. + +Version 3.14 +------------ + +- correctly cut off long file names for compares +- correctly initialize s_last_bmap + +Version 3.13 +------------ + +Major cleanup for 2.4 [Roman Zippel] +- new extended block handling +- new bitmap allocation functions +- locking should be safe for the future +- cleanup of some interfaces + +Version 3.12 +------------ + +more 2.4 fixes: [Roman Zippel] +- s_lock changes +- increased getblock mess +- clear meta blocks + +Version 3.11 +------------ + +- Converted to use 2.3.x page cache [Dave Jones <dave@powertweak.com>] +- Corruption in truncate() bugfix [Ken Tyler <kent@werple.net.au>] + +Version 3.10 +------------ + +- Changed partition checker to allow devices + with physical blocks != 512 bytes. + +- The partition checker now also ignores the + word at 0xd0 that Windows likes to write to. + +Version 3.9 +----------- + +- Moved cleanup from release_file() to put_inode(). + This makes the first one obsolete. + +- truncate() zeroes the unused remainder of a + partially used last block when a file is truncated. + It also marks the inode dirty now (which is not + really necessary as notify_change() will do + it anyway). + +- Added a few comments, fixed some typos (and + introduced some new ones), made the debug messages + more consistent. Changed a bad example in the + doc file (affs.txt). + +- Sets the NOEXEC flag in read_super() for old file + systems, since you can't run programs on them. + +Version 3.8 +----------- +Bill Hawes kindly reviewed the affs and sent me the +patches he did. They're marked (BH). Thanks, Bill! + +- Cleanup of error handling in read_super(). + Didn't release all resources in case of an + error. (BH) + +- put_inode() releases the ext cache only if it's + no longer needed. (BH) + +- One set of dentry callbacks is enough. (BH) + +- Cleanup of error handling in namei.c. (BH) + +- Cleanup of error handling in file.c. (BH) + +- The original blocksize of the device is + restored when the fs is unmounted. (BH) + +- getblock() did not invalidate the key cache + when it allocated a new block. + +- Removed some unnecessary locks as Bill + suggested. + +- Simplified match_name(), changed all hashing + and case insensitive name comparisons to use + uppercase. This makes the tolower() routines + obsolete. + +- Added mount option 'mufs' to force muFS + uid/gid interpretation. + +- File mode changes were not updated on disk. + This was fixed before, but somehow got lost. + +Version 3.7 +----------- + +- Added dentry callbacks to allow the dcache to + operate case insensitive and length ignorant + like the affs itself. + +- getblock() didn't update the lastblock field in the + inode if the fs was not an OFS. This bug only shows + up if a file was enlarged via truncate() and there + was not enough space. + +- Remove some more superfluous code left over from + the old link days ... + +- Fixed some oversights which were in patch 2.1.78. + +- Fixed a few typos. + +Version 3.6 +----------- + +- dentry changes. (Thanks to Jes Sorensen for his help.) + +- Fixed bug in balloc(): Superblock was not set dirty after + the bitmap was changed, so the bitmap wasn't sync'd. + +- Fixed nasty bug in find_new_zone(): If the current + zone number was zero, the loop didn't terminate, + causing a solid lock-up. + +- Removed support for old-style directory reads. + +- Fixed bug in add_entry(): When doing a sorted insert, + the pointer to the next entry in the hash chain wasn't + correctly byte-swapped. Since most of the users of the + affs use it on a 68k, they didn't notice. But why did + I not find this during my tests? + +- Fixed some oversights (version wasn't updated on some + directory changes). + +- Handling of hard links rewritten. To the VFS + they appear now as normal Unix links. They are + now resolved only once in lookup(). The backside + is that unlink(), rename() and rmdir() have to + be smart about them, but the result is worth the + effort. This also led to some code cleanup. + +- Changed name type to unsigned char; the test for + invalid filenames didn't work correctly. + (Thanks to Michael Krause for pointing at this.) + +- Changed mapping of executable flag. + +- Changed all network byte-order macros to the + recommended ones. + +- Added a remount function, so attempts to remount + a dircache filesystem or one with errors read/write + can be trapped. Previously, ro remounts didn't + flush the super block, and rw remounts didn't + create allocation zones ... + +- Call shrink_dcache_parent() in rmdir(). + (Thanks to Bill Hawes.) + +- Permission checks in unlink(). + +- Allow mounting of volumes with superfluous + bitmap pointers read only, also allows them + to be remounted read/write. + +- Owner/Group defaults now to the fs user (i.e. + the one that mounted it) instead of root. This + obsoletes the mount options uid and gid. + +- Argument to volume option could overflow the + name buffer. It is now silently truncated to + 30 characters. (Damn it! This kind of bug + is too embarrassing.) + +- Split inode.c into 2 files, the superblock + routines desperately wanted their own file. + +- truncate() didn't allocate an extension block + cache. If a file was extended by means of + truncate(), this led to an Oops. + +- fsuser is now checked last. + +- rename() will not ignore changes in filename + casing any more (though mv(1) still won't allow + you to do "mv oldname OldName"). + +Version 3.5 +----------- + +- Extension block caches are now allocated on + demand instead of when a file is opened, as + files can be read and written without opening + them (e. g. the loopback device does this). + +- Removed an unused function. + +Version 3.4 +----------- + +- Hash chains are now sorted by block numbers. + (Thanks to Kars de Jong for finding this.) +- Removed all unnecessary external symbols. + +Version 3.3 +----------- + +- Tried to make all types 'correct' and consistent. +- Errors and warnings are now reported via a + function. They are all prefixed by a severity + and have the same appearance: + "AFFS: <function>: <error message>" + (There's one exception to this, as in that function + is no pointer to the super block available.) +- The filesystem is remounted read-only after an + error. +- The names of newly created filesystem objects are + now checked for validity. +- Minor cleanups in comments. +- Added this Changes file. At last! + +Version 3.2 +----------- + +- Extension block cache: Reading/writing of huge files + (several MB) is much faster (of course the added + overhead slows down opening, but this is hardly + noticeable). +- The same get_block()-routine can now be used for + both OFS and FFS. +- The super block is now searched in the block that + was calculated and in the one following. This + should remedy the round-off error introduced by + the 1-k blocks that Linux uses. +- Minor changes to adhere to the new VFS interface. +- The number of used blocks is now also calculated + if the filesystem is mounted read-only. +- Prefixed some constants with AFFS_ to avoid name + clashes. +- Removed 'EXPERIMENTAL' status. + +Version 3.1 +----------- + +- Fixed a nasty bug which didn't allow read-only + mounts. +- Allow dir-cache filesystems to be mounted + read only. +- OFS support. +- Several other changes I just cannot remember + any more. + +Version 3.0 +----------- + +- Almost complete rewrite for the new VFS + interface in Linux 1.3. +- Write support. +- Support for hard and symbolic links. +- Lots of things I remember even less ... + +Version 2.0 +----------- + +- Fixed a few things to get it compiled. +- Automatic root block calculation. +- Partition checker for genhd.c + +======================================== + +Let's just call Ray Burr's original affs +'Version 1.0'. diff --git a/fs/affs/Makefile b/fs/affs/Makefile new file mode 100644 index 00000000000..b2c4f54446f --- /dev/null +++ b/fs/affs/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the Linux affs filesystem routines. +# + +#EXTRA_CFLAGS=-DDEBUG=1 + +obj-$(CONFIG_AFFS_FS) += affs.o + +affs-objs := super.o namei.o inode.o file.o dir.o amigaffs.o bitmap.o symlink.o diff --git a/fs/affs/affs.h b/fs/affs/affs.h new file mode 100644 index 00000000000..0c6799f2137 --- /dev/null +++ b/fs/affs/affs.h @@ -0,0 +1,304 @@ +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/buffer_head.h> +#include <linux/affs_fs.h> +#include <linux/amigaffs.h> + +/* AmigaOS allows file names with up to 30 characters length. + * Names longer than that will be silently truncated. If you + * want to disallow this, comment out the following #define. + * Creating filesystem objects with longer names will then + * result in an error (ENAMETOOLONG). + */ +/*#define AFFS_NO_TRUNCATE */ + +/* Ugly macros make the code more pretty. */ + +#define GET_END_PTR(st,p,sz) ((st *)((char *)(p)+((sz)-sizeof(st)))) +#define AFFS_GET_HASHENTRY(data,hashkey) be32_to_cpu(((struct dir_front *)data)->hashtable[hashkey]) +#define AFFS_BLOCK(sb, bh, blk) (AFFS_HEAD(bh)->table[AFFS_SB(sb)->s_hashsize-1-(blk)]) + +#ifdef __LITTLE_ENDIAN +#define BO_EXBITS 0x18UL +#elif defined(__BIG_ENDIAN) +#define BO_EXBITS 0x00UL +#else +#error Endianness must be known for affs to work. +#endif + +#define AFFS_HEAD(bh) ((struct affs_head *)(bh)->b_data) +#define AFFS_TAIL(sb, bh) ((struct affs_tail *)((bh)->b_data+(sb)->s_blocksize-sizeof(struct affs_tail))) +#define AFFS_ROOT_HEAD(bh) ((struct affs_root_head *)(bh)->b_data) +#define AFFS_ROOT_TAIL(sb, bh) ((struct affs_root_tail *)((bh)->b_data+(sb)->s_blocksize-sizeof(struct affs_root_tail))) +#define AFFS_DATA_HEAD(bh) ((struct affs_data_head *)(bh)->b_data) +#define AFFS_DATA(bh) (((struct affs_data_head *)(bh)->b_data)->data) + +#define AFFS_CACHE_SIZE PAGE_SIZE + +#define AFFS_MAX_PREALLOC 32 +#define AFFS_LC_SIZE (AFFS_CACHE_SIZE/sizeof(u32)/2) +#define AFFS_AC_SIZE (AFFS_CACHE_SIZE/sizeof(struct affs_ext_key)/2) +#define AFFS_AC_MASK (AFFS_AC_SIZE-1) + +struct affs_ext_key { + u32 ext; /* idx of the extended block */ + u32 key; /* block number */ +}; + +/* + * affs fs inode data in memory + */ +struct affs_inode_info { + u32 i_opencnt; + struct semaphore i_link_lock; /* Protects internal inode access. */ + struct semaphore i_ext_lock; /* Protects internal inode access. */ +#define i_hash_lock i_ext_lock + u32 i_blkcnt; /* block count */ + u32 i_extcnt; /* extended block count */ + u32 *i_lc; /* linear cache of extended blocks */ + u32 i_lc_size; + u32 i_lc_shift; + u32 i_lc_mask; + struct affs_ext_key *i_ac; /* associative cache of extended blocks */ + u32 i_ext_last; /* last accessed extended block */ + struct buffer_head *i_ext_bh; /* bh of last extended block */ + loff_t mmu_private; + u32 i_protect; /* unused attribute bits */ + u32 i_lastalloc; /* last allocated block */ + int i_pa_cnt; /* number of preallocated blocks */ + struct inode vfs_inode; +}; + +/* short cut to get to the affs specific inode data */ +static inline struct affs_inode_info *AFFS_I(struct inode *inode) +{ + return list_entry(inode, struct affs_inode_info, vfs_inode); +} + +/* + * super-block data in memory + * + * Block numbers are adjusted for their actual size + * + */ + +struct affs_bm_info { + u32 bm_key; /* Disk block number */ + u32 bm_free; /* Free blocks in here */ +}; + +struct affs_sb_info { + int s_partition_size; /* Partition size in blocks. */ + int s_reserved; /* Number of reserved blocks. */ + //u32 s_blksize; /* Initial device blksize */ + u32 s_data_blksize; /* size of the data block w/o header */ + u32 s_root_block; /* FFS root block number. */ + int s_hashsize; /* Size of hash table. */ + unsigned long s_flags; /* See below. */ + uid_t s_uid; /* uid to override */ + gid_t s_gid; /* gid to override */ + umode_t s_mode; /* mode to override */ + struct buffer_head *s_root_bh; /* Cached root block. */ + struct semaphore s_bmlock; /* Protects bitmap access. */ + struct affs_bm_info *s_bitmap; /* Bitmap infos. */ + u32 s_bmap_count; /* # of bitmap blocks. */ + u32 s_bmap_bits; /* # of bits in one bitmap blocks */ + u32 s_last_bmap; + struct buffer_head *s_bmap_bh; + char *s_prefix; /* Prefix for volumes and assigns. */ + int s_prefix_len; /* Length of prefix. */ + char s_volume[32]; /* Volume prefix for absolute symlinks. */ +}; + +#define SF_INTL 0x0001 /* International filesystem. */ +#define SF_BM_VALID 0x0002 /* Bitmap is valid. */ +#define SF_IMMUTABLE 0x0004 /* Protection bits cannot be changed */ +#define SF_QUIET 0x0008 /* chmod errors will be not reported */ +#define SF_SETUID 0x0010 /* Ignore Amiga uid */ +#define SF_SETGID 0x0020 /* Ignore Amiga gid */ +#define SF_SETMODE 0x0040 /* Ignore Amiga protection bits */ +#define SF_MUFS 0x0100 /* Use MUFS uid/gid mapping */ +#define SF_OFS 0x0200 /* Old filesystem */ +#define SF_PREFIX 0x0400 /* Buffer for prefix is allocated */ +#define SF_VERBOSE 0x0800 /* Talk about fs when mounting */ + +/* short cut to get to the affs specific sb data */ +static inline struct affs_sb_info *AFFS_SB(struct super_block *sb) +{ + return sb->s_fs_info; +} + +/* amigaffs.c */ + +extern int affs_insert_hash(struct inode *inode, struct buffer_head *bh); +extern int affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh); +extern int affs_remove_header(struct dentry *dentry); +extern u32 affs_checksum_block(struct super_block *sb, struct buffer_head *bh); +extern void affs_fix_checksum(struct super_block *sb, struct buffer_head *bh); +extern void secs_to_datestamp(time_t secs, struct affs_date *ds); +extern mode_t prot_to_mode(u32 prot); +extern void mode_to_prot(struct inode *inode); +extern void affs_error(struct super_block *sb, const char *function, const char *fmt, ...); +extern void affs_warning(struct super_block *sb, const char *function, const char *fmt, ...); +extern int affs_check_name(const unsigned char *name, int len); +extern int affs_copy_name(unsigned char *bstr, struct dentry *dentry); + +/* bitmap. c */ + +extern u32 affs_count_free_blocks(struct super_block *s); +extern void affs_free_block(struct super_block *sb, u32 block); +extern u32 affs_alloc_block(struct inode *inode, u32 goal); +extern int affs_init_bitmap(struct super_block *sb, int *flags); +extern void affs_free_bitmap(struct super_block *sb); + +/* namei.c */ + +extern int affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len); +extern struct dentry *affs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *); +extern int affs_unlink(struct inode *dir, struct dentry *dentry); +extern int affs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *); +extern int affs_mkdir(struct inode *dir, struct dentry *dentry, int mode); +extern int affs_rmdir(struct inode *dir, struct dentry *dentry); +extern int affs_link(struct dentry *olddentry, struct inode *dir, + struct dentry *dentry); +extern int affs_symlink(struct inode *dir, struct dentry *dentry, + const char *symname); +extern int affs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry); + +/* inode.c */ + +extern unsigned long affs_parent_ino(struct inode *dir); +extern struct inode *affs_new_inode(struct inode *dir); +extern int affs_notify_change(struct dentry *dentry, struct iattr *attr); +extern void affs_put_inode(struct inode *inode); +extern void affs_delete_inode(struct inode *inode); +extern void affs_clear_inode(struct inode *inode); +extern void affs_read_inode(struct inode *inode); +extern int affs_write_inode(struct inode *inode, int); +extern int affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s32 type); + +/* file.c */ + +void affs_free_prealloc(struct inode *inode); +extern void affs_truncate(struct inode *); + +/* dir.c */ + +extern void affs_dir_truncate(struct inode *); + +/* jump tables */ + +extern struct inode_operations affs_file_inode_operations; +extern struct inode_operations affs_dir_inode_operations; +extern struct inode_operations affs_symlink_inode_operations; +extern struct file_operations affs_file_operations; +extern struct file_operations affs_file_operations_ofs; +extern struct file_operations affs_dir_operations; +extern struct address_space_operations affs_symlink_aops; +extern struct address_space_operations affs_aops; +extern struct address_space_operations affs_aops_ofs; + +extern struct dentry_operations affs_dentry_operations; +extern struct dentry_operations affs_dentry_operations_intl; + +static inline void +affs_set_blocksize(struct super_block *sb, int size) +{ + sb_set_blocksize(sb, size); +} +static inline struct buffer_head * +affs_bread(struct super_block *sb, int block) +{ + pr_debug("affs_bread: %d\n", block); + if (block >= AFFS_SB(sb)->s_reserved && block < AFFS_SB(sb)->s_partition_size) + return sb_bread(sb, block); + return NULL; +} +static inline struct buffer_head * +affs_getblk(struct super_block *sb, int block) +{ + pr_debug("affs_getblk: %d\n", block); + if (block >= AFFS_SB(sb)->s_reserved && block < AFFS_SB(sb)->s_partition_size) + return sb_getblk(sb, block); + return NULL; +} +static inline struct buffer_head * +affs_getzeroblk(struct super_block *sb, int block) +{ + struct buffer_head *bh; + pr_debug("affs_getzeroblk: %d\n", block); + if (block >= AFFS_SB(sb)->s_reserved && block < AFFS_SB(sb)->s_partition_size) { + bh = sb_getblk(sb, block); + lock_buffer(bh); + memset(bh->b_data, 0 , sb->s_blocksize); + set_buffer_uptodate(bh); + unlock_buffer(bh); + return bh; + } + return NULL; +} +static inline struct buffer_head * +affs_getemptyblk(struct super_block *sb, int block) +{ + struct buffer_head *bh; + pr_debug("affs_getemptyblk: %d\n", block); + if (block >= AFFS_SB(sb)->s_reserved && block < AFFS_SB(sb)->s_partition_size) { + bh = sb_getblk(sb, block); + wait_on_buffer(bh); + set_buffer_uptodate(bh); + return bh; + } + return NULL; +} +static inline void +affs_brelse(struct buffer_head *bh) +{ + if (bh) + pr_debug("affs_brelse: %lld\n", (long long) bh->b_blocknr); + brelse(bh); +} + +static inline void +affs_adjust_checksum(struct buffer_head *bh, u32 val) +{ + u32 tmp = be32_to_cpu(((__be32 *)bh->b_data)[5]); + ((__be32 *)bh->b_data)[5] = cpu_to_be32(tmp - val); +} +static inline void +affs_adjust_bitmapchecksum(struct buffer_head *bh, u32 val) +{ + u32 tmp = be32_to_cpu(((__be32 *)bh->b_data)[0]); + ((__be32 *)bh->b_data)[0] = cpu_to_be32(tmp - val); +} + +static inline void +affs_lock_link(struct inode *inode) +{ + down(&AFFS_I(inode)->i_link_lock); +} +static inline void +affs_unlock_link(struct inode *inode) +{ + up(&AFFS_I(inode)->i_link_lock); +} +static inline void +affs_lock_dir(struct inode *inode) +{ + down(&AFFS_I(inode)->i_hash_lock); +} +static inline void +affs_unlock_dir(struct inode *inode) +{ + up(&AFFS_I(inode)->i_hash_lock); +} +static inline void +affs_lock_ext(struct inode *inode) +{ + down(&AFFS_I(inode)->i_ext_lock); +} +static inline void +affs_unlock_ext(struct inode *inode) +{ + up(&AFFS_I(inode)->i_ext_lock); +} diff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c new file mode 100644 index 00000000000..ccd624ef427 --- /dev/null +++ b/fs/affs/amigaffs.c @@ -0,0 +1,509 @@ +/* + * linux/fs/affs/amigaffs.c + * + * (c) 1996 Hans-Joachim Widmaier - Rewritten + * + * (C) 1993 Ray Burr - Amiga FFS filesystem. + * + * Please send bug reports to: hjw@zvw.de + */ + +#include "affs.h" + +extern struct timezone sys_tz; + +static char ErrorBuffer[256]; + +/* + * Functions for accessing Amiga-FFS structures. + */ + + +/* Insert a header block bh into the directory dir + * caller must hold AFFS_DIR->i_hash_lock! + */ + +int +affs_insert_hash(struct inode *dir, struct buffer_head *bh) +{ + struct super_block *sb = dir->i_sb; + struct buffer_head *dir_bh; + u32 ino, hash_ino; + int offset; + + ino = bh->b_blocknr; + offset = affs_hash_name(sb, AFFS_TAIL(sb, bh)->name + 1, AFFS_TAIL(sb, bh)->name[0]); + + pr_debug("AFFS: insert_hash(dir=%u, ino=%d)\n", (u32)dir->i_ino, ino); + + dir_bh = affs_bread(sb, dir->i_ino); + if (!dir_bh) + return -EIO; + + hash_ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[offset]); + while (hash_ino) { + affs_brelse(dir_bh); + dir_bh = affs_bread(sb, hash_ino); + if (!dir_bh) + return -EIO; + hash_ino = be32_to_cpu(AFFS_TAIL(sb, dir_bh)->hash_chain); + } + AFFS_TAIL(sb, bh)->parent = cpu_to_be32(dir->i_ino); + AFFS_TAIL(sb, bh)->hash_chain = 0; + affs_fix_checksum(sb, bh); + + if (dir->i_ino == dir_bh->b_blocknr) + AFFS_HEAD(dir_bh)->table[offset] = cpu_to_be32(ino); + else + AFFS_TAIL(sb, dir_bh)->hash_chain = cpu_to_be32(ino); + + affs_adjust_checksum(dir_bh, ino); + mark_buffer_dirty_inode(dir_bh, dir); + affs_brelse(dir_bh); + + dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; + dir->i_version++; + mark_inode_dirty(dir); + + return 0; +} + +/* Remove a header block from its directory. + * caller must hold AFFS_DIR->i_hash_lock! + */ + +int +affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh) +{ + struct super_block *sb; + struct buffer_head *bh; + u32 rem_ino, hash_ino; + __be32 ino; + int offset, retval; + + sb = dir->i_sb; + rem_ino = rem_bh->b_blocknr; + offset = affs_hash_name(sb, AFFS_TAIL(sb, rem_bh)->name+1, AFFS_TAIL(sb, rem_bh)->name[0]); + pr_debug("AFFS: remove_hash(dir=%d, ino=%d, hashval=%d)\n", (u32)dir->i_ino, rem_ino, offset); + + bh = affs_bread(sb, dir->i_ino); + if (!bh) + return -EIO; + + retval = -ENOENT; + hash_ino = be32_to_cpu(AFFS_HEAD(bh)->table[offset]); + while (hash_ino) { + if (hash_ino == rem_ino) { + ino = AFFS_TAIL(sb, rem_bh)->hash_chain; + if (dir->i_ino == bh->b_blocknr) + AFFS_HEAD(bh)->table[offset] = ino; + else + AFFS_TAIL(sb, bh)->hash_chain = ino; + affs_adjust_checksum(bh, be32_to_cpu(ino) - hash_ino); + mark_buffer_dirty_inode(bh, dir); + AFFS_TAIL(sb, rem_bh)->parent = 0; + retval = 0; + break; + } + affs_brelse(bh); + bh = affs_bread(sb, hash_ino); + if (!bh) + return -EIO; + hash_ino = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain); + } + + affs_brelse(bh); + + dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; + dir->i_version++; + mark_inode_dirty(dir); + + return retval; +} + +static void +affs_fix_dcache(struct dentry *dentry, u32 entry_ino) +{ + struct inode *inode = dentry->d_inode; + void *data = dentry->d_fsdata; + struct list_head *head, *next; + + spin_lock(&dcache_lock); + head = &inode->i_dentry; + next = head->next; + while (next != head) { + dentry = list_entry(next, struct dentry, d_alias); + if (entry_ino == (u32)(long)dentry->d_fsdata) { + dentry->d_fsdata = data; + break; + } + next = next->next; + } + spin_unlock(&dcache_lock); +} + + +/* Remove header from link chain */ + +static int +affs_remove_link(struct dentry *dentry) +{ + struct inode *dir, *inode = dentry->d_inode; + struct super_block *sb = inode->i_sb; + struct buffer_head *bh = NULL, *link_bh = NULL; + u32 link_ino, ino; + int retval; + + pr_debug("AFFS: remove_link(key=%ld)\n", inode->i_ino); + retval = -EIO; + bh = affs_bread(sb, inode->i_ino); + if (!bh) + goto done; + + link_ino = (u32)(long)dentry->d_fsdata; + if (inode->i_ino == link_ino) { + /* we can't remove the head of the link, as its blocknr is still used as ino, + * so we remove the block of the first link instead. + */ + link_ino = be32_to_cpu(AFFS_TAIL(sb, bh)->link_chain); + link_bh = affs_bread(sb, link_ino); + if (!link_bh) + goto done; + + dir = iget(sb, be32_to_cpu(AFFS_TAIL(sb, link_bh)->parent)); + if (!dir) + goto done; + + affs_lock_dir(dir); + affs_fix_dcache(dentry, link_ino); + retval = affs_remove_hash(dir, link_bh); + if (retval) + goto done; + mark_buffer_dirty_inode(link_bh, inode); + + memcpy(AFFS_TAIL(sb, bh)->name, AFFS_TAIL(sb, link_bh)->name, 32); + retval = affs_insert_hash(dir, bh); + if (retval) + goto done; + mark_buffer_dirty_inode(bh, inode); + + affs_unlock_dir(dir); + iput(dir); + } else { + link_bh = affs_bread(sb, link_ino); + if (!link_bh) + goto done; + } + + while ((ino = be32_to_cpu(AFFS_TAIL(sb, bh)->link_chain)) != 0) { + if (ino == link_ino) { + __be32 ino2 = AFFS_TAIL(sb, link_bh)->link_chain; + AFFS_TAIL(sb, bh)->link_chain = ino2; + affs_adjust_checksum(bh, be32_to_cpu(ino2) - link_ino); + mark_buffer_dirty_inode(bh, inode); + retval = 0; + /* Fix the link count, if bh is a normal header block without links */ + switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) { + case ST_LINKDIR: + case ST_LINKFILE: + break; + default: + if (!AFFS_TAIL(sb, bh)->link_chain) + inode->i_nlink = 1; + } + affs_free_block(sb, link_ino); + goto done; + } + affs_brelse(bh); + bh = affs_bread(sb, ino); + if (!bh) + goto done; + } + retval = -ENOENT; +done: + affs_brelse(link_bh); + affs_brelse(bh); + return retval; +} + + +static int +affs_empty_dir(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + struct buffer_head *bh; + int retval, size; + + retval = -EIO; + bh = affs_bread(sb, inode->i_ino); + if (!bh) + goto done; + + retval = -ENOTEMPTY; + for (size = AFFS_SB(sb)->s_hashsize - 1; size >= 0; size--) + if (AFFS_HEAD(bh)->table[size]) + goto not_empty; + retval = 0; +not_empty: + affs_brelse(bh); +done: + return retval; +} + + +/* Remove a filesystem object. If the object to be removed has + * links to it, one of the links must be changed to inherit + * the file or directory. As above, any inode will do. + * The buffer will not be freed. If the header is a link, the + * block will be marked as free. + * This function returns a negative error number in case of + * an error, else 0 if the inode is to be deleted or 1 if not. + */ + +int +affs_remove_header(struct dentry *dentry) +{ + struct super_block *sb; + struct inode *inode, *dir; + struct buffer_head *bh = NULL; + int retval; + + dir = dentry->d_parent->d_inode; + sb = dir->i_sb; + + retval = -ENOENT; + inode = dentry->d_inode; + if (!inode) + goto done; + + pr_debug("AFFS: remove_header(key=%ld)\n", inode->i_ino); + retval = -EIO; + bh = affs_bread(sb, (u32)(long)dentry->d_fsdata); + if (!bh) + goto done; + + affs_lock_link(inode); + affs_lock_dir(dir); + switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) { + case ST_USERDIR: + /* if we ever want to support links to dirs + * i_hash_lock of the inode must only be + * taken after some checks + */ + affs_lock_dir(inode); + retval = affs_empty_dir(inode); + affs_unlock_dir(inode); + if (retval) + goto done_unlock; + break; + default: + break; + } + + retval = affs_remove_hash(dir, bh); + if (retval) + goto done_unlock; + mark_buffer_dirty_inode(bh, inode); + + affs_unlock_dir(dir); + + if (inode->i_nlink > 1) + retval = affs_remove_link(dentry); + else + inode->i_nlink = 0; + affs_unlock_link(inode); + inode->i_ctime = CURRENT_TIME_SEC; + mark_inode_dirty(inode); + +done: + affs_brelse(bh); + return retval; + +done_unlock: + affs_unlock_dir(dir); + affs_unlock_link(inode); + goto done; +} + +/* Checksum a block, do various consistency checks and optionally return + the blocks type number. DATA points to the block. If their pointers + are non-null, *PTYPE and *STYPE are set to the primary and secondary + block types respectively, *HASHSIZE is set to the size of the hashtable + (which lets us calculate the block size). + Returns non-zero if the block is not consistent. */ + +u32 +affs_checksum_block(struct super_block *sb, struct buffer_head *bh) +{ + __be32 *ptr = (__be32 *)bh->b_data; + u32 sum; + int bsize; + + sum = 0; + for (bsize = sb->s_blocksize / sizeof(__be32); bsize > 0; bsize--) + sum += be32_to_cpu(*ptr++); + return sum; +} + +/* + * Calculate the checksum of a disk block and store it + * at the indicated position. + */ + +void +affs_fix_checksum(struct super_block *sb, struct buffer_head *bh) +{ + int cnt = sb->s_blocksize / sizeof(__be32); + __be32 *ptr = (__be32 *)bh->b_data; + u32 checksum; + __be32 *checksumptr; + + checksumptr = ptr + 5; + *checksumptr = 0; + for (checksum = 0; cnt > 0; ptr++, cnt--) + checksum += be32_to_cpu(*ptr); + *checksumptr = cpu_to_be32(-checksum); +} + +void +secs_to_datestamp(time_t secs, struct affs_date *ds) +{ + u32 days; + u32 minute; + + secs -= sys_tz.tz_minuteswest * 60 + ((8 * 365 + 2) * 24 * 60 * 60); + if (secs < 0) + secs = 0; + days = secs / 86400; + secs -= days * 86400; + minute = secs / 60; + secs -= minute * 60; + + ds->days = cpu_to_be32(days); + ds->mins = cpu_to_be32(minute); + ds->ticks = cpu_to_be32(secs * 50); +} + +mode_t +prot_to_mode(u32 prot) +{ + int mode = 0; + + if (!(prot & FIBF_NOWRITE)) + mode |= S_IWUSR; + if (!(prot & FIBF_NOREAD)) + mode |= S_IRUSR; + if (!(prot & FIBF_NOEXECUTE)) + mode |= S_IXUSR; + if (prot & FIBF_GRP_WRITE) + mode |= S_IWGRP; + if (prot & FIBF_GRP_READ) + mode |= S_IRGRP; + if (prot & FIBF_GRP_EXECUTE) + mode |= S_IXGRP; + if (prot & FIBF_OTR_WRITE) + mode |= S_IWOTH; + if (prot & FIBF_OTR_READ) + mode |= S_IROTH; + if (prot & FIBF_OTR_EXECUTE) + mode |= S_IXOTH; + + return mode; +} + +void +mode_to_prot(struct inode *inode) +{ + u32 prot = AFFS_I(inode)->i_protect; + mode_t mode = inode->i_mode; + + if (!(mode & S_IXUSR)) + prot |= FIBF_NOEXECUTE; + if (!(mode & S_IRUSR)) + prot |= FIBF_NOREAD; + if (!(mode & S_IWUSR)) + prot |= FIBF_NOWRITE; + if (mode & S_IXGRP) + prot |= FIBF_GRP_EXECUTE; + if (mode & S_IRGRP) + prot |= FIBF_GRP_READ; + if (mode & S_IWGRP) + prot |= FIBF_GRP_WRITE; + if (mode & S_IXOTH) + prot |= FIBF_OTR_EXECUTE; + if (mode & S_IROTH) + prot |= FIBF_OTR_READ; + if (mode & S_IWOTH) + prot |= FIBF_OTR_WRITE; + + AFFS_I(inode)->i_protect = prot; +} + +void +affs_error(struct super_block *sb, const char *function, const char *fmt, ...) +{ + va_list args; + + va_start(args,fmt); + vsprintf(ErrorBuffer,fmt,args); + va_end(args); + + printk(KERN_CRIT "AFFS error (device %s): %s(): %s\n", sb->s_id, + function,ErrorBuffer); + if (!(sb->s_flags & MS_RDONLY)) + printk(KERN_WARNING "AFFS: Remounting filesystem read-only\n"); + sb->s_flags |= MS_RDONLY; +} + +void +affs_warning(struct super_block *sb, const char *function, const char *fmt, ...) +{ + va_list args; + + va_start(args,fmt); + vsprintf(ErrorBuffer,fmt,args); + va_end(args); + + printk(KERN_WARNING "AFFS warning (device %s): %s(): %s\n", sb->s_id, + function,ErrorBuffer); +} + +/* Check if the name is valid for a affs object. */ + +int +affs_check_name(const unsigned char *name, int len) +{ + int i; + + if (len > 30) +#ifdef AFFS_NO_TRUNCATE + return -ENAMETOOLONG; +#else + len = 30; +#endif + + for (i = 0; i < len; i++) { + if (name[i] < ' ' || name[i] == ':' + || (name[i] > 0x7e && name[i] < 0xa0)) + return -EINVAL; + } + + return 0; +} + +/* This function copies name to bstr, with at most 30 + * characters length. The bstr will be prepended by + * a length byte. + * NOTE: The name will must be already checked by + * affs_check_name()! + */ + +int +affs_copy_name(unsigned char *bstr, struct dentry *dentry) +{ + int len = min(dentry->d_name.len, 30u); + + *bstr++ = len; + memcpy(bstr, dentry->d_name.name, len); + return len; +} diff --git a/fs/affs/bitmap.c b/fs/affs/bitmap.c new file mode 100644 index 00000000000..b0b953683c1 --- /dev/null +++ b/fs/affs/bitmap.c @@ -0,0 +1,390 @@ +/* + * linux/fs/affs/bitmap.c + * + * (c) 1996 Hans-Joachim Widmaier + * + * bitmap.c contains the code that handles all bitmap related stuff - + * block allocation, deallocation, calculation of free space. + */ + +#include "affs.h" + +/* This is, of course, shamelessly stolen from fs/minix */ + +static int nibblemap[] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 }; + +static u32 +affs_count_free_bits(u32 blocksize, const void *data) +{ + const u32 *map; + u32 free; + u32 tmp; + + map = data; + free = 0; + for (blocksize /= 4; blocksize > 0; blocksize--) { + tmp = *map++; + while (tmp) { + free += nibblemap[tmp & 0xf]; + tmp >>= 4; + } + } + + return free; +} + +u32 +affs_count_free_blocks(struct super_block *sb) +{ + struct affs_bm_info *bm; + u32 free; + int i; + + pr_debug("AFFS: count_free_blocks()\n"); + + if (sb->s_flags & MS_RDONLY) + return 0; + + down(&AFFS_SB(sb)->s_bmlock); + + bm = AFFS_SB(sb)->s_bitmap; + free = 0; + for (i = AFFS_SB(sb)->s_bmap_count; i > 0; bm++, i--) + free += bm->bm_free; + + up(&AFFS_SB(sb)->s_bmlock); + + return free; +} + +void +affs_free_block(struct super_block *sb, u32 block) +{ + struct affs_sb_info *sbi = AFFS_SB(sb); + struct affs_bm_info *bm; + struct buffer_head *bh; + u32 blk, bmap, bit, mask, tmp; + __be32 *data; + + pr_debug("AFFS: free_block(%u)\n", block); + + if (block > sbi->s_partition_size) + goto err_range; + + blk = block - sbi->s_reserved; + bmap = blk / sbi->s_bmap_bits; + bit = blk % sbi->s_bmap_bits; + bm = &sbi->s_bitmap[bmap]; + + down(&sbi->s_bmlock); + + bh = sbi->s_bmap_bh; + if (sbi->s_last_bmap != bmap) { + affs_brelse(bh); + bh = affs_bread(sb, bm->bm_key); + if (!bh) + goto err_bh_read; + sbi->s_bmap_bh = bh; + sbi->s_last_bmap = bmap; + } + + mask = 1 << (bit & 31); + data = (__be32 *)bh->b_data + bit / 32 + 1; + + /* mark block free */ + tmp = be32_to_cpu(*data); + if (tmp & mask) + goto err_free; + *data = cpu_to_be32(tmp | mask); + + /* fix checksum */ + tmp = be32_to_cpu(*(__be32 *)bh->b_data); + *(__be32 *)bh->b_data = cpu_to_be32(tmp - mask); + + mark_buffer_dirty(bh); + sb->s_dirt = 1; + bm->bm_free++; + + up(&sbi->s_bmlock); + return; + +err_free: + affs_warning(sb,"affs_free_block","Trying to free block %u which is already free", block); + up(&sbi->s_bmlock); + return; + +err_bh_read: + affs_error(sb,"affs_free_block","Cannot read bitmap block %u", bm->bm_key); + sbi->s_bmap_bh = NULL; + sbi->s_last_bmap = ~0; + up(&sbi->s_bmlock); + return; + +err_range: + affs_error(sb, "affs_free_block","Block %u outside partition", block); + return; +} + +/* + * Allocate a block in the given allocation zone. + * Since we have to byte-swap the bitmap on little-endian + * machines, this is rather expensive. Therefor we will + * preallocate up to 16 blocks from the same word, if + * possible. We are not doing preallocations in the + * header zone, though. + */ + +u32 +affs_alloc_block(struct inode *inode, u32 goal) +{ + struct super_block *sb; + struct affs_sb_info *sbi; + struct affs_bm_info *bm; + struct buffer_head *bh; + __be32 *data, *enddata; + u32 blk, bmap, bit, mask, mask2, tmp; + int i; + + sb = inode->i_sb; + sbi = AFFS_SB(sb); + + pr_debug("AFFS: balloc(inode=%lu,goal=%u): ", inode->i_ino, goal); + + if (AFFS_I(inode)->i_pa_cnt) { + pr_debug("%d\n", AFFS_I(inode)->i_lastalloc+1); + AFFS_I(inode)->i_pa_cnt--; + return ++AFFS_I(inode)->i_lastalloc; + } + + if (!goal || goal > sbi->s_partition_size) { + if (goal) + affs_warning(sb, "affs_balloc", "invalid goal %d", goal); + //if (!AFFS_I(inode)->i_last_block) + // affs_warning(sb, "affs_balloc", "no last alloc block"); + goal = sbi->s_reserved; + } + + blk = goal - sbi->s_reserved; + bmap = blk / sbi->s_bmap_bits; + bm = &sbi->s_bitmap[bmap]; + + down(&sbi->s_bmlock); + + if (bm->bm_free) + goto find_bmap_bit; + +find_bmap: + /* search for the next bmap buffer with free bits */ + i = sbi->s_bmap_count; + do { + if (--i < 0) + goto err_full; + bmap++; + bm++; + if (bmap < sbi->s_bmap_count) + continue; + /* restart search at zero */ + bmap = 0; + bm = sbi->s_bitmap; + } while (!bm->bm_free); + blk = bmap * sbi->s_bmap_bits; + +find_bmap_bit: + + bh = sbi->s_bmap_bh; + if (sbi->s_last_bmap != bmap) { + affs_brelse(bh); + bh = affs_bread(sb, bm->bm_key); + if (!bh) + goto err_bh_read; + sbi->s_bmap_bh = bh; + sbi->s_last_bmap = bmap; + } + + /* find an unused block in this bitmap block */ + bit = blk % sbi->s_bmap_bits; + data = (__be32 *)bh->b_data + bit / 32 + 1; + enddata = (__be32 *)((u8 *)bh->b_data + sb->s_blocksize); + mask = ~0UL << (bit & 31); + blk &= ~31UL; + + tmp = be32_to_cpu(*data); + if (tmp & mask) + goto find_bit; + + /* scan the rest of the buffer */ + do { + blk += 32; + if (++data >= enddata) + /* didn't find something, can only happen + * if scan didn't start at 0, try next bmap + */ + goto find_bmap; + } while (!*data); + tmp = be32_to_cpu(*data); + mask = ~0; + +find_bit: + /* finally look for a free bit in the word */ + bit = ffs(tmp & mask) - 1; + blk += bit + sbi->s_reserved; + mask2 = mask = 1 << (bit & 31); + AFFS_I(inode)->i_lastalloc = blk; + + /* prealloc as much as possible within this word */ + while ((mask2 <<= 1)) { + if (!(tmp & mask2)) + break; + AFFS_I(inode)->i_pa_cnt++; + mask |= mask2; + } + bm->bm_free -= AFFS_I(inode)->i_pa_cnt + 1; + + *data = cpu_to_be32(tmp & ~mask); + + /* fix checksum */ + tmp = be32_to_cpu(*(__be32 *)bh->b_data); + *(__be32 *)bh->b_data = cpu_to_be32(tmp + mask); + + mark_buffer_dirty(bh); + sb->s_dirt = 1; + + up(&sbi->s_bmlock); + + pr_debug("%d\n", blk); + return blk; + +err_bh_read: + affs_error(sb,"affs_read_block","Cannot read bitmap block %u", bm->bm_key); + sbi->s_bmap_bh = NULL; + sbi->s_last_bmap = ~0; +err_full: + up(&sbi->s_bmlock); + pr_debug("failed\n"); + return 0; +} + +int affs_init_bitmap(struct super_block *sb, int *flags) +{ + struct affs_bm_info *bm; + struct buffer_head *bmap_bh = NULL, *bh = NULL; + __be32 *bmap_blk; + u32 size, blk, end, offset, mask; + int i, res = 0; + struct affs_sb_info *sbi = AFFS_SB(sb); + + if (*flags & MS_RDONLY) + return 0; + + if (!AFFS_ROOT_TAIL(sb, sbi->s_root_bh)->bm_flag) { + printk(KERN_NOTICE "AFFS: Bitmap invalid - mounting %s read only\n", + sb->s_id); + *flags |= MS_RDONLY; + return 0; + } + + sbi->s_last_bmap = ~0; + sbi->s_bmap_bh = NULL; + sbi->s_bmap_bits = sb->s_blocksize * 8 - 32; + sbi->s_bmap_count = (sbi->s_partition_size - sbi->s_reserved + + sbi->s_bmap_bits - 1) / sbi->s_bmap_bits; + size = sbi->s_bmap_count * sizeof(*bm); + bm = sbi->s_bitmap = kmalloc(size, GFP_KERNEL); + if (!sbi->s_bitmap) { + printk(KERN_ERR "AFFS: Bitmap allocation failed\n"); + return -ENOMEM; + } + memset(sbi->s_bitmap, 0, size); + + bmap_blk = (__be32 *)sbi->s_root_bh->b_data; + blk = sb->s_blocksize / 4 - 49; + end = blk + 25; + + for (i = sbi->s_bmap_count; i > 0; bm++, i--) { + affs_brelse(bh); + + bm->bm_key = be32_to_cpu(bmap_blk[blk]); + bh = affs_bread(sb, bm->bm_key); + if (!bh) { + printk(KERN_ERR "AFFS: Cannot read bitmap\n"); + res = -EIO; + goto out; + } + if (affs_checksum_block(sb, bh)) { + printk(KERN_WARNING "AFFS: Bitmap %u invalid - mounting %s read only.\n", + bm->bm_key, sb->s_id); + *flags |= MS_RDONLY; + goto out; + } + pr_debug("AFFS: read bitmap block %d: %d\n", blk, bm->bm_key); + bm->bm_free = affs_count_free_bits(sb->s_blocksize - 4, bh->b_data + 4); + + /* Don't try read the extension if this is the last block, + * but we also need the right bm pointer below + */ + if (++blk < end || i == 1) + continue; + if (bmap_bh) + affs_brelse(bmap_bh); + bmap_bh = affs_bread(sb, be32_to_cpu(bmap_blk[blk])); + if (!bmap_bh) { + printk(KERN_ERR "AFFS: Cannot read bitmap extension\n"); + res = -EIO; + goto out; + } + bmap_blk = (__be32 *)bmap_bh->b_data; + blk = 0; + end = sb->s_blocksize / 4 - 1; + } + + offset = (sbi->s_partition_size - sbi->s_reserved) % sbi->s_bmap_bits; + mask = ~(0xFFFFFFFFU << (offset & 31)); + pr_debug("last word: %d %d %d\n", offset, offset / 32 + 1, mask); + offset = offset / 32 + 1; + + if (mask) { + u32 old, new; + + /* Mark unused bits in the last word as allocated */ + old = be32_to_cpu(((__be32 *)bh->b_data)[offset]); + new = old & mask; + //if (old != new) { + ((__be32 *)bh->b_data)[offset] = cpu_to_be32(new); + /* fix checksum */ + //new -= old; + //old = be32_to_cpu(*(__be32 *)bh->b_data); + //*(__be32 *)bh->b_data = cpu_to_be32(old - new); + //mark_buffer_dirty(bh); + //} + /* correct offset for the bitmap count below */ + //offset++; + } + while (++offset < sb->s_blocksize / 4) + ((__be32 *)bh->b_data)[offset] = 0; + ((__be32 *)bh->b_data)[0] = 0; + ((__be32 *)bh->b_data)[0] = cpu_to_be32(-affs_checksum_block(sb, bh)); + mark_buffer_dirty(bh); + + /* recalculate bitmap count for last block */ + bm--; + bm->bm_free = affs_count_free_bits(sb->s_blocksize - 4, bh->b_data + 4); + +out: + affs_brelse(bh); + affs_brelse(bmap_bh); + return res; +} + +void affs_free_bitmap(struct super_block *sb) +{ + struct affs_sb_info *sbi = AFFS_SB(sb); + + if (!sbi->s_bitmap) + return; + + affs_brelse(sbi->s_bmap_bh); + sbi->s_bmap_bh = NULL; + sbi->s_last_bmap = ~0; + kfree(sbi->s_bitmap); + sbi->s_bitmap = NULL; +} diff --git a/fs/affs/dir.c b/fs/affs/dir.c new file mode 100644 index 00000000000..548efd0ee98 --- /dev/null +++ b/fs/affs/dir.c @@ -0,0 +1,155 @@ +/* + * linux/fs/affs/dir.c + * + * (c) 1996 Hans-Joachim Widmaier - Rewritten + * + * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem. + * + * (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem. + * + * (C) 1991 Linus Torvalds - minix filesystem + * + * affs directory handling functions + * + */ + +#include "affs.h" + +static int affs_readdir(struct file *, void *, filldir_t); + +struct file_operations affs_dir_operations = { + .read = generic_read_dir, + .readdir = affs_readdir, + .fsync = file_fsync, +}; + +/* + * directories can handle most operations... + */ +struct inode_operations affs_dir_inode_operations = { + .create = affs_create, + .lookup = affs_lookup, + .link = affs_link, + .unlink = affs_unlink, + .symlink = affs_symlink, + .mkdir = affs_mkdir, + .rmdir = affs_rmdir, + .rename = affs_rename, + .setattr = affs_notify_change, +}; + +static int +affs_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct inode *inode = filp->f_dentry->d_inode; + struct super_block *sb = inode->i_sb; + struct buffer_head *dir_bh; + struct buffer_head *fh_bh; + unsigned char *name; + int namelen; + u32 i; + int hash_pos; + int chain_pos; + u32 f_pos; + u32 ino; + int stored; + int res; + + pr_debug("AFFS: readdir(ino=%lu,f_pos=%lx)\n",inode->i_ino,(unsigned long)filp->f_pos); + + stored = 0; + res = -EIO; + dir_bh = NULL; + fh_bh = NULL; + f_pos = filp->f_pos; + + if (f_pos == 0) { + filp->private_data = (void *)0; + if (filldir(dirent, ".", 1, f_pos, inode->i_ino, DT_DIR) < 0) + return 0; + filp->f_pos = f_pos = 1; + stored++; + } + if (f_pos == 1) { + if (filldir(dirent, "..", 2, f_pos, parent_ino(filp->f_dentry), DT_DIR) < 0) + return stored; + filp->f_pos = f_pos = 2; + stored++; + } + + affs_lock_dir(inode); + chain_pos = (f_pos - 2) & 0xffff; + hash_pos = (f_pos - 2) >> 16; + if (chain_pos == 0xffff) { + affs_warning(sb, "readdir", "More than 65535 entries in chain"); + chain_pos = 0; + hash_pos++; + filp->f_pos = ((hash_pos << 16) | chain_pos) + 2; + } + dir_bh = affs_bread(sb, inode->i_ino); + if (!dir_bh) + goto readdir_out; + + /* If the directory hasn't changed since the last call to readdir(), + * we can jump directly to where we left off. + */ + ino = (u32)(long)filp->private_data; + if (ino && filp->f_version == inode->i_version) { + pr_debug("AFFS: readdir() left off=%d\n", ino); + goto inside; + } + + ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[hash_pos]); + for (i = 0; ino && i < chain_pos; i++) { + fh_bh = affs_bread(sb, ino); + if (!fh_bh) { + affs_error(sb, "readdir","Cannot read block %d", i); + goto readdir_out; + } + ino = be32_to_cpu(AFFS_TAIL(sb, fh_bh)->hash_chain); + affs_brelse(fh_bh); + fh_bh = NULL; + } + if (ino) + goto inside; + hash_pos++; + + for (; hash_pos < AFFS_SB(sb)->s_hashsize; hash_pos++) { + ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[hash_pos]); + if (!ino) + continue; + f_pos = (hash_pos << 16) + 2; +inside: + do { + fh_bh = affs_bread(sb, ino); + if (!fh_bh) { + affs_error(sb, "readdir","Cannot read block %d", ino); + goto readdir_done; + } + + namelen = min(AFFS_TAIL(sb, fh_bh)->name[0], (u8)30); + name = AFFS_TAIL(sb, fh_bh)->name + 1; + pr_debug("AFFS: readdir(): filldir(\"%.*s\", ino=%u), hash=%d, f_pos=%x\n", + namelen, name, ino, hash_pos, f_pos); + if (filldir(dirent, name, namelen, f_pos, ino, DT_UNKNOWN) < 0) + goto readdir_done; + stored++; + f_pos++; + ino = be32_to_cpu(AFFS_TAIL(sb, fh_bh)->hash_chain); + affs_brelse(fh_bh); + fh_bh = NULL; + } while (ino); + } +readdir_done: + filp->f_pos = f_pos; + filp->f_version = inode->i_version; + filp->private_data = (void *)(long)ino; + res = stored; + +readdir_out: + affs_brelse(dir_bh); + affs_brelse(fh_bh); + affs_unlock_dir(inode); + pr_debug("AFFS: readdir()=%d\n", stored); + return res; +} diff --git a/fs/affs/file.c b/fs/affs/file.c new file mode 100644 index 00000000000..6744924b690 --- /dev/null +++ b/fs/affs/file.c @@ -0,0 +1,920 @@ +/* + * linux/fs/affs/file.c + * + * (c) 1996 Hans-Joachim Widmaier - Rewritten + * + * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem. + * + * (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem. + * + * (C) 1991 Linus Torvalds - minix filesystem + * + * affs regular file handling primitives + */ + +#include "affs.h" + +#if PAGE_SIZE < 4096 +#error PAGE_SIZE must be at least 4096 +#endif + +static int affs_grow_extcache(struct inode *inode, u32 lc_idx); +static struct buffer_head *affs_alloc_extblock(struct inode *inode, struct buffer_head *bh, u32 ext); +static inline struct buffer_head *affs_get_extblock(struct inode *inode, u32 ext); +static struct buffer_head *affs_get_extblock_slow(struct inode *inode, u32 ext); +static ssize_t affs_file_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos); +static int affs_file_open(struct inode *inode, struct file *filp); +static int affs_file_release(struct inode *inode, struct file *filp); + +struct file_operations affs_file_operations = { + .llseek = generic_file_llseek, + .read = generic_file_read, + .write = affs_file_write, + .mmap = generic_file_mmap, + .open = affs_file_open, + .release = affs_file_release, + .fsync = file_fsync, + .sendfile = generic_file_sendfile, +}; + +struct inode_operations affs_file_inode_operations = { + .truncate = affs_truncate, + .setattr = affs_notify_change, +}; + +static int +affs_file_open(struct inode *inode, struct file *filp) +{ + if (atomic_read(&filp->f_count) != 1) + return 0; + pr_debug("AFFS: open(%d)\n", AFFS_I(inode)->i_opencnt); + AFFS_I(inode)->i_opencnt++; + return 0; +} + +static int +affs_file_release(struct inode *inode, struct file *filp) +{ + if (atomic_read(&filp->f_count) != 0) + return 0; + pr_debug("AFFS: release(%d)\n", AFFS_I(inode)->i_opencnt); + AFFS_I(inode)->i_opencnt--; + if (!AFFS_I(inode)->i_opencnt) + affs_free_prealloc(inode); + + return 0; +} + +static int +affs_grow_extcache(struct inode *inode, u32 lc_idx) +{ + struct super_block *sb = inode->i_sb; + struct buffer_head *bh; + u32 lc_max; + int i, j, key; + + if (!AFFS_I(inode)->i_lc) { + char *ptr = (char *)get_zeroed_page(GFP_NOFS); + if (!ptr) + return -ENOMEM; + AFFS_I(inode)->i_lc = (u32 *)ptr; + AFFS_I(inode)->i_ac = (struct affs_ext_key *)(ptr + AFFS_CACHE_SIZE / 2); + } + + lc_max = AFFS_LC_SIZE << AFFS_I(inode)->i_lc_shift; + + if (AFFS_I(inode)->i_extcnt > lc_max) { + u32 lc_shift, lc_mask, tmp, off; + + /* need to recalculate linear cache, start from old size */ + lc_shift = AFFS_I(inode)->i_lc_shift; + tmp = (AFFS_I(inode)->i_extcnt / AFFS_LC_SIZE) >> lc_shift; + for (; tmp; tmp >>= 1) + lc_shift++; + lc_mask = (1 << lc_shift) - 1; + + /* fix idx and old size to new shift */ + lc_idx >>= (lc_shift - AFFS_I(inode)->i_lc_shift); + AFFS_I(inode)->i_lc_size >>= (lc_shift - AFFS_I(inode)->i_lc_shift); + + /* first shrink old cache to make more space */ + off = 1 << (lc_shift - AFFS_I(inode)->i_lc_shift); + for (i = 1, j = off; j < AFFS_LC_SIZE; i++, j += off) + AFFS_I(inode)->i_ac[i] = AFFS_I(inode)->i_ac[j]; + + AFFS_I(inode)->i_lc_shift = lc_shift; + AFFS_I(inode)->i_lc_mask = lc_mask; + } + + /* fill cache to the needed index */ + i = AFFS_I(inode)->i_lc_size; + AFFS_I(inode)->i_lc_size = lc_idx + 1; + for (; i <= lc_idx; i++) { + if (!i) { + AFFS_I(inode)->i_lc[0] = inode->i_ino; + continue; + } + key = AFFS_I(inode)->i_lc[i - 1]; + j = AFFS_I(inode)->i_lc_mask + 1; + // unlock cache + for (; j > 0; j--) { + bh = affs_bread(sb, key); + if (!bh) + goto err; + key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension); + affs_brelse(bh); + } + // lock cache + AFFS_I(inode)->i_lc[i] = key; + } + + return 0; + +err: + // lock cache + return -EIO; +} + +static struct buffer_head * +affs_alloc_extblock(struct inode *inode, struct buffer_head *bh, u32 ext) +{ + struct super_block *sb = inode->i_sb; + struct buffer_head *new_bh; + u32 blocknr, tmp; + + blocknr = affs_alloc_block(inode, bh->b_blocknr); + if (!blocknr) + return ERR_PTR(-ENOSPC); + + new_bh = affs_getzeroblk(sb, blocknr); + if (!new_bh) { + affs_free_block(sb, blocknr); + return ERR_PTR(-EIO); + } + + AFFS_HEAD(new_bh)->ptype = cpu_to_be32(T_LIST); + AFFS_HEAD(new_bh)->key = cpu_to_be32(blocknr); + AFFS_TAIL(sb, new_bh)->stype = cpu_to_be32(ST_FILE); + AFFS_TAIL(sb, new_bh)->parent = cpu_to_be32(inode->i_ino); + affs_fix_checksum(sb, new_bh); + + mark_buffer_dirty_inode(new_bh, inode); + + tmp = be32_to_cpu(AFFS_TAIL(sb, bh)->extension); + if (tmp) + affs_warning(sb, "alloc_ext", "previous extension set (%x)", tmp); + AFFS_TAIL(sb, bh)->extension = cpu_to_be32(blocknr); + affs_adjust_checksum(bh, blocknr - tmp); + mark_buffer_dirty_inode(bh, inode); + + AFFS_I(inode)->i_extcnt++; + mark_inode_dirty(inode); + + return new_bh; +} + +static inline struct buffer_head * +affs_get_extblock(struct inode *inode, u32 ext) +{ + /* inline the simplest case: same extended block as last time */ + struct buffer_head *bh = AFFS_I(inode)->i_ext_bh; + if (ext == AFFS_I(inode)->i_ext_last) + atomic_inc(&bh->b_count); + else + /* we have to do more (not inlined) */ + bh = affs_get_extblock_slow(inode, ext); + + return bh; +} + +static struct buffer_head * +affs_get_extblock_slow(struct inode *inode, u32 ext) +{ + struct super_block *sb = inode->i_sb; + struct buffer_head *bh; + u32 ext_key; + u32 lc_idx, lc_off, ac_idx; + u32 tmp, idx; + + if (ext == AFFS_I(inode)->i_ext_last + 1) { + /* read the next extended block from the current one */ + bh = AFFS_I(inode)->i_ext_bh; + ext_key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension); + if (ext < AFFS_I(inode)->i_extcnt) + goto read_ext; + if (ext > AFFS_I(inode)->i_extcnt) + BUG(); + bh = affs_alloc_extblock(inode, bh, ext); + if (IS_ERR(bh)) + return bh; + goto store_ext; + } + + if (ext == 0) { + /* we seek back to the file header block */ + ext_key = inode->i_ino; + goto read_ext; + } + + if (ext >= AFFS_I(inode)->i_extcnt) { + struct buffer_head *prev_bh; + + /* allocate a new extended block */ + if (ext > AFFS_I(inode)->i_extcnt) + BUG(); + + /* get previous extended block */ + prev_bh = affs_get_extblock(inode, ext - 1); + if (IS_ERR(prev_bh)) + return prev_bh; + bh = affs_alloc_extblock(inode, prev_bh, ext); + affs_brelse(prev_bh); + if (IS_ERR(bh)) + return bh; + goto store_ext; + } + +again: + /* check if there is an extended cache and whether it's large enough */ + lc_idx = ext >> AFFS_I(inode)->i_lc_shift; + lc_off = ext & AFFS_I(inode)->i_lc_mask; + + if (lc_idx >= AFFS_I(inode)->i_lc_size) { + int err; + + err = affs_grow_extcache(inode, lc_idx); + if (err) + return ERR_PTR(err); + goto again; + } + + /* every n'th key we find in the linear cache */ + if (!lc_off) { + ext_key = AFFS_I(inode)->i_lc[lc_idx]; + goto read_ext; + } + + /* maybe it's still in the associative cache */ + ac_idx = (ext - lc_idx - 1) & AFFS_AC_MASK; + if (AFFS_I(inode)->i_ac[ac_idx].ext == ext) { + ext_key = AFFS_I(inode)->i_ac[ac_idx].key; + goto read_ext; + } + + /* try to find one of the previous extended blocks */ + tmp = ext; + idx = ac_idx; + while (--tmp, --lc_off > 0) { + idx = (idx - 1) & AFFS_AC_MASK; + if (AFFS_I(inode)->i_ac[idx].ext == tmp) { + ext_key = AFFS_I(inode)->i_ac[idx].key; + goto find_ext; + } + } + + /* fall back to the linear cache */ + ext_key = AFFS_I(inode)->i_lc[lc_idx]; +find_ext: + /* read all extended blocks until we find the one we need */ + //unlock cache + do { + bh = affs_bread(sb, ext_key); + if (!bh) + goto err_bread; + ext_key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension); + affs_brelse(bh); + tmp++; + } while (tmp < ext); + //lock cache + + /* store it in the associative cache */ + // recalculate ac_idx? + AFFS_I(inode)->i_ac[ac_idx].ext = ext; + AFFS_I(inode)->i_ac[ac_idx].key = ext_key; + +read_ext: + /* finally read the right extended block */ + //unlock cache + bh = affs_bread(sb, ext_key); + if (!bh) + goto err_bread; + //lock cache + +store_ext: + /* release old cached extended block and store the new one */ + affs_brelse(AFFS_I(inode)->i_ext_bh); + AFFS_I(inode)->i_ext_last = ext; + AFFS_I(inode)->i_ext_bh = bh; + atomic_inc(&bh->b_count); + + return bh; + +err_bread: + affs_brelse(bh); + return ERR_PTR(-EIO); +} + +static int +affs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_result, int create) +{ + struct super_block *sb = inode->i_sb; + struct buffer_head *ext_bh; + u32 ext; + + pr_debug("AFFS: get_block(%u, %lu)\n", (u32)inode->i_ino, (unsigned long)block); + + + if (block > (sector_t)0x7fffffffUL) + BUG(); + + if (block >= AFFS_I(inode)->i_blkcnt) { + if (block > AFFS_I(inode)->i_blkcnt || !create) + goto err_big; + } else + create = 0; + + //lock cache + affs_lock_ext(inode); + + ext = (u32)block / AFFS_SB(sb)->s_hashsize; + block -= ext * AFFS_SB(sb)->s_hashsize; + ext_bh = affs_get_extblock(inode, ext); + if (IS_ERR(ext_bh)) + goto err_ext; + map_bh(bh_result, sb, (sector_t)be32_to_cpu(AFFS_BLOCK(sb, ext_bh, block))); + + if (create) { + u32 blocknr = affs_alloc_block(inode, ext_bh->b_blocknr); + if (!blocknr) + goto err_alloc; + set_buffer_new(bh_result); + AFFS_I(inode)->mmu_private += AFFS_SB(sb)->s_data_blksize; + AFFS_I(inode)->i_blkcnt++; + + /* store new block */ + if (bh_result->b_blocknr) + affs_warning(sb, "get_block", "block already set (%x)", bh_result->b_blocknr); + AFFS_BLOCK(sb, ext_bh, block) = cpu_to_be32(blocknr); + AFFS_HEAD(ext_bh)->block_count = cpu_to_be32(block + 1); + affs_adjust_checksum(ext_bh, blocknr - bh_result->b_blocknr + 1); + bh_result->b_blocknr = blocknr; + + if (!block) { + /* insert first block into header block */ + u32 tmp = be32_to_cpu(AFFS_HEAD(ext_bh)->first_data); + if (tmp) + affs_warning(sb, "get_block", "first block already set (%d)", tmp); + AFFS_HEAD(ext_bh)->first_data = cpu_to_be32(blocknr); + affs_adjust_checksum(ext_bh, blocknr - tmp); + } + } + + affs_brelse(ext_bh); + //unlock cache + affs_unlock_ext(inode); + return 0; + +err_big: + affs_error(inode->i_sb,"get_block","strange block request %d", block); + return -EIO; +err_ext: + // unlock cache + affs_unlock_ext(inode); + return PTR_ERR(ext_bh); +err_alloc: + brelse(ext_bh); + clear_buffer_mapped(bh_result); + bh_result->b_bdev = NULL; + // unlock cache + affs_unlock_ext(inode); + return -ENOSPC; +} + +static int affs_writepage(struct page *page, struct writeback_control *wbc) +{ + return block_write_full_page(page, affs_get_block, wbc); +} +static int affs_readpage(struct file *file, struct page *page) +{ + return block_read_full_page(page, affs_get_block); +} +static int affs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) +{ + return cont_prepare_write(page, from, to, affs_get_block, + &AFFS_I(page->mapping->host)->mmu_private); +} +static sector_t _affs_bmap(struct address_space *mapping, sector_t block) +{ + return generic_block_bmap(mapping,block,affs_get_block); +} +struct address_space_operations affs_aops = { + .readpage = affs_readpage, + .writepage = affs_writepage, + .sync_page = block_sync_page, + .prepare_write = affs_prepare_write, + .commit_write = generic_commit_write, + .bmap = _affs_bmap +}; + +static inline struct buffer_head * +affs_bread_ino(struct inode *inode, int block, int create) +{ + struct buffer_head *bh, tmp_bh; + int err; + + tmp_bh.b_state = 0; + err = affs_get_block(inode, block, &tmp_bh, create); + if (!err) { + bh = affs_bread(inode->i_sb, tmp_bh.b_blocknr); + if (bh) { + bh->b_state |= tmp_bh.b_state; + return bh; + } + err = -EIO; + } + return ERR_PTR(err); +} + +static inline struct buffer_head * +affs_getzeroblk_ino(struct inode *inode, int block) +{ + struct buffer_head *bh, tmp_bh; + int err; + + tmp_bh.b_state = 0; + err = affs_get_block(inode, block, &tmp_bh, 1); + if (!err) { + bh = affs_getzeroblk(inode->i_sb, tmp_bh.b_blocknr); + if (bh) { + bh->b_state |= tmp_bh.b_state; + return bh; + } + err = -EIO; + } + return ERR_PTR(err); +} + +static inline struct buffer_head * +affs_getemptyblk_ino(struct inode *inode, int block) +{ + struct buffer_head *bh, tmp_bh; + int err; + + tmp_bh.b_state = 0; + err = affs_get_block(inode, block, &tmp_bh, 1); + if (!err) { + bh = affs_getemptyblk(inode->i_sb, tmp_bh.b_blocknr); + if (bh) { + bh->b_state |= tmp_bh.b_state; + return bh; + } + err = -EIO; + } + return ERR_PTR(err); +} + +static ssize_t +affs_file_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t retval; + + retval = generic_file_write (file, buf, count, ppos); + if (retval >0) { + struct inode *inode = file->f_dentry->d_inode; + inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; + mark_inode_dirty(inode); + } + return retval; +} + +static int +affs_do_readpage_ofs(struct file *file, struct page *page, unsigned from, unsigned to) +{ + struct inode *inode = page->mapping->host; + struct super_block *sb = inode->i_sb; + struct buffer_head *bh; + char *data; + u32 bidx, boff, bsize; + u32 tmp; + + pr_debug("AFFS: read_page(%u, %ld, %d, %d)\n", (u32)inode->i_ino, page->index, from, to); + if (from > to || to > PAGE_CACHE_SIZE) + BUG(); + kmap(page); + data = page_address(page); + bsize = AFFS_SB(sb)->s_data_blksize; + tmp = (page->index << PAGE_CACHE_SHIFT) + from; + bidx = tmp / bsize; + boff = tmp % bsize; + + while (from < to) { + bh = affs_bread_ino(inode, bidx, 0); + if (IS_ERR(bh)) + return PTR_ERR(bh); + tmp = min(bsize - boff, to - from); + if (from + tmp > to || tmp > bsize) + BUG(); + memcpy(data + from, AFFS_DATA(bh) + boff, tmp); + affs_brelse(bh); + bidx++; + from += tmp; + boff = 0; + } + flush_dcache_page(page); + kunmap(page); + return 0; +} + +static int +affs_extent_file_ofs(struct inode *inode, u32 newsize) +{ + struct super_block *sb = inode->i_sb; + struct buffer_head *bh, *prev_bh; + u32 bidx, boff; + u32 size, bsize; + u32 tmp; + + pr_debug("AFFS: extent_file(%u, %d)\n", (u32)inode->i_ino, newsize); + bsize = AFFS_SB(sb)->s_data_blksize; + bh = NULL; + size = AFFS_I(inode)->mmu_private; + bidx = size / bsize; + boff = size % bsize; + if (boff) { + bh = affs_bread_ino(inode, bidx, 0); + if (IS_ERR(bh)) + return PTR_ERR(bh); + tmp = min(bsize - boff, newsize - size); + if (boff + tmp > bsize || tmp > bsize) + BUG(); + memset(AFFS_DATA(bh) + boff, 0, tmp); + AFFS_DATA_HEAD(bh)->size = cpu_to_be32(be32_to_cpu(AFFS_DATA_HEAD(bh)->size) + tmp); + affs_fix_checksum(sb, bh); + mark_buffer_dirty_inode(bh, inode); + size += tmp; + bidx++; + } else if (bidx) { + bh = affs_bread_ino(inode, bidx - 1, 0); + if (IS_ERR(bh)) + return PTR_ERR(bh); + } + + while (size < newsize) { + prev_bh = bh; + bh = affs_getzeroblk_ino(inode, bidx); + if (IS_ERR(bh)) + goto out; + tmp = min(bsize, newsize - size); + if (tmp > bsize) + BUG(); + AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA); + AFFS_DATA_HEAD(bh)->key = cpu_to_be32(inode->i_ino); + AFFS_DATA_HEAD(bh)->sequence = cpu_to_be32(bidx); + AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp); + affs_fix_checksum(sb, bh); + bh->b_state &= ~(1UL << BH_New); + mark_buffer_dirty_inode(bh, inode); + if (prev_bh) { + u32 tmp = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next); + if (tmp) + affs_warning(sb, "extent_file_ofs", "next block already set for %d (%d)", bidx, tmp); + AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr); + affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp); + mark_buffer_dirty_inode(prev_bh, inode); + affs_brelse(prev_bh); + } + size += bsize; + bidx++; + } + affs_brelse(bh); + inode->i_size = AFFS_I(inode)->mmu_private = newsize; + return 0; + +out: + inode->i_size = AFFS_I(inode)->mmu_private = newsize; + return PTR_ERR(bh); +} + +static int +affs_readpage_ofs(struct file *file, struct page *page) +{ + struct inode *inode = page->mapping->host; + u32 to; + int err; + + pr_debug("AFFS: read_page(%u, %ld)\n", (u32)inode->i_ino, page->index); + to = PAGE_CACHE_SIZE; + if (((page->index + 1) << PAGE_CACHE_SHIFT) > inode->i_size) { + to = inode->i_size & ~PAGE_CACHE_MASK; + memset(page_address(page) + to, 0, PAGE_CACHE_SIZE - to); + } + + err = affs_do_readpage_ofs(file, page, 0, to); + if (!err) + SetPageUptodate(page); + unlock_page(page); + return err; +} + +static int affs_prepare_write_ofs(struct file *file, struct page *page, unsigned from, unsigned to) +{ + struct inode *inode = page->mapping->host; + u32 size, offset; + u32 tmp; + int err = 0; + + pr_debug("AFFS: prepare_write(%u, %ld, %d, %d)\n", (u32)inode->i_ino, page->index, from, to); + offset = page->index << PAGE_CACHE_SHIFT; + if (offset + from > AFFS_I(inode)->mmu_private) { + err = affs_extent_file_ofs(inode, offset + from); + if (err) + return err; + } + size = inode->i_size; + + if (PageUptodate(page)) + return 0; + + if (from) { + err = affs_do_readpage_ofs(file, page, 0, from); + if (err) + return err; + } + if (to < PAGE_CACHE_SIZE) { + char *kaddr = kmap_atomic(page, KM_USER0); + + memset(kaddr + to, 0, PAGE_CACHE_SIZE - to); + flush_dcache_page(page); + kunmap_atomic(kaddr, KM_USER0); + if (size > offset + to) { + if (size < offset + PAGE_CACHE_SIZE) + tmp = size & ~PAGE_CACHE_MASK; + else + tmp = PAGE_CACHE_SIZE; + err = affs_do_readpage_ofs(file, page, to, tmp); + } + } + return err; +} + +static int affs_commit_write_ofs(struct file *file, struct page *page, unsigned from, unsigned to) +{ + struct inode *inode = page->mapping->host; + struct super_block *sb = inode->i_sb; + struct buffer_head *bh, *prev_bh; + char *data; + u32 bidx, boff, bsize; + u32 tmp; + int written; + + pr_debug("AFFS: commit_write(%u, %ld, %d, %d)\n", (u32)inode->i_ino, page->index, from, to); + bsize = AFFS_SB(sb)->s_data_blksize; + data = page_address(page); + + bh = NULL; + written = 0; + tmp = (page->index << PAGE_CACHE_SHIFT) + from; + bidx = tmp / bsize; + boff = tmp % bsize; + if (boff) { + bh = affs_bread_ino(inode, bidx, 0); + if (IS_ERR(bh)) + return PTR_ERR(bh); + tmp = min(bsize - boff, to - from); + if (boff + tmp > bsize || tmp > bsize) + BUG(); + memcpy(AFFS_DATA(bh) + boff, data + from, tmp); + AFFS_DATA_HEAD(bh)->size = cpu_to_be32(be32_to_cpu(AFFS_DATA_HEAD(bh)->size) + tmp); + affs_fix_checksum(sb, bh); + mark_buffer_dirty_inode(bh, inode); + written += tmp; + from += tmp; + bidx++; + } else if (bidx) { + bh = affs_bread_ino(inode, bidx - 1, 0); + if (IS_ERR(bh)) + return PTR_ERR(bh); + } + while (from + bsize <= to) { + prev_bh = bh; + bh = affs_getemptyblk_ino(inode, bidx); + if (IS_ERR(bh)) + goto out; + memcpy(AFFS_DATA(bh), data + from, bsize); + if (buffer_new(bh)) { + AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA); + AFFS_DATA_HEAD(bh)->key = cpu_to_be32(inode->i_ino); + AFFS_DATA_HEAD(bh)->sequence = cpu_to_be32(bidx); + AFFS_DATA_HEAD(bh)->size = cpu_to_be32(bsize); + AFFS_DATA_HEAD(bh)->next = 0; + bh->b_state &= ~(1UL << BH_New); + if (prev_bh) { + u32 tmp = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next); + if (tmp) + affs_warning(sb, "commit_write_ofs", "next block already set for %d (%d)", bidx, tmp); + AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr); + affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp); + mark_buffer_dirty_inode(prev_bh, inode); + } + } + affs_brelse(prev_bh); + affs_fix_checksum(sb, bh); + mark_buffer_dirty_inode(bh, inode); + written += bsize; + from += bsize; + bidx++; + } + if (from < to) { + prev_bh = bh; + bh = affs_bread_ino(inode, bidx, 1); + if (IS_ERR(bh)) + goto out; + tmp = min(bsize, to - from); + if (tmp > bsize) + BUG(); + memcpy(AFFS_DATA(bh), data + from, tmp); + if (buffer_new(bh)) { + AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA); + AFFS_DATA_HEAD(bh)->key = cpu_to_be32(inode->i_ino); + AFFS_DATA_HEAD(bh)->sequence = cpu_to_be32(bidx); + AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp); + AFFS_DATA_HEAD(bh)->next = 0; + bh->b_state &= ~(1UL << BH_New); + if (prev_bh) { + u32 tmp = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next); + if (tmp) + affs_warning(sb, "commit_write_ofs", "next block already set for %d (%d)", bidx, tmp); + AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr); + affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp); + mark_buffer_dirty_inode(prev_bh, inode); + } + } else if (be32_to_cpu(AFFS_DATA_HEAD(bh)->size) < tmp) + AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp); + affs_brelse(prev_bh); + affs_fix_checksum(sb, bh); + mark_buffer_dirty_inode(bh, inode); + written += tmp; + from += tmp; + bidx++; + } + SetPageUptodate(page); + +done: + affs_brelse(bh); + tmp = (page->index << PAGE_CACHE_SHIFT) + from; + if (tmp > inode->i_size) + inode->i_size = AFFS_I(inode)->mmu_private = tmp; + + return written; + +out: + bh = prev_bh; + if (!written) + written = PTR_ERR(bh); + goto done; +} + +struct address_space_operations affs_aops_ofs = { + .readpage = affs_readpage_ofs, + //.writepage = affs_writepage_ofs, + //.sync_page = affs_sync_page_ofs, + .prepare_write = affs_prepare_write_ofs, + .commit_write = affs_commit_write_ofs +}; + +/* Free any preallocated blocks. */ + +void +affs_free_prealloc(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + + pr_debug("AFFS: free_prealloc(ino=%lu)\n", inode->i_ino); + + while (AFFS_I(inode)->i_pa_cnt) { + AFFS_I(inode)->i_pa_cnt--; + affs_free_block(sb, ++AFFS_I(inode)->i_lastalloc); + } +} + +/* Truncate (or enlarge) a file to the requested size. */ + +void +affs_truncate(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + u32 ext, ext_key; + u32 last_blk, blkcnt, blk; + u32 size; + struct buffer_head *ext_bh; + int i; + + pr_debug("AFFS: truncate(inode=%d, oldsize=%u, newsize=%u)\n", + (u32)inode->i_ino, (u32)AFFS_I(inode)->mmu_private, (u32)inode->i_size); + + last_blk = 0; + ext = 0; + if (inode->i_size) { + last_blk = ((u32)inode->i_size - 1) / AFFS_SB(sb)->s_data_blksize; + ext = last_blk / AFFS_SB(sb)->s_hashsize; + } + + if (inode->i_size > AFFS_I(inode)->mmu_private) { + struct address_space *mapping = inode->i_mapping; + struct page *page; + u32 size = inode->i_size - 1; + int res; + + page = grab_cache_page(mapping, size >> PAGE_CACHE_SHIFT); + if (!page) + return; + size = (size & (PAGE_CACHE_SIZE - 1)) + 1; + res = mapping->a_ops->prepare_write(NULL, page, size, size); + if (!res) + res = mapping->a_ops->commit_write(NULL, page, size, size); + unlock_page(page); + page_cache_release(page); + mark_inode_dirty(inode); + return; + } else if (inode->i_size == AFFS_I(inode)->mmu_private) + return; + + // lock cache + ext_bh = affs_get_extblock(inode, ext); + if (IS_ERR(ext_bh)) { + affs_warning(sb, "truncate", "unexpected read error for ext block %u (%d)", + ext, PTR_ERR(ext_bh)); + return; + } + if (AFFS_I(inode)->i_lc) { + /* clear linear cache */ + i = (ext + 1) >> AFFS_I(inode)->i_lc_shift; + if (AFFS_I(inode)->i_lc_size > i) { + AFFS_I(inode)->i_lc_size = i; + for (; i < AFFS_LC_SIZE; i++) + AFFS_I(inode)->i_lc[i] = 0; + } + /* clear associative cache */ + for (i = 0; i < AFFS_AC_SIZE; i++) + if (AFFS_I(inode)->i_ac[i].ext >= ext) + AFFS_I(inode)->i_ac[i].ext = 0; + } + ext_key = be32_to_cpu(AFFS_TAIL(sb, ext_bh)->extension); + + blkcnt = AFFS_I(inode)->i_blkcnt; + i = 0; + blk = last_blk; + if (inode->i_size) { + i = last_blk % AFFS_SB(sb)->s_hashsize + 1; + blk++; + } else + AFFS_HEAD(ext_bh)->first_data = 0; + size = AFFS_SB(sb)->s_hashsize; + if (size > blkcnt - blk + i) + size = blkcnt - blk + i; + for (; i < size; i++, blk++) { + affs_free_block(sb, be32_to_cpu(AFFS_BLOCK(sb, ext_bh, i))); + AFFS_BLOCK(sb, ext_bh, i) = 0; + } + AFFS_TAIL(sb, ext_bh)->extension = 0; + affs_fix_checksum(sb, ext_bh); + mark_buffer_dirty_inode(ext_bh, inode); + affs_brelse(ext_bh); + + if (inode->i_size) { + AFFS_I(inode)->i_blkcnt = last_blk + 1; + AFFS_I(inode)->i_extcnt = ext + 1; + if (AFFS_SB(sb)->s_flags & SF_OFS) { + struct buffer_head *bh = affs_bread_ino(inode, last_blk, 0); + u32 tmp; + if (IS_ERR(ext_bh)) { + affs_warning(sb, "truncate", "unexpected read error for last block %u (%d)", + ext, PTR_ERR(ext_bh)); + return; + } + tmp = be32_to_cpu(AFFS_DATA_HEAD(bh)->next); + AFFS_DATA_HEAD(bh)->next = 0; + affs_adjust_checksum(bh, -tmp); + affs_brelse(bh); + } + } else { + AFFS_I(inode)->i_blkcnt = 0; + AFFS_I(inode)->i_extcnt = 1; + } + AFFS_I(inode)->mmu_private = inode->i_size; + // unlock cache + + while (ext_key) { + ext_bh = affs_bread(sb, ext_key); + size = AFFS_SB(sb)->s_hashsize; + if (size > blkcnt - blk) + size = blkcnt - blk; + for (i = 0; i < size; i++, blk++) + affs_free_block(sb, be32_to_cpu(AFFS_BLOCK(sb, ext_bh, i))); + affs_free_block(sb, ext_key); + ext_key = be32_to_cpu(AFFS_TAIL(sb, ext_bh)->extension); + affs_brelse(ext_bh); + } + affs_free_prealloc(inode); +} diff --git a/fs/affs/inode.c b/fs/affs/inode.c new file mode 100644 index 00000000000..7aa6f200453 --- /dev/null +++ b/fs/affs/inode.c @@ -0,0 +1,411 @@ +/* + * linux/fs/affs/inode.c + * + * (c) 1996 Hans-Joachim Widmaier - Rewritten + * + * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem. + * + * (C) 1992 Eric Youngdale Modified for ISO9660 filesystem. + * + * (C) 1991 Linus Torvalds - minix filesystem + */ + +#include "affs.h" + +extern struct inode_operations affs_symlink_inode_operations; +extern struct timezone sys_tz; + +void +affs_read_inode(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + struct affs_sb_info *sbi = AFFS_SB(sb); + struct buffer_head *bh; + struct affs_head *head; + struct affs_tail *tail; + u32 block; + u32 size; + u32 prot; + u16 id; + + pr_debug("AFFS: read_inode(%lu)\n",inode->i_ino); + + block = inode->i_ino; + bh = affs_bread(sb, block); + if (!bh) { + affs_warning(sb, "read_inode", "Cannot read block %d", block); + goto bad_inode; + } + if (affs_checksum_block(sb, bh) || be32_to_cpu(AFFS_HEAD(bh)->ptype) != T_SHORT) { + affs_warning(sb,"read_inode", + "Checksum or type (ptype=%d) error on inode %d", + AFFS_HEAD(bh)->ptype, block); + goto bad_inode; + } + + head = AFFS_HEAD(bh); + tail = AFFS_TAIL(sb, bh); + prot = be32_to_cpu(tail->protect); + + inode->i_size = 0; + inode->i_nlink = 1; + inode->i_mode = 0; + AFFS_I(inode)->i_extcnt = 1; + AFFS_I(inode)->i_ext_last = ~1; + AFFS_I(inode)->i_protect = prot; + AFFS_I(inode)->i_opencnt = 0; + AFFS_I(inode)->i_blkcnt = 0; + AFFS_I(inode)->i_lc = NULL; + AFFS_I(inode)->i_lc_size = 0; + AFFS_I(inode)->i_lc_shift = 0; + AFFS_I(inode)->i_lc_mask = 0; + AFFS_I(inode)->i_ac = NULL; + AFFS_I(inode)->i_ext_bh = NULL; + AFFS_I(inode)->mmu_private = 0; + AFFS_I(inode)->i_lastalloc = 0; + AFFS_I(inode)->i_pa_cnt = 0; + + if (sbi->s_flags & SF_SETMODE) + inode->i_mode = sbi->s_mode; + else + inode->i_mode = prot_to_mode(prot); + + id = be16_to_cpu(tail->uid); + if (id == 0 || sbi->s_flags & SF_SETUID) + inode->i_uid = sbi->s_uid; + else if (id == 0xFFFF && sbi->s_flags & SF_MUFS) + inode->i_uid = 0; + else + inode->i_uid = id; + + id = be16_to_cpu(tail->gid); + if (id == 0 || sbi->s_flags & SF_SETGID) + inode->i_gid = sbi->s_gid; + else if (id == 0xFFFF && sbi->s_flags & SF_MUFS) + inode->i_gid = 0; + else + inode->i_gid = id; + + switch (be32_to_cpu(tail->stype)) { + case ST_ROOT: + inode->i_uid = sbi->s_uid; + inode->i_gid = sbi->s_gid; + /* fall through */ + case ST_USERDIR: + if (be32_to_cpu(tail->stype) == ST_USERDIR || + sbi->s_flags & SF_SETMODE) { + if (inode->i_mode & S_IRUSR) + inode->i_mode |= S_IXUSR; + if (inode->i_mode & S_IRGRP) + inode->i_mode |= S_IXGRP; + if (inode->i_mode & S_IROTH) + inode->i_mode |= S_IXOTH; + inode->i_mode |= S_IFDIR; + } else + inode->i_mode = S_IRUGO | S_IXUGO | S_IWUSR | S_IFDIR; + if (tail->link_chain) + inode->i_nlink = 2; + /* Maybe it should be controlled by mount parameter? */ + //inode->i_mode |= S_ISVTX; + inode->i_op = &affs_dir_inode_operations; + inode->i_fop = &affs_dir_operations; + break; + case ST_LINKDIR: +#if 0 + affs_warning(sb, "read_inode", "inode is LINKDIR"); + goto bad_inode; +#else + inode->i_mode |= S_IFDIR; + inode->i_op = NULL; + inode->i_fop = NULL; + break; +#endif + case ST_LINKFILE: + affs_warning(sb, "read_inode", "inode is LINKFILE"); + goto bad_inode; + case ST_FILE: + size = be32_to_cpu(tail->size); + inode->i_mode |= S_IFREG; + AFFS_I(inode)->mmu_private = inode->i_size = size; + if (inode->i_size) { + AFFS_I(inode)->i_blkcnt = (size - 1) / + sbi->s_data_blksize + 1; + AFFS_I(inode)->i_extcnt = (AFFS_I(inode)->i_blkcnt - 1) / + sbi->s_hashsize + 1; + } + if (tail->link_chain) + inode->i_nlink = 2; + inode->i_mapping->a_ops = (sbi->s_flags & SF_OFS) ? &affs_aops_ofs : &affs_aops; + inode->i_op = &affs_file_inode_operations; + inode->i_fop = &affs_file_operations; + break; + case ST_SOFTLINK: + inode->i_mode |= S_IFLNK; + inode->i_op = &affs_symlink_inode_operations; + inode->i_data.a_ops = &affs_symlink_aops; + break; + } + + inode->i_mtime.tv_sec = inode->i_atime.tv_sec = inode->i_ctime.tv_sec + = (be32_to_cpu(tail->change.days) * (24 * 60 * 60) + + be32_to_cpu(tail->change.mins) * 60 + + be32_to_cpu(tail->change.ticks) / 50 + + ((8 * 365 + 2) * 24 * 60 * 60)) + + sys_tz.tz_minuteswest * 60; + inode->i_mtime.tv_nsec = inode->i_ctime.tv_nsec = inode->i_atime.tv_nsec = 0; + affs_brelse(bh); + return; + +bad_inode: + make_bad_inode(inode); + affs_brelse(bh); + return; +} + +int +affs_write_inode(struct inode *inode, int unused) +{ + struct super_block *sb = inode->i_sb; + struct buffer_head *bh; + struct affs_tail *tail; + uid_t uid; + gid_t gid; + + pr_debug("AFFS: write_inode(%lu)\n",inode->i_ino); + + if (!inode->i_nlink) + // possibly free block + return 0; + bh = affs_bread(sb, inode->i_ino); + if (!bh) { + affs_error(sb,"write_inode","Cannot read block %lu",inode->i_ino); + return -EIO; + } + tail = AFFS_TAIL(sb, bh); + if (tail->stype == cpu_to_be32(ST_ROOT)) { + secs_to_datestamp(inode->i_mtime.tv_sec,&AFFS_ROOT_TAIL(sb, bh)->root_change); + } else { + tail->protect = cpu_to_be32(AFFS_I(inode)->i_protect); + tail->size = cpu_to_be32(inode->i_size); + secs_to_datestamp(inode->i_mtime.tv_sec,&tail->change); + if (!(inode->i_ino == AFFS_SB(sb)->s_root_block)) { + uid = inode->i_uid; + gid = inode->i_gid; + if (AFFS_SB(sb)->s_flags & SF_MUFS) { + if (inode->i_uid == 0 || inode->i_uid == 0xFFFF) + uid = inode->i_uid ^ ~0; + if (inode->i_gid == 0 || inode->i_gid == 0xFFFF) + gid = inode->i_gid ^ ~0; + } + if (!(AFFS_SB(sb)->s_flags & SF_SETUID)) + tail->uid = cpu_to_be16(uid); + if (!(AFFS_SB(sb)->s_flags & SF_SETGID)) + tail->gid = cpu_to_be16(gid); + } + } + affs_fix_checksum(sb, bh); + mark_buffer_dirty_inode(bh, inode); + affs_brelse(bh); + affs_free_prealloc(inode); + return 0; +} + +int +affs_notify_change(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + int error; + + pr_debug("AFFS: notify_change(%lu,0x%x)\n",inode->i_ino,attr->ia_valid); + + error = inode_change_ok(inode,attr); + if (error) + goto out; + + if (((attr->ia_valid & ATTR_UID) && (AFFS_SB(inode->i_sb)->s_flags & SF_SETUID)) || + ((attr->ia_valid & ATTR_GID) && (AFFS_SB(inode->i_sb)->s_flags & SF_SETGID)) || + ((attr->ia_valid & ATTR_MODE) && + (AFFS_SB(inode->i_sb)->s_flags & (SF_SETMODE | SF_IMMUTABLE)))) { + if (!(AFFS_SB(inode->i_sb)->s_flags & SF_QUIET)) + error = -EPERM; + goto out; + } + + error = inode_setattr(inode, attr); + if (!error && (attr->ia_valid & ATTR_MODE)) + mode_to_prot(inode); +out: + return error; +} + +void +affs_put_inode(struct inode *inode) +{ + pr_debug("AFFS: put_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink); + affs_free_prealloc(inode); + if (atomic_read(&inode->i_count) == 1) { + down(&inode->i_sem); + if (inode->i_size != AFFS_I(inode)->mmu_private) + affs_truncate(inode); + up(&inode->i_sem); + } +} + +void +affs_delete_inode(struct inode *inode) +{ + pr_debug("AFFS: delete_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink); + inode->i_size = 0; + if (S_ISREG(inode->i_mode)) + affs_truncate(inode); + clear_inode(inode); + affs_free_block(inode->i_sb, inode->i_ino); +} + +void +affs_clear_inode(struct inode *inode) +{ + unsigned long cache_page = (unsigned long) AFFS_I(inode)->i_lc; + + pr_debug("AFFS: clear_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink); + if (cache_page) { + pr_debug("AFFS: freeing ext cache\n"); + AFFS_I(inode)->i_lc = NULL; + AFFS_I(inode)->i_ac = NULL; + free_page(cache_page); + } + affs_brelse(AFFS_I(inode)->i_ext_bh); + AFFS_I(inode)->i_ext_last = ~1; + AFFS_I(inode)->i_ext_bh = NULL; +} + +struct inode * +affs_new_inode(struct inode *dir) +{ + struct super_block *sb = dir->i_sb; + struct inode *inode; + u32 block; + struct buffer_head *bh; + + if (!(inode = new_inode(sb))) + goto err_inode; + + if (!(block = affs_alloc_block(dir, dir->i_ino))) + goto err_block; + + bh = affs_getzeroblk(sb, block); + if (!bh) + goto err_bh; + mark_buffer_dirty_inode(bh, inode); + affs_brelse(bh); + + inode->i_uid = current->fsuid; + inode->i_gid = current->fsgid; + inode->i_ino = block; + inode->i_nlink = 1; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC; + AFFS_I(inode)->i_opencnt = 0; + AFFS_I(inode)->i_blkcnt = 0; + AFFS_I(inode)->i_lc = NULL; + AFFS_I(inode)->i_lc_size = 0; + AFFS_I(inode)->i_lc_shift = 0; + AFFS_I(inode)->i_lc_mask = 0; + AFFS_I(inode)->i_ac = NULL; + AFFS_I(inode)->i_ext_bh = NULL; + AFFS_I(inode)->mmu_private = 0; + AFFS_I(inode)->i_protect = 0; + AFFS_I(inode)->i_lastalloc = 0; + AFFS_I(inode)->i_pa_cnt = 0; + AFFS_I(inode)->i_extcnt = 1; + AFFS_I(inode)->i_ext_last = ~1; + + insert_inode_hash(inode); + + return inode; + +err_bh: + affs_free_block(sb, block); +err_block: + iput(inode); +err_inode: + return NULL; +} + +/* + * Add an entry to a directory. Create the header block + * and insert it into the hash table. + */ + +int +affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s32 type) +{ + struct super_block *sb = dir->i_sb; + struct buffer_head *inode_bh = NULL; + struct buffer_head *bh = NULL; + u32 block = 0; + int retval; + + pr_debug("AFFS: add_entry(dir=%u, inode=%u, \"%*s\", type=%d)\n", (u32)dir->i_ino, + (u32)inode->i_ino, (int)dentry->d_name.len, dentry->d_name.name, type); + + retval = -EIO; + bh = affs_bread(sb, inode->i_ino); + if (!bh) + goto done; + + affs_lock_link(inode); + switch (type) { + case ST_LINKFILE: + case ST_LINKDIR: + inode_bh = bh; + retval = -ENOSPC; + block = affs_alloc_block(dir, dir->i_ino); + if (!block) + goto err; + retval = -EIO; + bh = affs_getzeroblk(sb, block); + if (!bh) + goto err; + break; + default: + break; + } + + AFFS_HEAD(bh)->ptype = cpu_to_be32(T_SHORT); + AFFS_HEAD(bh)->key = cpu_to_be32(bh->b_blocknr); + affs_copy_name(AFFS_TAIL(sb, bh)->name, dentry); + AFFS_TAIL(sb, bh)->stype = cpu_to_be32(type); + AFFS_TAIL(sb, bh)->parent = cpu_to_be32(dir->i_ino); + + if (inode_bh) { + __be32 chain; + chain = AFFS_TAIL(sb, inode_bh)->link_chain; + AFFS_TAIL(sb, bh)->original = cpu_to_be32(inode->i_ino); + AFFS_TAIL(sb, bh)->link_chain = chain; + AFFS_TAIL(sb, inode_bh)->link_chain = cpu_to_be32(block); + affs_adjust_checksum(inode_bh, block - be32_to_cpu(chain)); + mark_buffer_dirty_inode(inode_bh, inode); + inode->i_nlink = 2; + atomic_inc(&inode->i_count); + } + affs_fix_checksum(sb, bh); + mark_buffer_dirty_inode(bh, inode); + dentry->d_fsdata = (void *)(long)bh->b_blocknr; + + affs_lock_dir(dir); + retval = affs_insert_hash(dir, bh); + mark_buffer_dirty_inode(bh, inode); + affs_unlock_dir(dir); + affs_unlock_link(inode); + + d_instantiate(dentry, inode); +done: + affs_brelse(inode_bh); + affs_brelse(bh); + return retval; +err: + if (block) + affs_free_block(sb, block); + affs_unlock_link(inode); + goto done; +} diff --git a/fs/affs/namei.c b/fs/affs/namei.c new file mode 100644 index 00000000000..d4c2d636c47 --- /dev/null +++ b/fs/affs/namei.c @@ -0,0 +1,443 @@ +/* + * linux/fs/affs/namei.c + * + * (c) 1996 Hans-Joachim Widmaier - Rewritten + * + * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem. + * + * (C) 1991 Linus Torvalds - minix filesystem + */ + +#include "affs.h" + +typedef int (*toupper_t)(int); + +static int affs_toupper(int ch); +static int affs_hash_dentry(struct dentry *, struct qstr *); +static int affs_compare_dentry(struct dentry *, struct qstr *, struct qstr *); +static int affs_intl_toupper(int ch); +static int affs_intl_hash_dentry(struct dentry *, struct qstr *); +static int affs_intl_compare_dentry(struct dentry *, struct qstr *, struct qstr *); + +struct dentry_operations affs_dentry_operations = { + .d_hash = affs_hash_dentry, + .d_compare = affs_compare_dentry, +}; + +static struct dentry_operations affs_intl_dentry_operations = { + .d_hash = affs_intl_hash_dentry, + .d_compare = affs_intl_compare_dentry, +}; + + +/* Simple toupper() for DOS\1 */ + +static int +affs_toupper(int ch) +{ + return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch; +} + +/* International toupper() for DOS\3 ("international") */ + +static int +affs_intl_toupper(int ch) +{ + return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0 + && ch <= 0xFE && ch != 0xF7) ? + ch - ('a' - 'A') : ch; +} + +static inline toupper_t +affs_get_toupper(struct super_block *sb) +{ + return AFFS_SB(sb)->s_flags & SF_INTL ? affs_intl_toupper : affs_toupper; +} + +/* + * Note: the dentry argument is the parent dentry. + */ +static inline int +__affs_hash_dentry(struct dentry *dentry, struct qstr *qstr, toupper_t toupper) +{ + const u8 *name = qstr->name; + unsigned long hash; + int i; + + i = affs_check_name(qstr->name,qstr->len); + if (i) + return i; + + hash = init_name_hash(); + i = min(qstr->len, 30u); + for (; i > 0; name++, i--) + hash = partial_name_hash(toupper(*name), hash); + qstr->hash = end_name_hash(hash); + + return 0; +} + +static int +affs_hash_dentry(struct dentry *dentry, struct qstr *qstr) +{ + return __affs_hash_dentry(dentry, qstr, affs_toupper); +} +static int +affs_intl_hash_dentry(struct dentry *dentry, struct qstr *qstr) +{ + return __affs_hash_dentry(dentry, qstr, affs_intl_toupper); +} + +static inline int +__affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b, toupper_t toupper) +{ + const u8 *aname = a->name; + const u8 *bname = b->name; + int len; + + /* 'a' is the qstr of an already existing dentry, so the name + * must be valid. 'b' must be validated first. + */ + + if (affs_check_name(b->name,b->len)) + return 1; + + /* If the names are longer than the allowed 30 chars, + * the excess is ignored, so their length may differ. + */ + len = a->len; + if (len >= 30) { + if (b->len < 30) + return 1; + len = 30; + } else if (len != b->len) + return 1; + + for (; len > 0; len--) + if (toupper(*aname++) != toupper(*bname++)) + return 1; + + return 0; +} + +static int +affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b) +{ + return __affs_compare_dentry(dentry, a, b, affs_toupper); +} +static int +affs_intl_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b) +{ + return __affs_compare_dentry(dentry, a, b, affs_intl_toupper); +} + +/* + * NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure. + */ + +static inline int +affs_match(struct dentry *dentry, const u8 *name2, toupper_t toupper) +{ + const u8 *name = dentry->d_name.name; + int len = dentry->d_name.len; + + if (len >= 30) { + if (*name2 < 30) + return 0; + len = 30; + } else if (len != *name2) + return 0; + + for (name2++; len > 0; len--) + if (toupper(*name++) != toupper(*name2++)) + return 0; + return 1; +} + +int +affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len) +{ + toupper_t toupper = affs_get_toupper(sb); + int hash; + + hash = len = min(len, 30u); + for (; len > 0; len--) + hash = (hash * 13 + toupper(*name++)) & 0x7ff; + + return hash % AFFS_SB(sb)->s_hashsize; +} + +static struct buffer_head * +affs_find_entry(struct inode *dir, struct dentry *dentry) +{ + struct super_block *sb = dir->i_sb; + struct buffer_head *bh; + toupper_t toupper = affs_get_toupper(sb); + u32 key; + + pr_debug("AFFS: find_entry(\"%.*s\")\n", (int)dentry->d_name.len, dentry->d_name.name); + + bh = affs_bread(sb, dir->i_ino); + if (!bh) + return ERR_PTR(-EIO); + + key = be32_to_cpu(AFFS_HEAD(bh)->table[affs_hash_name(sb, dentry->d_name.name, dentry->d_name.len)]); + + for (;;) { + affs_brelse(bh); + if (key == 0) + return NULL; + bh = affs_bread(sb, key); + if (!bh) + return ERR_PTR(-EIO); + if (affs_match(dentry, AFFS_TAIL(sb, bh)->name, toupper)) + return bh; + key = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain); + } +} + +struct dentry * +affs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) +{ + struct super_block *sb = dir->i_sb; + struct buffer_head *bh; + struct inode *inode = NULL; + + pr_debug("AFFS: lookup(\"%.*s\")\n",(int)dentry->d_name.len,dentry->d_name.name); + + affs_lock_dir(dir); + bh = affs_find_entry(dir, dentry); + affs_unlock_dir(dir); + if (IS_ERR(bh)) { + return ERR_PTR(PTR_ERR(bh)); + } + if (bh) { + u32 ino = bh->b_blocknr; + + /* store the real header ino in d_fsdata for faster lookups */ + dentry->d_fsdata = (void *)(long)ino; + switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) { + //link to dirs disabled + //case ST_LINKDIR: + case ST_LINKFILE: + ino = be32_to_cpu(AFFS_TAIL(sb, bh)->original); + } + affs_brelse(bh); + inode = iget(sb, ino); + if (!inode) { + return ERR_PTR(-EACCES); + } + } + dentry->d_op = AFFS_SB(sb)->s_flags & SF_INTL ? &affs_intl_dentry_operations : &affs_dentry_operations; + d_add(dentry, inode); + return NULL; +} + +int +affs_unlink(struct inode *dir, struct dentry *dentry) +{ + pr_debug("AFFS: unlink(dir=%d, \"%.*s\")\n", (u32)dir->i_ino, + (int)dentry->d_name.len, dentry->d_name.name); + + return affs_remove_header(dentry); +} + +int +affs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) +{ + struct super_block *sb = dir->i_sb; + struct inode *inode; + int error; + + pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,(int)dentry->d_name.len, + dentry->d_name.name,mode); + + inode = affs_new_inode(dir); + if (!inode) + return -ENOSPC; + + inode->i_mode = mode; + mode_to_prot(inode); + mark_inode_dirty(inode); + + inode->i_op = &affs_file_inode_operations; + inode->i_fop = &affs_file_operations; + inode->i_mapping->a_ops = (AFFS_SB(sb)->s_flags & SF_OFS) ? &affs_aops_ofs : &affs_aops; + error = affs_add_entry(dir, inode, dentry, ST_FILE); + if (error) { + inode->i_nlink = 0; + iput(inode); + return error; + } + return 0; +} + +int +affs_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + struct inode *inode; + int error; + + pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino, + (int)dentry->d_name.len,dentry->d_name.name,mode); + + inode = affs_new_inode(dir); + if (!inode) + return -ENOSPC; + + inode->i_mode = S_IFDIR | mode; + mode_to_prot(inode); + + inode->i_op = &affs_dir_inode_operations; + inode->i_fop = &affs_dir_operations; + + error = affs_add_entry(dir, inode, dentry, ST_USERDIR); + if (error) { + inode->i_nlink = 0; + mark_inode_dirty(inode); + iput(inode); + return error; + } + return 0; +} + +int +affs_rmdir(struct inode *dir, struct dentry *dentry) +{ + pr_debug("AFFS: rmdir(dir=%u, \"%.*s\")\n", (u32)dir->i_ino, + (int)dentry->d_name.len, dentry->d_name.name); + + return affs_remove_header(dentry); +} + +int +affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) +{ + struct super_block *sb = dir->i_sb; + struct buffer_head *bh; + struct inode *inode; + char *p; + int i, maxlen, error; + char c, lc; + + pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino, + (int)dentry->d_name.len,dentry->d_name.name,symname); + + maxlen = AFFS_SB(sb)->s_hashsize * sizeof(u32) - 1; + inode = affs_new_inode(dir); + if (!inode) + return -ENOSPC; + + inode->i_op = &affs_symlink_inode_operations; + inode->i_data.a_ops = &affs_symlink_aops; + inode->i_mode = S_IFLNK | 0777; + mode_to_prot(inode); + + error = -EIO; + bh = affs_bread(sb, inode->i_ino); + if (!bh) + goto err; + i = 0; + p = (char *)AFFS_HEAD(bh)->table; + lc = '/'; + if (*symname == '/') { + while (*symname == '/') + symname++; + while (AFFS_SB(sb)->s_volume[i]) /* Cannot overflow */ + *p++ = AFFS_SB(sb)->s_volume[i++]; + } + while (i < maxlen && (c = *symname++)) { + if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') { + *p++ = '/'; + i++; + symname += 2; + lc = '/'; + } else if (c == '.' && lc == '/' && *symname == '/') { + symname++; + lc = '/'; + } else { + *p++ = c; + lc = c; + i++; + } + if (lc == '/') + while (*symname == '/') + symname++; + } + *p = 0; + mark_buffer_dirty_inode(bh, inode); + affs_brelse(bh); + mark_inode_dirty(inode); + + error = affs_add_entry(dir, inode, dentry, ST_SOFTLINK); + if (error) + goto err; + + return 0; + +err: + inode->i_nlink = 0; + mark_inode_dirty(inode); + iput(inode); + return error; +} + +int +affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) +{ + struct inode *inode = old_dentry->d_inode; + + pr_debug("AFFS: link(%u, %u, \"%.*s\")\n", (u32)inode->i_ino, (u32)dir->i_ino, + (int)dentry->d_name.len,dentry->d_name.name); + + return affs_add_entry(dir, inode, dentry, ST_LINKFILE); +} + +int +affs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + struct super_block *sb = old_dir->i_sb; + struct buffer_head *bh = NULL; + int retval; + + pr_debug("AFFS: rename(old=%u,\"%*s\" to new=%u,\"%*s\")\n", + (u32)old_dir->i_ino, (int)old_dentry->d_name.len, old_dentry->d_name.name, + (u32)new_dir->i_ino, (int)new_dentry->d_name.len, new_dentry->d_name.name); + + retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len); + if (retval) + return retval; + + /* Unlink destination if it already exists */ + if (new_dentry->d_inode) { + retval = affs_remove_header(new_dentry); + if (retval) + return retval; + } + + retval = -EIO; + bh = affs_bread(sb, old_dentry->d_inode->i_ino); + if (!bh) + goto done; + + /* Remove header from its parent directory. */ + affs_lock_dir(old_dir); + retval = affs_remove_hash(old_dir, bh); + affs_unlock_dir(old_dir); + if (retval) + goto done; + + /* And insert it into the new directory with the new name. */ + affs_copy_name(AFFS_TAIL(sb, bh)->name, new_dentry); + affs_fix_checksum(sb, bh); + affs_lock_dir(new_dir); + retval = affs_insert_hash(new_dir, bh); + affs_unlock_dir(new_dir); + /* TODO: move it back to old_dir, if error? */ + +done: + mark_buffer_dirty_inode(bh, retval ? old_dir : new_dir); + affs_brelse(bh); + return retval; +} diff --git a/fs/affs/super.c b/fs/affs/super.c new file mode 100644 index 00000000000..9c3080716c9 --- /dev/null +++ b/fs/affs/super.c @@ -0,0 +1,569 @@ +/* + * linux/fs/affs/inode.c + * + * (c) 1996 Hans-Joachim Widmaier - Rewritten + * + * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem. + * + * (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem. + * + * (C) 1991 Linus Torvalds - minix filesystem + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/statfs.h> +#include <linux/parser.h> +#include "affs.h" + +extern struct timezone sys_tz; + +static int affs_statfs(struct super_block *sb, struct kstatfs *buf); +static int affs_remount (struct super_block *sb, int *flags, char *data); + +static void +affs_put_super(struct super_block *sb) +{ + struct affs_sb_info *sbi = AFFS_SB(sb); + pr_debug("AFFS: put_super()\n"); + + if (!(sb->s_flags & MS_RDONLY)) { + AFFS_ROOT_TAIL(sb, sbi->s_root_bh)->bm_flag = cpu_to_be32(1); + secs_to_datestamp(get_seconds(), + &AFFS_ROOT_TAIL(sb, sbi->s_root_bh)->disk_change); + affs_fix_checksum(sb, sbi->s_root_bh); + mark_buffer_dirty(sbi->s_root_bh); + } + + if (sbi->s_prefix) + kfree(sbi->s_prefix); + affs_free_bitmap(sb); + affs_brelse(sbi->s_root_bh); + kfree(sbi); + sb->s_fs_info = NULL; + return; +} + +static void +affs_write_super(struct super_block *sb) +{ + int clean = 2; + struct affs_sb_info *sbi = AFFS_SB(sb); + + if (!(sb->s_flags & MS_RDONLY)) { + // if (sbi->s_bitmap[i].bm_bh) { + // if (buffer_dirty(sbi->s_bitmap[i].bm_bh)) { + // clean = 0; + AFFS_ROOT_TAIL(sb, sbi->s_root_bh)->bm_flag = cpu_to_be32(clean); + secs_to_datestamp(get_seconds(), + &AFFS_ROOT_TAIL(sb, sbi->s_root_bh)->disk_change); + affs_fix_checksum(sb, sbi->s_root_bh); + mark_buffer_dirty(sbi->s_root_bh); + sb->s_dirt = !clean; /* redo until bitmap synced */ + } else + sb->s_dirt = 0; + + pr_debug("AFFS: write_super() at %lu, clean=%d\n", get_seconds(), clean); +} + +static kmem_cache_t * affs_inode_cachep; + +static struct inode *affs_alloc_inode(struct super_block *sb) +{ + struct affs_inode_info *ei; + ei = (struct affs_inode_info *)kmem_cache_alloc(affs_inode_cachep, SLAB_KERNEL); + if (!ei) + return NULL; + ei->vfs_inode.i_version = 1; + return &ei->vfs_inode; +} + +static void affs_destroy_inode(struct inode *inode) +{ + kmem_cache_free(affs_inode_cachep, AFFS_I(inode)); +} + +static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) +{ + struct affs_inode_info *ei = (struct affs_inode_info *) foo; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) { + init_MUTEX(&ei->i_link_lock); + init_MUTEX(&ei->i_ext_lock); + inode_init_once(&ei->vfs_inode); + } +} + +static int init_inodecache(void) +{ + affs_inode_cachep = kmem_cache_create("affs_inode_cache", + sizeof(struct affs_inode_info), + 0, SLAB_RECLAIM_ACCOUNT, + init_once, NULL); + if (affs_inode_cachep == NULL) + return -ENOMEM; + return 0; +} + +static void destroy_inodecache(void) +{ + if (kmem_cache_destroy(affs_inode_cachep)) + printk(KERN_INFO "affs_inode_cache: not all structures were freed\n"); +} + +static struct super_operations affs_sops = { + .alloc_inode = affs_alloc_inode, + .destroy_inode = affs_destroy_inode, + .read_inode = affs_read_inode, + .write_inode = affs_write_inode, + .put_inode = affs_put_inode, + .delete_inode = affs_delete_inode, + .clear_inode = affs_clear_inode, + .put_super = affs_put_super, + .write_super = affs_write_super, + .statfs = affs_statfs, + .remount_fs = affs_remount, +}; + +enum { + Opt_bs, Opt_mode, Opt_mufs, Opt_prefix, Opt_protect, + Opt_reserved, Opt_root, Opt_setgid, Opt_setuid, + Opt_verbose, Opt_volume, Opt_ignore, Opt_err, +}; + +static match_table_t tokens = { + {Opt_bs, "bs=%u"}, + {Opt_mode, "mode=%o"}, + {Opt_mufs, "mufs"}, + {Opt_prefix, "prefix=%s"}, + {Opt_protect, "protect"}, + {Opt_reserved, "reserved=%u"}, + {Opt_root, "root=%u"}, + {Opt_setgid, "setgid=%u"}, + {Opt_setuid, "setuid=%u"}, + {Opt_verbose, "verbose"}, + {Opt_volume, "volume=%s"}, + {Opt_ignore, "grpquota"}, + {Opt_ignore, "noquota"}, + {Opt_ignore, "quota"}, + {Opt_ignore, "usrquota"}, + {Opt_err, NULL}, +}; + +static int +parse_options(char *options, uid_t *uid, gid_t *gid, int *mode, int *reserved, s32 *root, + int *blocksize, char **prefix, char *volume, unsigned long *mount_opts) +{ + char *p; + substring_t args[MAX_OPT_ARGS]; + + /* Fill in defaults */ + + *uid = current->uid; + *gid = current->gid; + *reserved = 2; + *root = -1; + *blocksize = -1; + volume[0] = ':'; + volume[1] = 0; + *mount_opts = 0; + if (!options) + return 1; + + while ((p = strsep(&options, ",")) != NULL) { + int token, n, option; + if (!*p) + continue; + + token = match_token(p, tokens, args); + switch (token) { + case Opt_bs: + if (match_int(&args[0], &n)) + return -EINVAL; + if (n != 512 && n != 1024 && n != 2048 + && n != 4096) { + printk ("AFFS: Invalid blocksize (512, 1024, 2048, 4096 allowed)\n"); + return 0; + } + *blocksize = n; + break; + case Opt_mode: + if (match_octal(&args[0], &option)) + return 1; + *mode = option & 0777; + *mount_opts |= SF_SETMODE; + break; + case Opt_mufs: + *mount_opts |= SF_MUFS; + break; + case Opt_prefix: + if (*prefix) { /* Free any previous prefix */ + kfree(*prefix); + *prefix = NULL; + } + *prefix = match_strdup(&args[0]); + if (!*prefix) + return 0; + *mount_opts |= SF_PREFIX; + break; + case Opt_protect: + *mount_opts |= SF_IMMUTABLE; + break; + case Opt_reserved: + if (match_int(&args[0], reserved)) + return 1; + break; + case Opt_root: + if (match_int(&args[0], root)) + return 1; + break; + case Opt_setgid: + if (match_int(&args[0], &option)) + return 1; + *gid = option; + *mount_opts |= SF_SETGID; + break; + case Opt_setuid: + if (match_int(&args[0], &option)) + return -EINVAL; + *uid = option; + *mount_opts |= SF_SETUID; + break; + case Opt_verbose: + *mount_opts |= SF_VERBOSE; + break; + case Opt_volume: { + char *vol = match_strdup(&args[0]); + strlcpy(volume, vol, 32); + kfree(vol); + break; + } + case Opt_ignore: + /* Silently ignore the quota options */ + break; + default: + printk("AFFS: Unrecognized mount option \"%s\" " + "or missing value\n", p); + return 0; + } + } + return 1; +} + +/* This function definitely needs to be split up. Some fine day I'll + * hopefully have the guts to do so. Until then: sorry for the mess. + */ + +static int affs_fill_super(struct super_block *sb, void *data, int silent) +{ + struct affs_sb_info *sbi; + struct buffer_head *root_bh = NULL; + struct buffer_head *boot_bh; + struct inode *root_inode = NULL; + s32 root_block; + int size, blocksize; + u32 chksum; + int num_bm; + int i, j; + s32 key; + uid_t uid; + gid_t gid; + int reserved; + unsigned long mount_flags; + int tmp_flags; /* fix remount prototype... */ + + pr_debug("AFFS: read_super(%s)\n",data ? (const char *)data : "no options"); + + sb->s_magic = AFFS_SUPER_MAGIC; + sb->s_op = &affs_sops; + sb->s_flags |= MS_NODIRATIME; + + sbi = kmalloc(sizeof(struct affs_sb_info), GFP_KERNEL); + if (!sbi) + return -ENOMEM; + sb->s_fs_info = sbi; + memset(sbi, 0, sizeof(*sbi)); + init_MUTEX(&sbi->s_bmlock); + + if (!parse_options(data,&uid,&gid,&i,&reserved,&root_block, + &blocksize,&sbi->s_prefix, + sbi->s_volume, &mount_flags)) { + printk(KERN_ERR "AFFS: Error parsing options\n"); + return -EINVAL; + } + /* N.B. after this point s_prefix must be released */ + + sbi->s_flags = mount_flags; + sbi->s_mode = i; + sbi->s_uid = uid; + sbi->s_gid = gid; + sbi->s_reserved= reserved; + + /* Get the size of the device in 512-byte blocks. + * If we later see that the partition uses bigger + * blocks, we will have to change it. + */ + + size = sb->s_bdev->bd_inode->i_size >> 9; + pr_debug("AFFS: initial blocksize=%d, #blocks=%d\n", 512, size); + + affs_set_blocksize(sb, PAGE_SIZE); + /* Try to find root block. Its location depends on the block size. */ + + i = 512; + j = 4096; + if (blocksize > 0) { + i = j = blocksize; + size = size / (blocksize / 512); + } + for (blocksize = i, key = 0; blocksize <= j; blocksize <<= 1, size >>= 1) { + sbi->s_root_block = root_block; + if (root_block < 0) + sbi->s_root_block = (reserved + size - 1) / 2; + pr_debug("AFFS: setting blocksize to %d\n", blocksize); + affs_set_blocksize(sb, blocksize); + sbi->s_partition_size = size; + + /* The root block location that was calculated above is not + * correct if the partition size is an odd number of 512- + * byte blocks, which will be rounded down to a number of + * 1024-byte blocks, and if there were an even number of + * reserved blocks. Ideally, all partition checkers should + * report the real number of blocks of the real blocksize, + * but since this just cannot be done, we have to try to + * find the root block anyways. In the above case, it is one + * block behind the calculated one. So we check this one, too. + */ + for (num_bm = 0; num_bm < 2; num_bm++) { + pr_debug("AFFS: Dev %s, trying root=%u, bs=%d, " + "size=%d, reserved=%d\n", + sb->s_id, + sbi->s_root_block + num_bm, + blocksize, size, reserved); + root_bh = affs_bread(sb, sbi->s_root_block + num_bm); + if (!root_bh) + continue; + if (!affs_checksum_block(sb, root_bh) && + be32_to_cpu(AFFS_ROOT_HEAD(root_bh)->ptype) == T_SHORT && + be32_to_cpu(AFFS_ROOT_TAIL(sb, root_bh)->stype) == ST_ROOT) { + sbi->s_hashsize = blocksize / 4 - 56; + sbi->s_root_block += num_bm; + key = 1; + goto got_root; + } + affs_brelse(root_bh); + root_bh = NULL; + } + } + if (!silent) + printk(KERN_ERR "AFFS: No valid root block on device %s\n", + sb->s_id); + goto out_error; + + /* N.B. after this point bh must be released */ +got_root: + root_block = sbi->s_root_block; + + /* Find out which kind of FS we have */ + boot_bh = sb_bread(sb, 0); + if (!boot_bh) { + printk(KERN_ERR "AFFS: Cannot read boot block\n"); + goto out_error; + } + chksum = be32_to_cpu(*(__be32 *)boot_bh->b_data); + brelse(boot_bh); + + /* Dircache filesystems are compatible with non-dircache ones + * when reading. As long as they aren't supported, writing is + * not recommended. + */ + if ((chksum == FS_DCFFS || chksum == MUFS_DCFFS || chksum == FS_DCOFS + || chksum == MUFS_DCOFS) && !(sb->s_flags & MS_RDONLY)) { + printk(KERN_NOTICE "AFFS: Dircache FS - mounting %s read only\n", + sb->s_id); + sb->s_flags |= MS_RDONLY; + } + switch (chksum) { + case MUFS_FS: + case MUFS_INTLFFS: + case MUFS_DCFFS: + sbi->s_flags |= SF_MUFS; + /* fall thru */ + case FS_INTLFFS: + case FS_DCFFS: + sbi->s_flags |= SF_INTL; + break; + case MUFS_FFS: + sbi->s_flags |= SF_MUFS; + break; + case FS_FFS: + break; + case MUFS_OFS: + sbi->s_flags |= SF_MUFS; + /* fall thru */ + case FS_OFS: + sbi->s_flags |= SF_OFS; + sb->s_flags |= MS_NOEXEC; + break; + case MUFS_DCOFS: + case MUFS_INTLOFS: + sbi->s_flags |= SF_MUFS; + case FS_DCOFS: + case FS_INTLOFS: + sbi->s_flags |= SF_INTL | SF_OFS; + sb->s_flags |= MS_NOEXEC; + break; + default: + printk(KERN_ERR "AFFS: Unknown filesystem on device %s: %08X\n", + sb->s_id, chksum); + goto out_error; + } + + if (mount_flags & SF_VERBOSE) { + chksum = cpu_to_be32(chksum); + printk(KERN_NOTICE "AFFS: Mounting volume \"%*s\": Type=%.3s\\%c, Blocksize=%d\n", + AFFS_ROOT_TAIL(sb, root_bh)->disk_name[0], + AFFS_ROOT_TAIL(sb, root_bh)->disk_name + 1, + (char *)&chksum,((char *)&chksum)[3] + '0',blocksize); + } + + sb->s_flags |= MS_NODEV | MS_NOSUID; + + sbi->s_data_blksize = sb->s_blocksize; + if (sbi->s_flags & SF_OFS) + sbi->s_data_blksize -= 24; + + /* Keep super block in cache */ + sbi->s_root_bh = root_bh; + /* N.B. after this point s_root_bh must be released */ + + tmp_flags = sb->s_flags; + if (affs_init_bitmap(sb, &tmp_flags)) + goto out_error; + sb->s_flags = tmp_flags; + + /* set up enough so that it can read an inode */ + + root_inode = iget(sb, root_block); + sb->s_root = d_alloc_root(root_inode); + if (!sb->s_root) { + printk(KERN_ERR "AFFS: Get root inode failed\n"); + goto out_error; + } + sb->s_root->d_op = &affs_dentry_operations; + + pr_debug("AFFS: s_flags=%lX\n",sb->s_flags); + return 0; + + /* + * Begin the cascaded cleanup ... + */ +out_error: + if (root_inode) + iput(root_inode); + if (sbi->s_bitmap) + kfree(sbi->s_bitmap); + affs_brelse(root_bh); + if (sbi->s_prefix) + kfree(sbi->s_prefix); + kfree(sbi); + sb->s_fs_info = NULL; + return -EINVAL; +} + +static int +affs_remount(struct super_block *sb, int *flags, char *data) +{ + struct affs_sb_info *sbi = AFFS_SB(sb); + int blocksize; + uid_t uid; + gid_t gid; + int mode; + int reserved; + int root_block; + unsigned long mount_flags; + int res = 0; + + pr_debug("AFFS: remount(flags=0x%x,opts=\"%s\")\n",*flags,data); + + *flags |= MS_NODIRATIME; + + if (!parse_options(data,&uid,&gid,&mode,&reserved,&root_block, + &blocksize,&sbi->s_prefix,sbi->s_volume,&mount_flags)) + return -EINVAL; + sbi->s_flags = mount_flags; + sbi->s_mode = mode; + sbi->s_uid = uid; + sbi->s_gid = gid; + + if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) + return 0; + if (*flags & MS_RDONLY) { + sb->s_dirt = 1; + while (sb->s_dirt) + affs_write_super(sb); + affs_free_bitmap(sb); + } else + res = affs_init_bitmap(sb, flags); + + return res; +} + +static int +affs_statfs(struct super_block *sb, struct kstatfs *buf) +{ + int free; + + pr_debug("AFFS: statfs() partsize=%d, reserved=%d\n",AFFS_SB(sb)->s_partition_size, + AFFS_SB(sb)->s_reserved); + + free = affs_count_free_blocks(sb); + buf->f_type = AFFS_SUPER_MAGIC; + buf->f_bsize = sb->s_blocksize; + buf->f_blocks = AFFS_SB(sb)->s_partition_size - AFFS_SB(sb)->s_reserved; + buf->f_bfree = free; + buf->f_bavail = free; + return 0; +} + +static struct super_block *affs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) +{ + return get_sb_bdev(fs_type, flags, dev_name, data, affs_fill_super); +} + +static struct file_system_type affs_fs_type = { + .owner = THIS_MODULE, + .name = "affs", + .get_sb = affs_get_sb, + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, +}; + +static int __init init_affs_fs(void) +{ + int err = init_inodecache(); + if (err) + goto out1; + err = register_filesystem(&affs_fs_type); + if (err) + goto out; + return 0; +out: + destroy_inodecache(); +out1: + return err; +} + +static void __exit exit_affs_fs(void) +{ + unregister_filesystem(&affs_fs_type); + destroy_inodecache(); +} + +MODULE_DESCRIPTION("Amiga filesystem support for Linux"); +MODULE_LICENSE("GPL"); + +module_init(init_affs_fs) +module_exit(exit_affs_fs) diff --git a/fs/affs/symlink.c b/fs/affs/symlink.c new file mode 100644 index 00000000000..426f0f094f2 --- /dev/null +++ b/fs/affs/symlink.c @@ -0,0 +1,78 @@ +/* + * linux/fs/affs/symlink.c + * + * 1995 Hans-Joachim Widmaier - Modified for affs. + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * affs symlink handling code + */ + +#include "affs.h" + +static int affs_symlink_readpage(struct file *file, struct page *page) +{ + struct buffer_head *bh; + struct inode *inode = page->mapping->host; + char *link = kmap(page); + struct slink_front *lf; + int err; + int i, j; + char c; + char lc; + char *pf; + + pr_debug("AFFS: follow_link(ino=%lu)\n",inode->i_ino); + + err = -EIO; + bh = affs_bread(inode->i_sb, inode->i_ino); + if (!bh) + goto fail; + i = 0; + j = 0; + lf = (struct slink_front *)bh->b_data; + lc = 0; + pf = AFFS_SB(inode->i_sb)->s_prefix ? AFFS_SB(inode->i_sb)->s_prefix : "/"; + + if (strchr(lf->symname,':')) { /* Handle assign or volume name */ + while (i < 1023 && (c = pf[i])) + link[i++] = c; + while (i < 1023 && lf->symname[j] != ':') + link[i++] = lf->symname[j++]; + if (i < 1023) + link[i++] = '/'; + j++; + lc = '/'; + } + while (i < 1023 && (c = lf->symname[j])) { + if (c == '/' && lc == '/' && i < 1020) { /* parent dir */ + link[i++] = '.'; + link[i++] = '.'; + } + link[i++] = c; + lc = c; + j++; + } + link[i] = '\0'; + affs_brelse(bh); + SetPageUptodate(page); + kunmap(page); + unlock_page(page); + return 0; +fail: + SetPageError(page); + kunmap(page); + unlock_page(page); + return err; +} + +struct address_space_operations affs_symlink_aops = { + .readpage = affs_symlink_readpage, +}; + +struct inode_operations affs_symlink_inode_operations = { + .readlink = generic_readlink, + .follow_link = page_follow_link_light, + .put_link = page_put_link, + .setattr = affs_notify_change, +}; diff --git a/fs/afs/Makefile b/fs/afs/Makefile new file mode 100644 index 00000000000..4029c9da4b8 --- /dev/null +++ b/fs/afs/Makefile @@ -0,0 +1,28 @@ +# +# Makefile for Red Hat Linux AFS client. +# + +#CFLAGS += -finstrument-functions + +kafs-objs := \ + callback.o \ + cell.o \ + cmservice.o \ + dir.o \ + file.o \ + fsclient.o \ + inode.o \ + kafsasyncd.o \ + kafstimod.o \ + main.o \ + misc.o \ + mntpt.o \ + proc.o \ + server.o \ + super.o \ + vlclient.o \ + vlocation.o \ + vnode.o \ + volume.o + +obj-$(CONFIG_AFS_FS) := kafs.o diff --git a/fs/afs/cache.h b/fs/afs/cache.h new file mode 100644 index 00000000000..9eb7722b34d --- /dev/null +++ b/fs/afs/cache.h @@ -0,0 +1,27 @@ +/* cache.h: AFS local cache management interface + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_AFS_CACHE_H +#define _LINUX_AFS_CACHE_H + +#undef AFS_CACHING_SUPPORT + +#include <linux/mm.h> +#ifdef AFS_CACHING_SUPPORT +#include <linux/cachefs.h> +#endif +#include "types.h" + +#ifdef __KERNEL__ + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_AFS_CACHE_H */ diff --git a/fs/afs/callback.c b/fs/afs/callback.c new file mode 100644 index 00000000000..2fd62f89ae0 --- /dev/null +++ b/fs/afs/callback.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2002 Red Hat, Inc. All rights reserved. + * + * This software may be freely redistributed under the terms of the + * GNU General Public License. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: David Woodhouse <dwmw2@cambridge.redhat.com> + * David Howells <dhowells@redhat.com> + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include "server.h" +#include "vnode.h" +#include "internal.h" + +/*****************************************************************************/ +/* + * allow the fileserver to request callback state (re-)initialisation + */ +int SRXAFSCM_InitCallBackState(struct afs_server *server) +{ + struct list_head callbacks; + + _enter("%p", server); + + INIT_LIST_HEAD(&callbacks); + + /* transfer the callback list from the server to a temp holding area */ + spin_lock(&server->cb_lock); + + list_add(&callbacks, &server->cb_promises); + list_del_init(&server->cb_promises); + + /* munch our way through the list, grabbing the inode, dropping all the + * locks and regetting them in the right order + */ + while (!list_empty(&callbacks)) { + struct afs_vnode *vnode; + struct inode *inode; + + vnode = list_entry(callbacks.next, struct afs_vnode, cb_link); + list_del_init(&vnode->cb_link); + + /* try and grab the inode - may fail */ + inode = igrab(AFS_VNODE_TO_I(vnode)); + if (inode) { + int release = 0; + + spin_unlock(&server->cb_lock); + spin_lock(&vnode->lock); + + if (vnode->cb_server == server) { + vnode->cb_server = NULL; + afs_kafstimod_del_timer(&vnode->cb_timeout); + spin_lock(&afs_cb_hash_lock); + list_del_init(&vnode->cb_hash_link); + spin_unlock(&afs_cb_hash_lock); + release = 1; + } + + spin_unlock(&vnode->lock); + + iput(inode); + afs_put_server(server); + + spin_lock(&server->cb_lock); + } + } + + spin_unlock(&server->cb_lock); + + _leave(" = 0"); + return 0; +} /* end SRXAFSCM_InitCallBackState() */ + +/*****************************************************************************/ +/* + * allow the fileserver to break callback promises + */ +int SRXAFSCM_CallBack(struct afs_server *server, size_t count, + struct afs_callback callbacks[]) +{ + _enter("%p,%u,", server, count); + + for (; count > 0; callbacks++, count--) { + struct afs_vnode *vnode = NULL; + struct inode *inode = NULL; + int valid = 0; + + _debug("- Fid { vl=%08x n=%u u=%u } CB { v=%u x=%u t=%u }", + callbacks->fid.vid, + callbacks->fid.vnode, + callbacks->fid.unique, + callbacks->version, + callbacks->expiry, + callbacks->type + ); + + /* find the inode for this fid */ + spin_lock(&afs_cb_hash_lock); + + list_for_each_entry(vnode, + &afs_cb_hash(server, &callbacks->fid), + cb_hash_link) { + if (memcmp(&vnode->fid, &callbacks->fid, + sizeof(struct afs_fid)) != 0) + continue; + + /* right vnode, but is it same server? */ + if (vnode->cb_server != server) + break; /* no */ + + /* try and nail the inode down */ + inode = igrab(AFS_VNODE_TO_I(vnode)); + break; + } + + spin_unlock(&afs_cb_hash_lock); + + if (inode) { + /* we've found the record for this vnode */ + spin_lock(&vnode->lock); + if (vnode->cb_server == server) { + /* the callback _is_ on the calling server */ + vnode->cb_server = NULL; + valid = 1; + + afs_kafstimod_del_timer(&vnode->cb_timeout); + vnode->flags |= AFS_VNODE_CHANGED; + + spin_lock(&server->cb_lock); + list_del_init(&vnode->cb_link); + spin_unlock(&server->cb_lock); + + spin_lock(&afs_cb_hash_lock); + list_del_init(&vnode->cb_hash_link); + spin_unlock(&afs_cb_hash_lock); + } + spin_unlock(&vnode->lock); + + if (valid) { + invalidate_remote_inode(inode); + afs_put_server(server); + } + iput(inode); + } + } + + _leave(" = 0"); + return 0; +} /* end SRXAFSCM_CallBack() */ + +/*****************************************************************************/ +/* + * allow the fileserver to see if the cache manager is still alive + */ +int SRXAFSCM_Probe(struct afs_server *server) +{ + _debug("SRXAFSCM_Probe(%p)\n", server); + return 0; +} /* end SRXAFSCM_Probe() */ diff --git a/fs/afs/cell.c b/fs/afs/cell.c new file mode 100644 index 00000000000..009a9ae88d6 --- /dev/null +++ b/fs/afs/cell.c @@ -0,0 +1,569 @@ +/* cell.c: AFS cell and server record management + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <rxrpc/peer.h> +#include <rxrpc/connection.h> +#include "volume.h" +#include "cell.h" +#include "server.h" +#include "transport.h" +#include "vlclient.h" +#include "kafstimod.h" +#include "super.h" +#include "internal.h" + +DECLARE_RWSEM(afs_proc_cells_sem); +LIST_HEAD(afs_proc_cells); + +static struct list_head afs_cells = LIST_HEAD_INIT(afs_cells); +static DEFINE_RWLOCK(afs_cells_lock); +static DECLARE_RWSEM(afs_cells_sem); /* add/remove serialisation */ +static struct afs_cell *afs_cell_root; + +#ifdef AFS_CACHING_SUPPORT +static cachefs_match_val_t afs_cell_cache_match(void *target, + const void *entry); +static void afs_cell_cache_update(void *source, void *entry); + +struct cachefs_index_def afs_cache_cell_index_def = { + .name = "cell_ix", + .data_size = sizeof(struct afs_cache_cell), + .keys[0] = { CACHEFS_INDEX_KEYS_ASCIIZ, 64 }, + .match = afs_cell_cache_match, + .update = afs_cell_cache_update, +}; +#endif + +/*****************************************************************************/ +/* + * create a cell record + * - "name" is the name of the cell + * - "vllist" is a colon separated list of IP addresses in "a.b.c.d" format + */ +int afs_cell_create(const char *name, char *vllist, struct afs_cell **_cell) +{ + struct afs_cell *cell; + char *next; + int ret; + + _enter("%s", name); + + BUG_ON(!name); /* TODO: want to look up "this cell" in the cache */ + + /* allocate and initialise a cell record */ + cell = kmalloc(sizeof(struct afs_cell) + strlen(name) + 1, GFP_KERNEL); + if (!cell) { + _leave(" = -ENOMEM"); + return -ENOMEM; + } + + down_write(&afs_cells_sem); + + memset(cell, 0, sizeof(struct afs_cell)); + atomic_set(&cell->usage, 0); + + INIT_LIST_HEAD(&cell->link); + + rwlock_init(&cell->sv_lock); + INIT_LIST_HEAD(&cell->sv_list); + INIT_LIST_HEAD(&cell->sv_graveyard); + spin_lock_init(&cell->sv_gylock); + + init_rwsem(&cell->vl_sem); + INIT_LIST_HEAD(&cell->vl_list); + INIT_LIST_HEAD(&cell->vl_graveyard); + spin_lock_init(&cell->vl_gylock); + + strcpy(cell->name,name); + + /* fill in the VL server list from the rest of the string */ + ret = -EINVAL; + do { + unsigned a, b, c, d; + + next = strchr(vllist, ':'); + if (next) + *next++ = 0; + + if (sscanf(vllist, "%u.%u.%u.%u", &a, &b, &c, &d) != 4) + goto badaddr; + + if (a > 255 || b > 255 || c > 255 || d > 255) + goto badaddr; + + cell->vl_addrs[cell->vl_naddrs++].s_addr = + htonl((a << 24) | (b << 16) | (c << 8) | d); + + if (cell->vl_naddrs >= AFS_CELL_MAX_ADDRS) + break; + + } while(vllist = next, vllist); + + /* add a proc dir for this cell */ + ret = afs_proc_cell_setup(cell); + if (ret < 0) + goto error; + +#ifdef AFS_CACHING_SUPPORT + /* put it up for caching */ + cachefs_acquire_cookie(afs_cache_netfs.primary_index, + &afs_vlocation_cache_index_def, + cell, + &cell->cache); +#endif + + /* add to the cell lists */ + write_lock(&afs_cells_lock); + list_add_tail(&cell->link, &afs_cells); + write_unlock(&afs_cells_lock); + + down_write(&afs_proc_cells_sem); + list_add_tail(&cell->proc_link, &afs_proc_cells); + up_write(&afs_proc_cells_sem); + + *_cell = cell; + up_write(&afs_cells_sem); + + _leave(" = 0 (%p)", cell); + return 0; + + badaddr: + printk(KERN_ERR "kAFS: bad VL server IP address: '%s'\n", vllist); + error: + up_write(&afs_cells_sem); + kfree(cell); + _leave(" = %d", ret); + return ret; +} /* end afs_cell_create() */ + +/*****************************************************************************/ +/* + * initialise the cell database from module parameters + */ +int afs_cell_init(char *rootcell) +{ + struct afs_cell *old_root, *new_root; + char *cp; + int ret; + + _enter(""); + + if (!rootcell) { + /* module is loaded with no parameters, or built statically. + * - in the future we might initialize cell DB here. + */ + _leave(" = 0 (but no root)"); + return 0; + } + + cp = strchr(rootcell, ':'); + if (!cp) { + printk(KERN_ERR "kAFS: no VL server IP addresses specified\n"); + _leave(" = %d (no colon)", -EINVAL); + return -EINVAL; + } + + /* allocate a cell record for the root cell */ + *cp++ = 0; + ret = afs_cell_create(rootcell, cp, &new_root); + if (ret < 0) { + _leave(" = %d", ret); + return ret; + } + + /* as afs_put_cell() takes locks by itself, we have to do + * a little gymnastics to be race-free. + */ + afs_get_cell(new_root); + + write_lock(&afs_cells_lock); + while (afs_cell_root) { + old_root = afs_cell_root; + afs_cell_root = NULL; + write_unlock(&afs_cells_lock); + afs_put_cell(old_root); + write_lock(&afs_cells_lock); + } + afs_cell_root = new_root; + write_unlock(&afs_cells_lock); + + _leave(" = %d", ret); + return ret; + +} /* end afs_cell_init() */ + +/*****************************************************************************/ +/* + * lookup a cell record + */ +int afs_cell_lookup(const char *name, unsigned namesz, struct afs_cell **_cell) +{ + struct afs_cell *cell; + int ret; + + _enter("\"%*.*s\",", namesz, namesz, name ? name : ""); + + *_cell = NULL; + + if (name) { + /* if the cell was named, look for it in the cell record list */ + ret = -ENOENT; + cell = NULL; + read_lock(&afs_cells_lock); + + list_for_each_entry(cell, &afs_cells, link) { + if (strncmp(cell->name, name, namesz) == 0) { + afs_get_cell(cell); + goto found; + } + } + cell = NULL; + found: + + read_unlock(&afs_cells_lock); + + if (cell) + ret = 0; + } + else { + read_lock(&afs_cells_lock); + + cell = afs_cell_root; + if (!cell) { + /* this should not happen unless user tries to mount + * when root cell is not set. Return an impossibly + * bizzare errno to alert the user. Things like + * ENOENT might be "more appropriate" but they happen + * for other reasons. + */ + ret = -EDESTADDRREQ; + } + else { + afs_get_cell(cell); + ret = 0; + } + + read_unlock(&afs_cells_lock); + } + + *_cell = cell; + _leave(" = %d (%p)", ret, cell); + return ret; + +} /* end afs_cell_lookup() */ + +/*****************************************************************************/ +/* + * try and get a cell record + */ +struct afs_cell *afs_get_cell_maybe(struct afs_cell **_cell) +{ + struct afs_cell *cell; + + write_lock(&afs_cells_lock); + + cell = *_cell; + if (cell && !list_empty(&cell->link)) + afs_get_cell(cell); + else + cell = NULL; + + write_unlock(&afs_cells_lock); + + return cell; +} /* end afs_get_cell_maybe() */ + +/*****************************************************************************/ +/* + * destroy a cell record + */ +void afs_put_cell(struct afs_cell *cell) +{ + if (!cell) + return; + + _enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name); + + /* sanity check */ + BUG_ON(atomic_read(&cell->usage) <= 0); + + /* to prevent a race, the decrement and the dequeue must be effectively + * atomic */ + write_lock(&afs_cells_lock); + + if (likely(!atomic_dec_and_test(&cell->usage))) { + write_unlock(&afs_cells_lock); + _leave(""); + return; + } + + write_unlock(&afs_cells_lock); + + BUG_ON(!list_empty(&cell->sv_list)); + BUG_ON(!list_empty(&cell->sv_graveyard)); + BUG_ON(!list_empty(&cell->vl_list)); + BUG_ON(!list_empty(&cell->vl_graveyard)); + + _leave(" [unused]"); +} /* end afs_put_cell() */ + +/*****************************************************************************/ +/* + * destroy a cell record + */ +static void afs_cell_destroy(struct afs_cell *cell) +{ + _enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name); + + /* to prevent a race, the decrement and the dequeue must be effectively + * atomic */ + write_lock(&afs_cells_lock); + + /* sanity check */ + BUG_ON(atomic_read(&cell->usage) != 0); + + list_del_init(&cell->link); + + write_unlock(&afs_cells_lock); + + down_write(&afs_cells_sem); + + afs_proc_cell_remove(cell); + + down_write(&afs_proc_cells_sem); + list_del_init(&cell->proc_link); + up_write(&afs_proc_cells_sem); + +#ifdef AFS_CACHING_SUPPORT + cachefs_relinquish_cookie(cell->cache, 0); +#endif + + up_write(&afs_cells_sem); + + BUG_ON(!list_empty(&cell->sv_list)); + BUG_ON(!list_empty(&cell->sv_graveyard)); + BUG_ON(!list_empty(&cell->vl_list)); + BUG_ON(!list_empty(&cell->vl_graveyard)); + + /* finish cleaning up the cell */ + kfree(cell); + + _leave(" [destroyed]"); +} /* end afs_cell_destroy() */ + +/*****************************************************************************/ +/* + * lookup the server record corresponding to an Rx RPC peer + */ +int afs_server_find_by_peer(const struct rxrpc_peer *peer, + struct afs_server **_server) +{ + struct afs_server *server; + struct afs_cell *cell; + + _enter("%p{a=%08x},", peer, ntohl(peer->addr.s_addr)); + + /* search the cell list */ + read_lock(&afs_cells_lock); + + list_for_each_entry(cell, &afs_cells, link) { + + _debug("? cell %s",cell->name); + + write_lock(&cell->sv_lock); + + /* check the active list */ + list_for_each_entry(server, &cell->sv_list, link) { + _debug("?? server %08x", ntohl(server->addr.s_addr)); + + if (memcmp(&server->addr, &peer->addr, + sizeof(struct in_addr)) == 0) + goto found_server; + } + + /* check the inactive list */ + spin_lock(&cell->sv_gylock); + list_for_each_entry(server, &cell->sv_graveyard, link) { + _debug("?? dead server %08x", + ntohl(server->addr.s_addr)); + + if (memcmp(&server->addr, &peer->addr, + sizeof(struct in_addr)) == 0) + goto found_dead_server; + } + spin_unlock(&cell->sv_gylock); + + write_unlock(&cell->sv_lock); + } + read_unlock(&afs_cells_lock); + + _leave(" = -ENOENT"); + return -ENOENT; + + /* we found it in the graveyard - resurrect it */ + found_dead_server: + list_del(&server->link); + list_add_tail(&server->link, &cell->sv_list); + afs_get_server(server); + afs_kafstimod_del_timer(&server->timeout); + spin_unlock(&cell->sv_gylock); + goto success; + + /* we found it - increment its ref count and return it */ + found_server: + afs_get_server(server); + + success: + write_unlock(&cell->sv_lock); + read_unlock(&afs_cells_lock); + + *_server = server; + _leave(" = 0 (s=%p c=%p)", server, cell); + return 0; + +} /* end afs_server_find_by_peer() */ + +/*****************************************************************************/ +/* + * purge in-memory cell database on module unload or afs_init() failure + * - the timeout daemon is stopped before calling this + */ +void afs_cell_purge(void) +{ + struct afs_vlocation *vlocation; + struct afs_cell *cell; + + _enter(""); + + afs_put_cell(afs_cell_root); + + while (!list_empty(&afs_cells)) { + cell = NULL; + + /* remove the next cell from the front of the list */ + write_lock(&afs_cells_lock); + + if (!list_empty(&afs_cells)) { + cell = list_entry(afs_cells.next, + struct afs_cell, link); + list_del_init(&cell->link); + } + + write_unlock(&afs_cells_lock); + + if (cell) { + _debug("PURGING CELL %s (%d)", + cell->name, atomic_read(&cell->usage)); + + BUG_ON(!list_empty(&cell->sv_list)); + BUG_ON(!list_empty(&cell->vl_list)); + + /* purge the cell's VL graveyard list */ + _debug(" - clearing VL graveyard"); + + spin_lock(&cell->vl_gylock); + + while (!list_empty(&cell->vl_graveyard)) { + vlocation = list_entry(cell->vl_graveyard.next, + struct afs_vlocation, + link); + list_del_init(&vlocation->link); + + afs_kafstimod_del_timer(&vlocation->timeout); + + spin_unlock(&cell->vl_gylock); + + afs_vlocation_do_timeout(vlocation); + /* TODO: race if move to use krxtimod instead + * of kafstimod */ + + spin_lock(&cell->vl_gylock); + } + + spin_unlock(&cell->vl_gylock); + + /* purge the cell's server graveyard list */ + _debug(" - clearing server graveyard"); + + spin_lock(&cell->sv_gylock); + + while (!list_empty(&cell->sv_graveyard)) { + struct afs_server *server; + + server = list_entry(cell->sv_graveyard.next, + struct afs_server, link); + list_del_init(&server->link); + + afs_kafstimod_del_timer(&server->timeout); + + spin_unlock(&cell->sv_gylock); + + afs_server_do_timeout(server); + + spin_lock(&cell->sv_gylock); + } + + spin_unlock(&cell->sv_gylock); + + /* now the cell should be left with no references */ + afs_cell_destroy(cell); + } + } + + _leave(""); +} /* end afs_cell_purge() */ + +/*****************************************************************************/ +/* + * match a cell record obtained from the cache + */ +#ifdef AFS_CACHING_SUPPORT +static cachefs_match_val_t afs_cell_cache_match(void *target, + const void *entry) +{ + const struct afs_cache_cell *ccell = entry; + struct afs_cell *cell = target; + + _enter("{%s},{%s}", ccell->name, cell->name); + + if (strncmp(ccell->name, cell->name, sizeof(ccell->name)) == 0) { + _leave(" = SUCCESS"); + return CACHEFS_MATCH_SUCCESS; + } + + _leave(" = FAILED"); + return CACHEFS_MATCH_FAILED; +} /* end afs_cell_cache_match() */ +#endif + +/*****************************************************************************/ +/* + * update a cell record in the cache + */ +#ifdef AFS_CACHING_SUPPORT +static void afs_cell_cache_update(void *source, void *entry) +{ + struct afs_cache_cell *ccell = entry; + struct afs_cell *cell = source; + + _enter("%p,%p", source, entry); + + strncpy(ccell->name, cell->name, sizeof(ccell->name)); + + memcpy(ccell->vl_servers, + cell->vl_addrs, + min(sizeof(ccell->vl_servers), sizeof(cell->vl_addrs))); + +} /* end afs_cell_cache_update() */ +#endif diff --git a/fs/afs/cell.h b/fs/afs/cell.h new file mode 100644 index 00000000000..48349108fb0 --- /dev/null +++ b/fs/afs/cell.h @@ -0,0 +1,78 @@ +/* cell.h: AFS cell record + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_AFS_CELL_H +#define _LINUX_AFS_CELL_H + +#include "types.h" +#include "cache.h" + +#define AFS_CELL_MAX_ADDRS 15 + +extern volatile int afs_cells_being_purged; /* T when cells are being purged by rmmod */ + +/*****************************************************************************/ +/* + * entry in the cached cell catalogue + */ +struct afs_cache_cell +{ + char name[64]; /* cell name (padded with NULs) */ + struct in_addr vl_servers[15]; /* cached cell VL servers */ +}; + +/*****************************************************************************/ +/* + * AFS cell record + */ +struct afs_cell +{ + atomic_t usage; + struct list_head link; /* main cell list link */ + struct list_head proc_link; /* /proc cell list link */ + struct proc_dir_entry *proc_dir; /* /proc dir for this cell */ +#ifdef AFS_CACHING_SUPPORT + struct cachefs_cookie *cache; /* caching cookie */ +#endif + + /* server record management */ + rwlock_t sv_lock; /* active server list lock */ + struct list_head sv_list; /* active server list */ + struct list_head sv_graveyard; /* inactive server list */ + spinlock_t sv_gylock; /* inactive server list lock */ + + /* volume location record management */ + struct rw_semaphore vl_sem; /* volume management serialisation semaphore */ + struct list_head vl_list; /* cell's active VL record list */ + struct list_head vl_graveyard; /* cell's inactive VL record list */ + spinlock_t vl_gylock; /* graveyard lock */ + unsigned short vl_naddrs; /* number of VL servers in addr list */ + unsigned short vl_curr_svix; /* current server index */ + struct in_addr vl_addrs[AFS_CELL_MAX_ADDRS]; /* cell VL server addresses */ + + char name[0]; /* cell name - must go last */ +}; + +extern int afs_cell_init(char *rootcell); + +extern int afs_cell_create(const char *name, char *vllist, struct afs_cell **_cell); + +extern int afs_cell_lookup(const char *name, unsigned nmsize, struct afs_cell **_cell); + +#define afs_get_cell(C) do { atomic_inc(&(C)->usage); } while(0) + +extern struct afs_cell *afs_get_cell_maybe(struct afs_cell **_cell); + +extern void afs_put_cell(struct afs_cell *cell); + +extern void afs_cell_purge(void); + +#endif /* _LINUX_AFS_CELL_H */ diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c new file mode 100644 index 00000000000..0a57fd7c726 --- /dev/null +++ b/fs/afs/cmservice.c @@ -0,0 +1,652 @@ +/* cmservice.c: AFS Cache Manager Service + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/completion.h> +#include "server.h" +#include "cell.h" +#include "transport.h" +#include <rxrpc/rxrpc.h> +#include <rxrpc/transport.h> +#include <rxrpc/connection.h> +#include <rxrpc/call.h> +#include "cmservice.h" +#include "internal.h" + +static unsigned afscm_usage; /* AFS cache manager usage count */ +static struct rw_semaphore afscm_sem; /* AFS cache manager start/stop semaphore */ + +static int afscm_new_call(struct rxrpc_call *call); +static void afscm_attention(struct rxrpc_call *call); +static void afscm_error(struct rxrpc_call *call); +static void afscm_aemap(struct rxrpc_call *call); + +static void _SRXAFSCM_CallBack(struct rxrpc_call *call); +static void _SRXAFSCM_InitCallBackState(struct rxrpc_call *call); +static void _SRXAFSCM_Probe(struct rxrpc_call *call); + +typedef void (*_SRXAFSCM_xxxx_t)(struct rxrpc_call *call); + +static const struct rxrpc_operation AFSCM_ops[] = { + { + .id = 204, + .asize = RXRPC_APP_MARK_EOF, + .name = "CallBack", + .user = _SRXAFSCM_CallBack, + }, + { + .id = 205, + .asize = RXRPC_APP_MARK_EOF, + .name = "InitCallBackState", + .user = _SRXAFSCM_InitCallBackState, + }, + { + .id = 206, + .asize = RXRPC_APP_MARK_EOF, + .name = "Probe", + .user = _SRXAFSCM_Probe, + }, +#if 0 + { + .id = 207, + .asize = RXRPC_APP_MARK_EOF, + .name = "GetLock", + .user = _SRXAFSCM_GetLock, + }, + { + .id = 208, + .asize = RXRPC_APP_MARK_EOF, + .name = "GetCE", + .user = _SRXAFSCM_GetCE, + }, + { + .id = 209, + .asize = RXRPC_APP_MARK_EOF, + .name = "GetXStatsVersion", + .user = _SRXAFSCM_GetXStatsVersion, + }, + { + .id = 210, + .asize = RXRPC_APP_MARK_EOF, + .name = "GetXStats", + .user = _SRXAFSCM_GetXStats, + } +#endif +}; + +static struct rxrpc_service AFSCM_service = { + .name = "AFS/CM", + .owner = THIS_MODULE, + .link = LIST_HEAD_INIT(AFSCM_service.link), + .new_call = afscm_new_call, + .service_id = 1, + .attn_func = afscm_attention, + .error_func = afscm_error, + .aemap_func = afscm_aemap, + .ops_begin = &AFSCM_ops[0], + .ops_end = &AFSCM_ops[sizeof(AFSCM_ops) / sizeof(AFSCM_ops[0])], +}; + +static DECLARE_COMPLETION(kafscmd_alive); +static DECLARE_COMPLETION(kafscmd_dead); +static DECLARE_WAIT_QUEUE_HEAD(kafscmd_sleepq); +static LIST_HEAD(kafscmd_attention_list); +static LIST_HEAD(afscm_calls); +static DEFINE_SPINLOCK(afscm_calls_lock); +static DEFINE_SPINLOCK(kafscmd_attention_lock); +static int kafscmd_die; + +/*****************************************************************************/ +/* + * AFS Cache Manager kernel thread + */ +static int kafscmd(void *arg) +{ + DECLARE_WAITQUEUE(myself, current); + + struct rxrpc_call *call; + _SRXAFSCM_xxxx_t func; + int die; + + printk("kAFS: Started kafscmd %d\n", current->pid); + + daemonize("kafscmd"); + + complete(&kafscmd_alive); + + /* loop around looking for things to attend to */ + do { + if (list_empty(&kafscmd_attention_list)) { + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&kafscmd_sleepq, &myself); + + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (!list_empty(&kafscmd_attention_list) || + signal_pending(current) || + kafscmd_die) + break; + + schedule(); + } + + remove_wait_queue(&kafscmd_sleepq, &myself); + set_current_state(TASK_RUNNING); + } + + die = kafscmd_die; + + /* dequeue the next call requiring attention */ + call = NULL; + spin_lock(&kafscmd_attention_lock); + + if (!list_empty(&kafscmd_attention_list)) { + call = list_entry(kafscmd_attention_list.next, + struct rxrpc_call, + app_attn_link); + list_del_init(&call->app_attn_link); + die = 0; + } + + spin_unlock(&kafscmd_attention_lock); + + if (call) { + /* act upon it */ + _debug("@@@ Begin Attend Call %p", call); + + func = call->app_user; + if (func) + func(call); + + rxrpc_put_call(call); + + _debug("@@@ End Attend Call %p", call); + } + + } while(!die); + + /* and that's all */ + complete_and_exit(&kafscmd_dead, 0); + +} /* end kafscmd() */ + +/*****************************************************************************/ +/* + * handle a call coming in to the cache manager + * - if I want to keep the call, I must increment its usage count + * - the return value will be negated and passed back in an abort packet if + * non-zero + * - serialised by virtue of there only being one krxiod + */ +static int afscm_new_call(struct rxrpc_call *call) +{ + _enter("%p{cid=%u u=%d}", + call, ntohl(call->call_id), atomic_read(&call->usage)); + + rxrpc_get_call(call); + + /* add to my current call list */ + spin_lock(&afscm_calls_lock); + list_add(&call->app_link,&afscm_calls); + spin_unlock(&afscm_calls_lock); + + _leave(" = 0"); + return 0; + +} /* end afscm_new_call() */ + +/*****************************************************************************/ +/* + * queue on the kafscmd queue for attention + */ +static void afscm_attention(struct rxrpc_call *call) +{ + _enter("%p{cid=%u u=%d}", + call, ntohl(call->call_id), atomic_read(&call->usage)); + + spin_lock(&kafscmd_attention_lock); + + if (list_empty(&call->app_attn_link)) { + list_add_tail(&call->app_attn_link, &kafscmd_attention_list); + rxrpc_get_call(call); + } + + spin_unlock(&kafscmd_attention_lock); + + wake_up(&kafscmd_sleepq); + + _leave(" {u=%d}", atomic_read(&call->usage)); +} /* end afscm_attention() */ + +/*****************************************************************************/ +/* + * handle my call being aborted + * - clean up, dequeue and put my ref to the call + */ +static void afscm_error(struct rxrpc_call *call) +{ + int removed; + + _enter("%p{est=%s ac=%u er=%d}", + call, + rxrpc_call_error_states[call->app_err_state], + call->app_abort_code, + call->app_errno); + + spin_lock(&kafscmd_attention_lock); + + if (list_empty(&call->app_attn_link)) { + list_add_tail(&call->app_attn_link, &kafscmd_attention_list); + rxrpc_get_call(call); + } + + spin_unlock(&kafscmd_attention_lock); + + removed = 0; + spin_lock(&afscm_calls_lock); + if (!list_empty(&call->app_link)) { + list_del_init(&call->app_link); + removed = 1; + } + spin_unlock(&afscm_calls_lock); + + if (removed) + rxrpc_put_call(call); + + wake_up(&kafscmd_sleepq); + + _leave(""); +} /* end afscm_error() */ + +/*****************************************************************************/ +/* + * map afs abort codes to/from Linux error codes + * - called with call->lock held + */ +static void afscm_aemap(struct rxrpc_call *call) +{ + switch (call->app_err_state) { + case RXRPC_ESTATE_LOCAL_ABORT: + call->app_abort_code = -call->app_errno; + break; + case RXRPC_ESTATE_PEER_ABORT: + call->app_errno = -ECONNABORTED; + break; + default: + break; + } +} /* end afscm_aemap() */ + +/*****************************************************************************/ +/* + * start the cache manager service if not already started + */ +int afscm_start(void) +{ + int ret; + + down_write(&afscm_sem); + if (!afscm_usage) { + ret = kernel_thread(kafscmd, NULL, 0); + if (ret < 0) + goto out; + + wait_for_completion(&kafscmd_alive); + + ret = rxrpc_add_service(afs_transport, &AFSCM_service); + if (ret < 0) + goto kill; + + afs_kafstimod_add_timer(&afs_mntpt_expiry_timer, + afs_mntpt_expiry_timeout * HZ); + } + + afscm_usage++; + up_write(&afscm_sem); + + return 0; + + kill: + kafscmd_die = 1; + wake_up(&kafscmd_sleepq); + wait_for_completion(&kafscmd_dead); + + out: + up_write(&afscm_sem); + return ret; + +} /* end afscm_start() */ + +/*****************************************************************************/ +/* + * stop the cache manager service + */ +void afscm_stop(void) +{ + struct rxrpc_call *call; + + down_write(&afscm_sem); + + BUG_ON(afscm_usage == 0); + afscm_usage--; + + if (afscm_usage == 0) { + /* don't want more incoming calls */ + rxrpc_del_service(afs_transport, &AFSCM_service); + + /* abort any calls I've still got open (the afscm_error() will + * dequeue them) */ + spin_lock(&afscm_calls_lock); + while (!list_empty(&afscm_calls)) { + call = list_entry(afscm_calls.next, + struct rxrpc_call, + app_link); + + list_del_init(&call->app_link); + rxrpc_get_call(call); + spin_unlock(&afscm_calls_lock); + + rxrpc_call_abort(call, -ESRCH); /* abort, dequeue and + * put */ + + _debug("nuking active call %08x.%d", + ntohl(call->conn->conn_id), + ntohl(call->call_id)); + rxrpc_put_call(call); + rxrpc_put_call(call); + + spin_lock(&afscm_calls_lock); + } + spin_unlock(&afscm_calls_lock); + + /* get rid of my daemon */ + kafscmd_die = 1; + wake_up(&kafscmd_sleepq); + wait_for_completion(&kafscmd_dead); + + /* dispose of any calls waiting for attention */ + spin_lock(&kafscmd_attention_lock); + while (!list_empty(&kafscmd_attention_list)) { + call = list_entry(kafscmd_attention_list.next, + struct rxrpc_call, + app_attn_link); + + list_del_init(&call->app_attn_link); + spin_unlock(&kafscmd_attention_lock); + + rxrpc_put_call(call); + + spin_lock(&kafscmd_attention_lock); + } + spin_unlock(&kafscmd_attention_lock); + + afs_kafstimod_del_timer(&afs_mntpt_expiry_timer); + } + + up_write(&afscm_sem); + +} /* end afscm_stop() */ + +/*****************************************************************************/ +/* + * handle the fileserver breaking a set of callbacks + */ +static void _SRXAFSCM_CallBack(struct rxrpc_call *call) +{ + struct afs_server *server; + size_t count, qty, tmp; + int ret = 0, removed; + + _enter("%p{acs=%s}", call, rxrpc_call_states[call->app_call_state]); + + server = afs_server_get_from_peer(call->conn->peer); + + switch (call->app_call_state) { + /* we've received the last packet + * - drain all the data from the call and send the reply + */ + case RXRPC_CSTATE_SRVR_GOT_ARGS: + ret = -EBADMSG; + qty = call->app_ready_qty; + if (qty < 8 || qty > 50 * (6 * 4) + 8) + break; + + { + struct afs_callback *cb, *pcb; + int loop; + __be32 *fp, *bp; + + fp = rxrpc_call_alloc_scratch(call, qty); + + /* drag the entire argument block out to the scratch + * space */ + ret = rxrpc_call_read_data(call, fp, qty, 0); + if (ret < 0) + break; + + /* and unmarshall the parameter block */ + ret = -EBADMSG; + count = ntohl(*fp++); + if (count>AFSCBMAX || + (count * (3 * 4) + 8 != qty && + count * (6 * 4) + 8 != qty)) + break; + + bp = fp + count*3; + tmp = ntohl(*bp++); + if (tmp > 0 && tmp != count) + break; + if (tmp == 0) + bp = NULL; + + pcb = cb = rxrpc_call_alloc_scratch_s( + call, struct afs_callback); + + for (loop = count - 1; loop >= 0; loop--) { + pcb->fid.vid = ntohl(*fp++); + pcb->fid.vnode = ntohl(*fp++); + pcb->fid.unique = ntohl(*fp++); + if (bp) { + pcb->version = ntohl(*bp++); + pcb->expiry = ntohl(*bp++); + pcb->type = ntohl(*bp++); + } + else { + pcb->version = 0; + pcb->expiry = 0; + pcb->type = AFSCM_CB_UNTYPED; + } + pcb++; + } + + /* invoke the actual service routine */ + ret = SRXAFSCM_CallBack(server, count, cb); + if (ret < 0) + break; + } + + /* send the reply */ + ret = rxrpc_call_write_data(call, 0, NULL, RXRPC_LAST_PACKET, + GFP_KERNEL, 0, &count); + if (ret < 0) + break; + break; + + /* operation complete */ + case RXRPC_CSTATE_COMPLETE: + call->app_user = NULL; + removed = 0; + spin_lock(&afscm_calls_lock); + if (!list_empty(&call->app_link)) { + list_del_init(&call->app_link); + removed = 1; + } + spin_unlock(&afscm_calls_lock); + + if (removed) + rxrpc_put_call(call); + break; + + /* operation terminated on error */ + case RXRPC_CSTATE_ERROR: + call->app_user = NULL; + break; + + default: + break; + } + + if (ret < 0) + rxrpc_call_abort(call, ret); + + afs_put_server(server); + + _leave(" = %d", ret); + +} /* end _SRXAFSCM_CallBack() */ + +/*****************************************************************************/ +/* + * handle the fileserver asking us to initialise our callback state + */ +static void _SRXAFSCM_InitCallBackState(struct rxrpc_call *call) +{ + struct afs_server *server; + size_t count; + int ret = 0, removed; + + _enter("%p{acs=%s}", call, rxrpc_call_states[call->app_call_state]); + + server = afs_server_get_from_peer(call->conn->peer); + + switch (call->app_call_state) { + /* we've received the last packet - drain all the data from the + * call */ + case RXRPC_CSTATE_SRVR_GOT_ARGS: + /* shouldn't be any args */ + ret = -EBADMSG; + break; + + /* send the reply when asked for it */ + case RXRPC_CSTATE_SRVR_SND_REPLY: + /* invoke the actual service routine */ + ret = SRXAFSCM_InitCallBackState(server); + if (ret < 0) + break; + + ret = rxrpc_call_write_data(call, 0, NULL, RXRPC_LAST_PACKET, + GFP_KERNEL, 0, &count); + if (ret < 0) + break; + break; + + /* operation complete */ + case RXRPC_CSTATE_COMPLETE: + call->app_user = NULL; + removed = 0; + spin_lock(&afscm_calls_lock); + if (!list_empty(&call->app_link)) { + list_del_init(&call->app_link); + removed = 1; + } + spin_unlock(&afscm_calls_lock); + + if (removed) + rxrpc_put_call(call); + break; + + /* operation terminated on error */ + case RXRPC_CSTATE_ERROR: + call->app_user = NULL; + break; + + default: + break; + } + + if (ret < 0) + rxrpc_call_abort(call, ret); + + afs_put_server(server); + + _leave(" = %d", ret); + +} /* end _SRXAFSCM_InitCallBackState() */ + +/*****************************************************************************/ +/* + * handle a probe from a fileserver + */ +static void _SRXAFSCM_Probe(struct rxrpc_call *call) +{ + struct afs_server *server; + size_t count; + int ret = 0, removed; + + _enter("%p{acs=%s}", call, rxrpc_call_states[call->app_call_state]); + + server = afs_server_get_from_peer(call->conn->peer); + + switch (call->app_call_state) { + /* we've received the last packet - drain all the data from the + * call */ + case RXRPC_CSTATE_SRVR_GOT_ARGS: + /* shouldn't be any args */ + ret = -EBADMSG; + break; + + /* send the reply when asked for it */ + case RXRPC_CSTATE_SRVR_SND_REPLY: + /* invoke the actual service routine */ + ret = SRXAFSCM_Probe(server); + if (ret < 0) + break; + + ret = rxrpc_call_write_data(call, 0, NULL, RXRPC_LAST_PACKET, + GFP_KERNEL, 0, &count); + if (ret < 0) + break; + break; + + /* operation complete */ + case RXRPC_CSTATE_COMPLETE: + call->app_user = NULL; + removed = 0; + spin_lock(&afscm_calls_lock); + if (!list_empty(&call->app_link)) { + list_del_init(&call->app_link); + removed = 1; + } + spin_unlock(&afscm_calls_lock); + + if (removed) + rxrpc_put_call(call); + break; + + /* operation terminated on error */ + case RXRPC_CSTATE_ERROR: + call->app_user = NULL; + break; + + default: + break; + } + + if (ret < 0) + rxrpc_call_abort(call, ret); + + afs_put_server(server); + + _leave(" = %d", ret); + +} /* end _SRXAFSCM_Probe() */ diff --git a/fs/afs/cmservice.h b/fs/afs/cmservice.h new file mode 100644 index 00000000000..af8d4d689cb --- /dev/null +++ b/fs/afs/cmservice.h @@ -0,0 +1,29 @@ +/* cmservice.h: AFS Cache Manager Service declarations + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_AFS_CMSERVICE_H +#define _LINUX_AFS_CMSERVICE_H + +#include <rxrpc/transport.h> +#include "types.h" + +/* cache manager start/stop */ +extern int afscm_start(void); +extern void afscm_stop(void); + +/* cache manager server functions */ +extern int SRXAFSCM_InitCallBackState(struct afs_server *server); +extern int SRXAFSCM_CallBack(struct afs_server *server, + size_t count, + struct afs_callback callbacks[]); +extern int SRXAFSCM_Probe(struct afs_server *server); + +#endif /* _LINUX_AFS_CMSERVICE_H */ diff --git a/fs/afs/dir.c b/fs/afs/dir.c new file mode 100644 index 00000000000..6682d6d7f29 --- /dev/null +++ b/fs/afs/dir.c @@ -0,0 +1,666 @@ +/* dir.c: AFS filesystem directory handling + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/fs.h> +#include <linux/pagemap.h> +#include <linux/smp_lock.h> +#include "vnode.h" +#include "volume.h" +#include <rxrpc/call.h> +#include "super.h" +#include "internal.h" + +static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd); +static int afs_dir_open(struct inode *inode, struct file *file); +static int afs_dir_readdir(struct file *file, void *dirent, filldir_t filldir); +static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd); +static int afs_d_delete(struct dentry *dentry); +static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen, + loff_t fpos, ino_t ino, unsigned dtype); + +struct file_operations afs_dir_file_operations = { + .open = afs_dir_open, + .readdir = afs_dir_readdir, +}; + +struct inode_operations afs_dir_inode_operations = { + .lookup = afs_dir_lookup, + .getattr = afs_inode_getattr, +#if 0 /* TODO */ + .create = afs_dir_create, + .link = afs_dir_link, + .unlink = afs_dir_unlink, + .symlink = afs_dir_symlink, + .mkdir = afs_dir_mkdir, + .rmdir = afs_dir_rmdir, + .mknod = afs_dir_mknod, + .rename = afs_dir_rename, +#endif +}; + +static struct dentry_operations afs_fs_dentry_operations = { + .d_revalidate = afs_d_revalidate, + .d_delete = afs_d_delete, +}; + +#define AFS_DIR_HASHTBL_SIZE 128 +#define AFS_DIR_DIRENT_SIZE 32 +#define AFS_DIRENT_PER_BLOCK 64 + +union afs_dirent { + struct { + uint8_t valid; + uint8_t unused[1]; + __be16 hash_next; + __be32 vnode; + __be32 unique; + uint8_t name[16]; + uint8_t overflow[4]; /* if any char of the name (inc + * NUL) reaches here, consume + * the next dirent too */ + } u; + uint8_t extended_name[32]; +}; + +/* AFS directory page header (one at the beginning of every 2048-byte chunk) */ +struct afs_dir_pagehdr { + __be16 npages; + __be16 magic; +#define AFS_DIR_MAGIC htons(1234) + uint8_t nentries; + uint8_t bitmap[8]; + uint8_t pad[19]; +}; + +/* directory block layout */ +union afs_dir_block { + + struct afs_dir_pagehdr pagehdr; + + struct { + struct afs_dir_pagehdr pagehdr; + uint8_t alloc_ctrs[128]; + /* dir hash table */ + uint16_t hashtable[AFS_DIR_HASHTBL_SIZE]; + } hdr; + + union afs_dirent dirents[AFS_DIRENT_PER_BLOCK]; +}; + +/* layout on a linux VM page */ +struct afs_dir_page { + union afs_dir_block blocks[PAGE_SIZE / sizeof(union afs_dir_block)]; +}; + +struct afs_dir_lookup_cookie { + struct afs_fid fid; + const char *name; + size_t nlen; + int found; +}; + +/*****************************************************************************/ +/* + * check that a directory page is valid + */ +static inline void afs_dir_check_page(struct inode *dir, struct page *page) +{ + struct afs_dir_page *dbuf; + loff_t latter; + int tmp, qty; + +#if 0 + /* check the page count */ + qty = desc.size / sizeof(dbuf->blocks[0]); + if (qty == 0) + goto error; + + if (page->index==0 && qty!=ntohs(dbuf->blocks[0].pagehdr.npages)) { + printk("kAFS: %s(%lu): wrong number of dir blocks %d!=%hu\n", + __FUNCTION__,dir->i_ino,qty,ntohs(dbuf->blocks[0].pagehdr.npages)); + goto error; + } +#endif + + /* determine how many magic numbers there should be in this page */ + latter = dir->i_size - (page->index << PAGE_CACHE_SHIFT); + if (latter >= PAGE_SIZE) + qty = PAGE_SIZE; + else + qty = latter; + qty /= sizeof(union afs_dir_block); + + /* check them */ + dbuf = page_address(page); + for (tmp = 0; tmp < qty; tmp++) { + if (dbuf->blocks[tmp].pagehdr.magic != AFS_DIR_MAGIC) { + printk("kAFS: %s(%lu): bad magic %d/%d is %04hx\n", + __FUNCTION__, dir->i_ino, tmp, qty, + ntohs(dbuf->blocks[tmp].pagehdr.magic)); + goto error; + } + } + + SetPageChecked(page); + return; + + error: + SetPageChecked(page); + SetPageError(page); + +} /* end afs_dir_check_page() */ + +/*****************************************************************************/ +/* + * discard a page cached in the pagecache + */ +static inline void afs_dir_put_page(struct page *page) +{ + kunmap(page); + page_cache_release(page); + +} /* end afs_dir_put_page() */ + +/*****************************************************************************/ +/* + * get a page into the pagecache + */ +static struct page *afs_dir_get_page(struct inode *dir, unsigned long index) +{ + struct page *page; + + _enter("{%lu},%lu", dir->i_ino, index); + + page = read_cache_page(dir->i_mapping,index, + (filler_t *) dir->i_mapping->a_ops->readpage, + NULL); + if (!IS_ERR(page)) { + wait_on_page_locked(page); + kmap(page); + if (!PageUptodate(page)) + goto fail; + if (!PageChecked(page)) + afs_dir_check_page(dir, page); + if (PageError(page)) + goto fail; + } + return page; + + fail: + afs_dir_put_page(page); + return ERR_PTR(-EIO); +} /* end afs_dir_get_page() */ + +/*****************************************************************************/ +/* + * open an AFS directory file + */ +static int afs_dir_open(struct inode *inode, struct file *file) +{ + _enter("{%lu}", inode->i_ino); + + BUG_ON(sizeof(union afs_dir_block) != 2048); + BUG_ON(sizeof(union afs_dirent) != 32); + + if (AFS_FS_I(inode)->flags & AFS_VNODE_DELETED) + return -ENOENT; + + _leave(" = 0"); + return 0; + +} /* end afs_dir_open() */ + +/*****************************************************************************/ +/* + * deal with one block in an AFS directory + */ +static int afs_dir_iterate_block(unsigned *fpos, + union afs_dir_block *block, + unsigned blkoff, + void *cookie, + filldir_t filldir) +{ + union afs_dirent *dire; + unsigned offset, next, curr; + size_t nlen; + int tmp, ret; + + _enter("%u,%x,%p,,",*fpos,blkoff,block); + + curr = (*fpos - blkoff) / sizeof(union afs_dirent); + + /* walk through the block, an entry at a time */ + for (offset = AFS_DIRENT_PER_BLOCK - block->pagehdr.nentries; + offset < AFS_DIRENT_PER_BLOCK; + offset = next + ) { + next = offset + 1; + + /* skip entries marked unused in the bitmap */ + if (!(block->pagehdr.bitmap[offset / 8] & + (1 << (offset % 8)))) { + _debug("ENT[%Zu.%u]: unused\n", + blkoff / sizeof(union afs_dir_block), offset); + if (offset >= curr) + *fpos = blkoff + + next * sizeof(union afs_dirent); + continue; + } + + /* got a valid entry */ + dire = &block->dirents[offset]; + nlen = strnlen(dire->u.name, + sizeof(*block) - + offset * sizeof(union afs_dirent)); + + _debug("ENT[%Zu.%u]: %s %Zu \"%s\"\n", + blkoff / sizeof(union afs_dir_block), offset, + (offset < curr ? "skip" : "fill"), + nlen, dire->u.name); + + /* work out where the next possible entry is */ + for (tmp = nlen; tmp > 15; tmp -= sizeof(union afs_dirent)) { + if (next >= AFS_DIRENT_PER_BLOCK) { + _debug("ENT[%Zu.%u]:" + " %u travelled beyond end dir block" + " (len %u/%Zu)\n", + blkoff / sizeof(union afs_dir_block), + offset, next, tmp, nlen); + return -EIO; + } + if (!(block->pagehdr.bitmap[next / 8] & + (1 << (next % 8)))) { + _debug("ENT[%Zu.%u]:" + " %u unmarked extension (len %u/%Zu)\n", + blkoff / sizeof(union afs_dir_block), + offset, next, tmp, nlen); + return -EIO; + } + + _debug("ENT[%Zu.%u]: ext %u/%Zu\n", + blkoff / sizeof(union afs_dir_block), + next, tmp, nlen); + next++; + } + + /* skip if starts before the current position */ + if (offset < curr) + continue; + + /* found the next entry */ + ret = filldir(cookie, + dire->u.name, + nlen, + blkoff + offset * sizeof(union afs_dirent), + ntohl(dire->u.vnode), + filldir == afs_dir_lookup_filldir ? + ntohl(dire->u.unique) : DT_UNKNOWN); + if (ret < 0) { + _leave(" = 0 [full]"); + return 0; + } + + *fpos = blkoff + next * sizeof(union afs_dirent); + } + + _leave(" = 1 [more]"); + return 1; +} /* end afs_dir_iterate_block() */ + +/*****************************************************************************/ +/* + * read an AFS directory + */ +static int afs_dir_iterate(struct inode *dir, unsigned *fpos, void *cookie, + filldir_t filldir) +{ + union afs_dir_block *dblock; + struct afs_dir_page *dbuf; + struct page *page; + unsigned blkoff, limit; + int ret; + + _enter("{%lu},%u,,", dir->i_ino, *fpos); + + if (AFS_FS_I(dir)->flags & AFS_VNODE_DELETED) { + _leave(" = -ESTALE"); + return -ESTALE; + } + + /* round the file position up to the next entry boundary */ + *fpos += sizeof(union afs_dirent) - 1; + *fpos &= ~(sizeof(union afs_dirent) - 1); + + /* walk through the blocks in sequence */ + ret = 0; + while (*fpos < dir->i_size) { + blkoff = *fpos & ~(sizeof(union afs_dir_block) - 1); + + /* fetch the appropriate page from the directory */ + page = afs_dir_get_page(dir, blkoff / PAGE_SIZE); + if (IS_ERR(page)) { + ret = PTR_ERR(page); + break; + } + + limit = blkoff & ~(PAGE_SIZE - 1); + + dbuf = page_address(page); + + /* deal with the individual blocks stashed on this page */ + do { + dblock = &dbuf->blocks[(blkoff % PAGE_SIZE) / + sizeof(union afs_dir_block)]; + ret = afs_dir_iterate_block(fpos, dblock, blkoff, + cookie, filldir); + if (ret != 1) { + afs_dir_put_page(page); + goto out; + } + + blkoff += sizeof(union afs_dir_block); + + } while (*fpos < dir->i_size && blkoff < limit); + + afs_dir_put_page(page); + ret = 0; + } + + out: + _leave(" = %d", ret); + return ret; +} /* end afs_dir_iterate() */ + +/*****************************************************************************/ +/* + * read an AFS directory + */ +static int afs_dir_readdir(struct file *file, void *cookie, filldir_t filldir) +{ + unsigned fpos; + int ret; + + _enter("{%Ld,{%lu}}", file->f_pos, file->f_dentry->d_inode->i_ino); + + fpos = file->f_pos; + ret = afs_dir_iterate(file->f_dentry->d_inode, &fpos, cookie, filldir); + file->f_pos = fpos; + + _leave(" = %d", ret); + return ret; +} /* end afs_dir_readdir() */ + +/*****************************************************************************/ +/* + * search the directory for a name + * - if afs_dir_iterate_block() spots this function, it'll pass the FID + * uniquifier through dtype + */ +static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen, + loff_t fpos, ino_t ino, unsigned dtype) +{ + struct afs_dir_lookup_cookie *cookie = _cookie; + + _enter("{%s,%Zu},%s,%u,,%lu,%u", + cookie->name, cookie->nlen, name, nlen, ino, dtype); + + if (cookie->nlen != nlen || memcmp(cookie->name, name, nlen) != 0) { + _leave(" = 0 [no]"); + return 0; + } + + cookie->fid.vnode = ino; + cookie->fid.unique = dtype; + cookie->found = 1; + + _leave(" = -1 [found]"); + return -1; +} /* end afs_dir_lookup_filldir() */ + +/*****************************************************************************/ +/* + * look up an entry in a directory + */ +static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) +{ + struct afs_dir_lookup_cookie cookie; + struct afs_super_info *as; + struct afs_vnode *vnode; + struct inode *inode; + unsigned fpos; + int ret; + + _enter("{%lu},%p{%s}", dir->i_ino, dentry, dentry->d_name.name); + + /* insanity checks first */ + BUG_ON(sizeof(union afs_dir_block) != 2048); + BUG_ON(sizeof(union afs_dirent) != 32); + + if (dentry->d_name.len > 255) { + _leave(" = -ENAMETOOLONG"); + return ERR_PTR(-ENAMETOOLONG); + } + + vnode = AFS_FS_I(dir); + if (vnode->flags & AFS_VNODE_DELETED) { + _leave(" = -ESTALE"); + return ERR_PTR(-ESTALE); + } + + as = dir->i_sb->s_fs_info; + + /* search the directory */ + cookie.name = dentry->d_name.name; + cookie.nlen = dentry->d_name.len; + cookie.fid.vid = as->volume->vid; + cookie.found = 0; + + fpos = 0; + ret = afs_dir_iterate(dir, &fpos, &cookie, afs_dir_lookup_filldir); + if (ret < 0) { + _leave(" = %d", ret); + return ERR_PTR(ret); + } + + ret = -ENOENT; + if (!cookie.found) { + _leave(" = %d", ret); + return ERR_PTR(ret); + } + + /* instantiate the dentry */ + ret = afs_iget(dir->i_sb, &cookie.fid, &inode); + if (ret < 0) { + _leave(" = %d", ret); + return ERR_PTR(ret); + } + + dentry->d_op = &afs_fs_dentry_operations; + dentry->d_fsdata = (void *) (unsigned long) vnode->status.version; + + d_add(dentry, inode); + _leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%lu }", + cookie.fid.vnode, + cookie.fid.unique, + dentry->d_inode->i_ino, + dentry->d_inode->i_version); + + return NULL; +} /* end afs_dir_lookup() */ + +/*****************************************************************************/ +/* + * check that a dentry lookup hit has found a valid entry + * - NOTE! the hit can be a negative hit too, so we can't assume we have an + * inode + * (derived from nfs_lookup_revalidate) + */ +static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd) +{ + struct afs_dir_lookup_cookie cookie; + struct dentry *parent; + struct inode *inode, *dir; + unsigned fpos; + int ret; + + _enter("{sb=%p n=%s},", dentry->d_sb, dentry->d_name.name); + + /* lock down the parent dentry so we can peer at it */ + parent = dget_parent(dentry->d_parent); + + dir = parent->d_inode; + inode = dentry->d_inode; + + /* handle a negative dentry */ + if (!inode) + goto out_bad; + + /* handle a bad inode */ + if (is_bad_inode(inode)) { + printk("kAFS: afs_d_revalidate: %s/%s has bad inode\n", + dentry->d_parent->d_name.name, dentry->d_name.name); + goto out_bad; + } + + /* force a full look up if the parent directory changed since last the + * server was consulted + * - otherwise this inode must still exist, even if the inode details + * themselves have changed + */ + if (AFS_FS_I(dir)->flags & AFS_VNODE_CHANGED) + afs_vnode_fetch_status(AFS_FS_I(dir)); + + if (AFS_FS_I(dir)->flags & AFS_VNODE_DELETED) { + _debug("%s: parent dir deleted", dentry->d_name.name); + goto out_bad; + } + + if (AFS_FS_I(inode)->flags & AFS_VNODE_DELETED) { + _debug("%s: file already deleted", dentry->d_name.name); + goto out_bad; + } + + if ((unsigned long) dentry->d_fsdata != + (unsigned long) AFS_FS_I(dir)->status.version) { + _debug("%s: parent changed %lu -> %u", + dentry->d_name.name, + (unsigned long) dentry->d_fsdata, + (unsigned) AFS_FS_I(dir)->status.version); + + /* search the directory for this vnode */ + cookie.name = dentry->d_name.name; + cookie.nlen = dentry->d_name.len; + cookie.fid.vid = AFS_FS_I(inode)->volume->vid; + cookie.found = 0; + + fpos = 0; + ret = afs_dir_iterate(dir, &fpos, &cookie, + afs_dir_lookup_filldir); + if (ret < 0) { + _debug("failed to iterate dir %s: %d", + parent->d_name.name, ret); + goto out_bad; + } + + if (!cookie.found) { + _debug("%s: dirent not found", dentry->d_name.name); + goto not_found; + } + + /* if the vnode ID has changed, then the dirent points to a + * different file */ + if (cookie.fid.vnode != AFS_FS_I(inode)->fid.vnode) { + _debug("%s: dirent changed", dentry->d_name.name); + goto not_found; + } + + /* if the vnode ID uniqifier has changed, then the file has + * been deleted */ + if (cookie.fid.unique != AFS_FS_I(inode)->fid.unique) { + _debug("%s: file deleted (uq %u -> %u I:%lu)", + dentry->d_name.name, + cookie.fid.unique, + AFS_FS_I(inode)->fid.unique, + inode->i_version); + spin_lock(&AFS_FS_I(inode)->lock); + AFS_FS_I(inode)->flags |= AFS_VNODE_DELETED; + spin_unlock(&AFS_FS_I(inode)->lock); + invalidate_remote_inode(inode); + goto out_bad; + } + + dentry->d_fsdata = + (void *) (unsigned long) AFS_FS_I(dir)->status.version; + } + + out_valid: + dput(parent); + _leave(" = 1 [valid]"); + return 1; + + /* the dirent, if it exists, now points to a different vnode */ + not_found: + spin_lock(&dentry->d_lock); + dentry->d_flags |= DCACHE_NFSFS_RENAMED; + spin_unlock(&dentry->d_lock); + + out_bad: + if (inode) { + /* don't unhash if we have submounts */ + if (have_submounts(dentry)) + goto out_valid; + } + + shrink_dcache_parent(dentry); + + _debug("dropping dentry %s/%s", + dentry->d_parent->d_name.name, dentry->d_name.name); + d_drop(dentry); + + dput(parent); + + _leave(" = 0 [bad]"); + return 0; +} /* end afs_d_revalidate() */ + +/*****************************************************************************/ +/* + * allow the VFS to enquire as to whether a dentry should be unhashed (mustn't + * sleep) + * - called from dput() when d_count is going to 0. + * - return 1 to request dentry be unhashed, 0 otherwise + */ +static int afs_d_delete(struct dentry *dentry) +{ + _enter("%s", dentry->d_name.name); + + if (dentry->d_flags & DCACHE_NFSFS_RENAMED) + goto zap; + + if (dentry->d_inode) { + if (AFS_FS_I(dentry->d_inode)->flags & AFS_VNODE_DELETED) + goto zap; + } + + _leave(" = 0 [keep]"); + return 0; + + zap: + _leave(" = 1 [zap]"); + return 1; +} /* end afs_d_delete() */ diff --git a/fs/afs/errors.h b/fs/afs/errors.h new file mode 100644 index 00000000000..574d94ac8d0 --- /dev/null +++ b/fs/afs/errors.h @@ -0,0 +1,34 @@ +/* errors.h: AFS abort/error codes + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_AFS_ERRORS_H +#define _LINUX_AFS_ERRORS_H + +#include "types.h" + +/* file server abort codes */ +typedef enum { + VSALVAGE = 101, /* volume needs salvaging */ + VNOVNODE = 102, /* no such file/dir (vnode) */ + VNOVOL = 103, /* no such volume or volume unavailable */ + VVOLEXISTS = 104, /* volume name already exists */ + VNOSERVICE = 105, /* volume not currently in service */ + VOFFLINE = 106, /* volume is currently offline (more info available [VVL-spec]) */ + VONLINE = 107, /* volume is already online */ + VDISKFULL = 108, /* disk partition is full */ + VOVERQUOTA = 109, /* volume's maximum quota exceeded */ + VBUSY = 110, /* volume is temporarily unavailable */ + VMOVED = 111, /* volume moved to new server - ask this FS where */ +} afs_rxfs_abort_t; + +extern int afs_abort_to_error(int abortcode); + +#endif /* _LINUX_AFS_ERRORS_H */ diff --git a/fs/afs/file.c b/fs/afs/file.c new file mode 100644 index 00000000000..6b6bb7c8abf --- /dev/null +++ b/fs/afs/file.c @@ -0,0 +1,305 @@ +/* file.c: AFS filesystem file handling + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/fs.h> +#include <linux/pagemap.h> +#include <linux/buffer_head.h> +#include "volume.h" +#include "vnode.h" +#include <rxrpc/call.h> +#include "internal.h" + +#if 0 +static int afs_file_open(struct inode *inode, struct file *file); +static int afs_file_release(struct inode *inode, struct file *file); +#endif + +static int afs_file_readpage(struct file *file, struct page *page); +static int afs_file_invalidatepage(struct page *page, unsigned long offset); +static int afs_file_releasepage(struct page *page, int gfp_flags); + +static ssize_t afs_file_write(struct file *file, const char __user *buf, + size_t size, loff_t *off); + +struct inode_operations afs_file_inode_operations = { + .getattr = afs_inode_getattr, +}; + +struct file_operations afs_file_file_operations = { + .read = generic_file_read, + .write = afs_file_write, + .mmap = generic_file_mmap, +#if 0 + .open = afs_file_open, + .release = afs_file_release, + .fsync = afs_file_fsync, +#endif +}; + +struct address_space_operations afs_fs_aops = { + .readpage = afs_file_readpage, + .sync_page = block_sync_page, + .set_page_dirty = __set_page_dirty_nobuffers, + .releasepage = afs_file_releasepage, + .invalidatepage = afs_file_invalidatepage, +}; + +/*****************************************************************************/ +/* + * AFS file write + */ +static ssize_t afs_file_write(struct file *file, const char __user *buf, + size_t size, loff_t *off) +{ + struct afs_vnode *vnode; + + vnode = AFS_FS_I(file->f_dentry->d_inode); + if (vnode->flags & AFS_VNODE_DELETED) + return -ESTALE; + + return -EIO; +} /* end afs_file_write() */ + +/*****************************************************************************/ +/* + * deal with notification that a page was read from the cache + */ +#ifdef AFS_CACHING_SUPPORT +static void afs_file_readpage_read_complete(void *cookie_data, + struct page *page, + void *data, + int error) +{ + _enter("%p,%p,%p,%d", cookie_data, page, data, error); + + if (error) + SetPageError(page); + else + SetPageUptodate(page); + unlock_page(page); + +} /* end afs_file_readpage_read_complete() */ +#endif + +/*****************************************************************************/ +/* + * deal with notification that a page was written to the cache + */ +#ifdef AFS_CACHING_SUPPORT +static void afs_file_readpage_write_complete(void *cookie_data, + struct page *page, + void *data, + int error) +{ + _enter("%p,%p,%p,%d", cookie_data, page, data, error); + + unlock_page(page); + +} /* end afs_file_readpage_write_complete() */ +#endif + +/*****************************************************************************/ +/* + * AFS read page from file (or symlink) + */ +static int afs_file_readpage(struct file *file, struct page *page) +{ + struct afs_rxfs_fetch_descriptor desc; +#ifdef AFS_CACHING_SUPPORT + struct cachefs_page *pageio; +#endif + struct afs_vnode *vnode; + struct inode *inode; + int ret; + + inode = page->mapping->host; + + _enter("{%lu},{%lu}", inode->i_ino, page->index); + + vnode = AFS_FS_I(inode); + + if (!PageLocked(page)) + PAGE_BUG(page); + + ret = -ESTALE; + if (vnode->flags & AFS_VNODE_DELETED) + goto error; + +#ifdef AFS_CACHING_SUPPORT + ret = cachefs_page_get_private(page, &pageio, GFP_NOIO); + if (ret < 0) + goto error; + + /* is it cached? */ + ret = cachefs_read_or_alloc_page(vnode->cache, + page, + afs_file_readpage_read_complete, + NULL, + GFP_KERNEL); +#else + ret = -ENOBUFS; +#endif + + switch (ret) { + /* read BIO submitted and wb-journal entry found */ + case 1: + BUG(); // TODO - handle wb-journal match + + /* read BIO submitted (page in cache) */ + case 0: + break; + + /* no page available in cache */ + case -ENOBUFS: + case -ENODATA: + default: + desc.fid = vnode->fid; + desc.offset = page->index << PAGE_CACHE_SHIFT; + desc.size = min((size_t) (inode->i_size - desc.offset), + (size_t) PAGE_SIZE); + desc.buffer = kmap(page); + + clear_page(desc.buffer); + + /* read the contents of the file from the server into the + * page */ + ret = afs_vnode_fetch_data(vnode, &desc); + kunmap(page); + if (ret < 0) { + if (ret==-ENOENT) { + _debug("got NOENT from server" + " - marking file deleted and stale"); + vnode->flags |= AFS_VNODE_DELETED; + ret = -ESTALE; + } + +#ifdef AFS_CACHING_SUPPORT + cachefs_uncache_page(vnode->cache, page); +#endif + goto error; + } + + SetPageUptodate(page); + +#ifdef AFS_CACHING_SUPPORT + if (cachefs_write_page(vnode->cache, + page, + afs_file_readpage_write_complete, + NULL, + GFP_KERNEL) != 0 + ) { + cachefs_uncache_page(vnode->cache, page); + unlock_page(page); + } +#else + unlock_page(page); +#endif + } + + _leave(" = 0"); + return 0; + + error: + SetPageError(page); + unlock_page(page); + + _leave(" = %d", ret); + return ret; + +} /* end afs_file_readpage() */ + +/*****************************************************************************/ +/* + * get a page cookie for the specified page + */ +#ifdef AFS_CACHING_SUPPORT +int afs_cache_get_page_cookie(struct page *page, + struct cachefs_page **_page_cookie) +{ + int ret; + + _enter(""); + ret = cachefs_page_get_private(page,_page_cookie, GFP_NOIO); + + _leave(" = %d", ret); + return ret; +} /* end afs_cache_get_page_cookie() */ +#endif + +/*****************************************************************************/ +/* + * invalidate part or all of a page + */ +static int afs_file_invalidatepage(struct page *page, unsigned long offset) +{ + int ret = 1; + + _enter("{%lu},%lu", page->index, offset); + + BUG_ON(!PageLocked(page)); + + if (PagePrivate(page)) { +#ifdef AFS_CACHING_SUPPORT + struct afs_vnode *vnode = AFS_FS_I(page->mapping->host); + cachefs_uncache_page(vnode->cache,page); +#endif + + /* We release buffers only if the entire page is being + * invalidated. + * The get_block cached value has been unconditionally + * invalidated, so real IO is not possible anymore. + */ + if (offset == 0) { + BUG_ON(!PageLocked(page)); + + ret = 0; + if (!PageWriteback(page)) + ret = page->mapping->a_ops->releasepage(page, + 0); + } + } + + _leave(" = %d", ret); + return ret; +} /* end afs_file_invalidatepage() */ + +/*****************************************************************************/ +/* + * release a page and cleanup its private data + */ +static int afs_file_releasepage(struct page *page, int gfp_flags) +{ + struct cachefs_page *pageio; + + _enter("{%lu},%x", page->index, gfp_flags); + + if (PagePrivate(page)) { +#ifdef AFS_CACHING_SUPPORT + struct afs_vnode *vnode = AFS_FS_I(page->mapping->host); + cachefs_uncache_page(vnode->cache, page); +#endif + + pageio = (struct cachefs_page *) page->private; + page->private = 0; + ClearPagePrivate(page); + + if (pageio) + kfree(pageio); + } + + _leave(" = 0"); + return 0; +} /* end afs_file_releasepage() */ diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c new file mode 100644 index 00000000000..61bc371532a --- /dev/null +++ b/fs/afs/fsclient.c @@ -0,0 +1,837 @@ +/* fsclient.c: AFS File Server client stubs + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/init.h> +#include <linux/sched.h> +#include <rxrpc/rxrpc.h> +#include <rxrpc/transport.h> +#include <rxrpc/connection.h> +#include <rxrpc/call.h> +#include "fsclient.h" +#include "cmservice.h" +#include "vnode.h" +#include "server.h" +#include "errors.h" +#include "internal.h" + +#define FSFETCHSTATUS 132 /* AFS Fetch file status */ +#define FSFETCHDATA 130 /* AFS Fetch file data */ +#define FSGIVEUPCALLBACKS 147 /* AFS Discard callback promises */ +#define FSGETVOLUMEINFO 148 /* AFS Get root volume information */ +#define FSGETROOTVOLUME 151 /* AFS Get root volume name */ +#define FSLOOKUP 161 /* AFS lookup file in directory */ + +/*****************************************************************************/ +/* + * map afs abort codes to/from Linux error codes + * - called with call->lock held + */ +static void afs_rxfs_aemap(struct rxrpc_call *call) +{ + switch (call->app_err_state) { + case RXRPC_ESTATE_LOCAL_ABORT: + call->app_abort_code = -call->app_errno; + break; + case RXRPC_ESTATE_PEER_ABORT: + call->app_errno = afs_abort_to_error(call->app_abort_code); + break; + default: + break; + } +} /* end afs_rxfs_aemap() */ + +/*****************************************************************************/ +/* + * get the root volume name from a fileserver + * - this operation doesn't seem to work correctly in OpenAFS server 1.2.2 + */ +#if 0 +int afs_rxfs_get_root_volume(struct afs_server *server, + char *buf, size_t *buflen) +{ + struct rxrpc_connection *conn; + struct rxrpc_call *call; + struct kvec piov[2]; + size_t sent; + int ret; + u32 param[1]; + + DECLARE_WAITQUEUE(myself, current); + + kenter("%p,%p,%u",server, buf, *buflen); + + /* get hold of the fileserver connection */ + ret = afs_server_get_fsconn(server, &conn); + if (ret < 0) + goto out; + + /* create a call through that connection */ + ret = rxrpc_create_call(conn, NULL, NULL, afs_rxfs_aemap, &call); + if (ret < 0) { + printk("kAFS: Unable to create call: %d\n", ret); + goto out_put_conn; + } + call->app_opcode = FSGETROOTVOLUME; + + /* we want to get event notifications from the call */ + add_wait_queue(&call->waitq, &myself); + + /* marshall the parameters */ + param[0] = htonl(FSGETROOTVOLUME); + + piov[0].iov_len = sizeof(param); + piov[0].iov_base = param; + + /* send the parameters to the server */ + ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, GFP_NOFS, + 0, &sent); + if (ret < 0) + goto abort; + + /* wait for the reply to completely arrive */ + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (call->app_call_state != RXRPC_CSTATE_CLNT_RCV_REPLY || + signal_pending(current)) + break; + schedule(); + } + set_current_state(TASK_RUNNING); + + ret = -EINTR; + if (signal_pending(current)) + goto abort; + + switch (call->app_call_state) { + case RXRPC_CSTATE_ERROR: + ret = call->app_errno; + kdebug("Got Error: %d", ret); + goto out_unwait; + + case RXRPC_CSTATE_CLNT_GOT_REPLY: + /* read the reply */ + kdebug("Got Reply: qty=%d", call->app_ready_qty); + + ret = -EBADMSG; + if (call->app_ready_qty <= 4) + goto abort; + + ret = rxrpc_call_read_data(call, NULL, call->app_ready_qty, 0); + if (ret < 0) + goto abort; + +#if 0 + /* unmarshall the reply */ + bp = buffer; + for (loop = 0; loop < 65; loop++) + entry->name[loop] = ntohl(*bp++); + entry->name[64] = 0; + + entry->type = ntohl(*bp++); + entry->num_servers = ntohl(*bp++); + + for (loop = 0; loop < 8; loop++) + entry->servers[loop].addr.s_addr = *bp++; + + for (loop = 0; loop < 8; loop++) + entry->servers[loop].partition = ntohl(*bp++); + + for (loop = 0; loop < 8; loop++) + entry->servers[loop].flags = ntohl(*bp++); + + for (loop = 0; loop < 3; loop++) + entry->volume_ids[loop] = ntohl(*bp++); + + entry->clone_id = ntohl(*bp++); + entry->flags = ntohl(*bp); +#endif + + /* success */ + ret = 0; + goto out_unwait; + + default: + BUG(); + } + + abort: + set_current_state(TASK_UNINTERRUPTIBLE); + rxrpc_call_abort(call, ret); + schedule(); + out_unwait: + set_current_state(TASK_RUNNING); + remove_wait_queue(&call->waitq, &myself); + rxrpc_put_call(call); + out_put_conn: + afs_server_release_fsconn(server, conn); + out: + kleave(""); + return ret; +} /* end afs_rxfs_get_root_volume() */ +#endif + +/*****************************************************************************/ +/* + * get information about a volume + */ +#if 0 +int afs_rxfs_get_volume_info(struct afs_server *server, + const char *name, + struct afs_volume_info *vinfo) +{ + struct rxrpc_connection *conn; + struct rxrpc_call *call; + struct kvec piov[3]; + size_t sent; + int ret; + u32 param[2], *bp, zero; + + DECLARE_WAITQUEUE(myself, current); + + _enter("%p,%s,%p", server, name, vinfo); + + /* get hold of the fileserver connection */ + ret = afs_server_get_fsconn(server, &conn); + if (ret < 0) + goto out; + + /* create a call through that connection */ + ret = rxrpc_create_call(conn, NULL, NULL, afs_rxfs_aemap, &call); + if (ret < 0) { + printk("kAFS: Unable to create call: %d\n", ret); + goto out_put_conn; + } + call->app_opcode = FSGETVOLUMEINFO; + + /* we want to get event notifications from the call */ + add_wait_queue(&call->waitq, &myself); + + /* marshall the parameters */ + piov[1].iov_len = strlen(name); + piov[1].iov_base = (char *) name; + + zero = 0; + piov[2].iov_len = (4 - (piov[1].iov_len & 3)) & 3; + piov[2].iov_base = &zero; + + param[0] = htonl(FSGETVOLUMEINFO); + param[1] = htonl(piov[1].iov_len); + + piov[0].iov_len = sizeof(param); + piov[0].iov_base = param; + + /* send the parameters to the server */ + ret = rxrpc_call_write_data(call, 3, piov, RXRPC_LAST_PACKET, GFP_NOFS, + 0, &sent); + if (ret < 0) + goto abort; + + /* wait for the reply to completely arrive */ + bp = rxrpc_call_alloc_scratch(call, 64); + + ret = rxrpc_call_read_data(call, bp, 64, + RXRPC_CALL_READ_BLOCK | + RXRPC_CALL_READ_ALL); + if (ret < 0) { + if (ret == -ECONNABORTED) { + ret = call->app_errno; + goto out_unwait; + } + goto abort; + } + + /* unmarshall the reply */ + vinfo->vid = ntohl(*bp++); + vinfo->type = ntohl(*bp++); + + vinfo->type_vids[0] = ntohl(*bp++); + vinfo->type_vids[1] = ntohl(*bp++); + vinfo->type_vids[2] = ntohl(*bp++); + vinfo->type_vids[3] = ntohl(*bp++); + vinfo->type_vids[4] = ntohl(*bp++); + + vinfo->nservers = ntohl(*bp++); + vinfo->servers[0].addr.s_addr = *bp++; + vinfo->servers[1].addr.s_addr = *bp++; + vinfo->servers[2].addr.s_addr = *bp++; + vinfo->servers[3].addr.s_addr = *bp++; + vinfo->servers[4].addr.s_addr = *bp++; + vinfo->servers[5].addr.s_addr = *bp++; + vinfo->servers[6].addr.s_addr = *bp++; + vinfo->servers[7].addr.s_addr = *bp++; + + ret = -EBADMSG; + if (vinfo->nservers > 8) + goto abort; + + /* success */ + ret = 0; + + out_unwait: + set_current_state(TASK_RUNNING); + remove_wait_queue(&call->waitq, &myself); + rxrpc_put_call(call); + out_put_conn: + afs_server_release_fsconn(server, conn); + out: + _leave(""); + return ret; + + abort: + set_current_state(TASK_UNINTERRUPTIBLE); + rxrpc_call_abort(call, ret); + schedule(); + goto out_unwait; + +} /* end afs_rxfs_get_volume_info() */ +#endif + +/*****************************************************************************/ +/* + * fetch the status information for a file + */ +int afs_rxfs_fetch_file_status(struct afs_server *server, + struct afs_vnode *vnode, + struct afs_volsync *volsync) +{ + struct afs_server_callslot callslot; + struct rxrpc_call *call; + struct kvec piov[1]; + size_t sent; + int ret; + __be32 *bp; + + DECLARE_WAITQUEUE(myself, current); + + _enter("%p,{%u,%u,%u}", + server, vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique); + + /* get hold of the fileserver connection */ + ret = afs_server_request_callslot(server, &callslot); + if (ret < 0) + goto out; + + /* create a call through that connection */ + ret = rxrpc_create_call(callslot.conn, NULL, NULL, afs_rxfs_aemap, + &call); + if (ret < 0) { + printk("kAFS: Unable to create call: %d\n", ret); + goto out_put_conn; + } + call->app_opcode = FSFETCHSTATUS; + + /* we want to get event notifications from the call */ + add_wait_queue(&call->waitq, &myself); + + /* marshall the parameters */ + bp = rxrpc_call_alloc_scratch(call, 16); + bp[0] = htonl(FSFETCHSTATUS); + bp[1] = htonl(vnode->fid.vid); + bp[2] = htonl(vnode->fid.vnode); + bp[3] = htonl(vnode->fid.unique); + + piov[0].iov_len = 16; + piov[0].iov_base = bp; + + /* send the parameters to the server */ + ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, GFP_NOFS, + 0, &sent); + if (ret < 0) + goto abort; + + /* wait for the reply to completely arrive */ + bp = rxrpc_call_alloc_scratch(call, 120); + + ret = rxrpc_call_read_data(call, bp, 120, + RXRPC_CALL_READ_BLOCK | + RXRPC_CALL_READ_ALL); + if (ret < 0) { + if (ret == -ECONNABORTED) { + ret = call->app_errno; + goto out_unwait; + } + goto abort; + } + + /* unmarshall the reply */ + vnode->status.if_version = ntohl(*bp++); + vnode->status.type = ntohl(*bp++); + vnode->status.nlink = ntohl(*bp++); + vnode->status.size = ntohl(*bp++); + vnode->status.version = ntohl(*bp++); + vnode->status.author = ntohl(*bp++); + vnode->status.owner = ntohl(*bp++); + vnode->status.caller_access = ntohl(*bp++); + vnode->status.anon_access = ntohl(*bp++); + vnode->status.mode = ntohl(*bp++); + vnode->status.parent.vid = vnode->fid.vid; + vnode->status.parent.vnode = ntohl(*bp++); + vnode->status.parent.unique = ntohl(*bp++); + bp++; /* seg size */ + vnode->status.mtime_client = ntohl(*bp++); + vnode->status.mtime_server = ntohl(*bp++); + bp++; /* group */ + bp++; /* sync counter */ + vnode->status.version |= ((unsigned long long) ntohl(*bp++)) << 32; + bp++; /* spare2 */ + bp++; /* spare3 */ + bp++; /* spare4 */ + + vnode->cb_version = ntohl(*bp++); + vnode->cb_expiry = ntohl(*bp++); + vnode->cb_type = ntohl(*bp++); + + if (volsync) { + volsync->creation = ntohl(*bp++); + bp++; /* spare2 */ + bp++; /* spare3 */ + bp++; /* spare4 */ + bp++; /* spare5 */ + bp++; /* spare6 */ + } + + /* success */ + ret = 0; + + out_unwait: + set_current_state(TASK_RUNNING); + remove_wait_queue(&call->waitq, &myself); + rxrpc_put_call(call); + out_put_conn: + afs_server_release_callslot(server, &callslot); + out: + _leave(""); + return ret; + + abort: + set_current_state(TASK_UNINTERRUPTIBLE); + rxrpc_call_abort(call, ret); + schedule(); + goto out_unwait; +} /* end afs_rxfs_fetch_file_status() */ + +/*****************************************************************************/ +/* + * fetch the contents of a file or directory + */ +int afs_rxfs_fetch_file_data(struct afs_server *server, + struct afs_vnode *vnode, + struct afs_rxfs_fetch_descriptor *desc, + struct afs_volsync *volsync) +{ + struct afs_server_callslot callslot; + struct rxrpc_call *call; + struct kvec piov[1]; + size_t sent; + int ret; + __be32 *bp; + + DECLARE_WAITQUEUE(myself, current); + + _enter("%p,{fid={%u,%u,%u},sz=%Zu,of=%lu}", + server, + desc->fid.vid, + desc->fid.vnode, + desc->fid.unique, + desc->size, + desc->offset); + + /* get hold of the fileserver connection */ + ret = afs_server_request_callslot(server, &callslot); + if (ret < 0) + goto out; + + /* create a call through that connection */ + ret = rxrpc_create_call(callslot.conn, NULL, NULL, afs_rxfs_aemap, &call); + if (ret < 0) { + printk("kAFS: Unable to create call: %d\n", ret); + goto out_put_conn; + } + call->app_opcode = FSFETCHDATA; + + /* we want to get event notifications from the call */ + add_wait_queue(&call->waitq, &myself); + + /* marshall the parameters */ + bp = rxrpc_call_alloc_scratch(call, 24); + bp[0] = htonl(FSFETCHDATA); + bp[1] = htonl(desc->fid.vid); + bp[2] = htonl(desc->fid.vnode); + bp[3] = htonl(desc->fid.unique); + bp[4] = htonl(desc->offset); + bp[5] = htonl(desc->size); + + piov[0].iov_len = 24; + piov[0].iov_base = bp; + + /* send the parameters to the server */ + ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, GFP_NOFS, + 0, &sent); + if (ret < 0) + goto abort; + + /* wait for the data count to arrive */ + ret = rxrpc_call_read_data(call, bp, 4, RXRPC_CALL_READ_BLOCK); + if (ret < 0) + goto read_failed; + + desc->actual = ntohl(bp[0]); + if (desc->actual != desc->size) { + ret = -EBADMSG; + goto abort; + } + + /* call the app to read the actual data */ + rxrpc_call_reset_scratch(call); + + ret = rxrpc_call_read_data(call, desc->buffer, desc->actual, + RXRPC_CALL_READ_BLOCK); + if (ret < 0) + goto read_failed; + + /* wait for the rest of the reply to completely arrive */ + rxrpc_call_reset_scratch(call); + bp = rxrpc_call_alloc_scratch(call, 120); + + ret = rxrpc_call_read_data(call, bp, 120, + RXRPC_CALL_READ_BLOCK | + RXRPC_CALL_READ_ALL); + if (ret < 0) + goto read_failed; + + /* unmarshall the reply */ + vnode->status.if_version = ntohl(*bp++); + vnode->status.type = ntohl(*bp++); + vnode->status.nlink = ntohl(*bp++); + vnode->status.size = ntohl(*bp++); + vnode->status.version = ntohl(*bp++); + vnode->status.author = ntohl(*bp++); + vnode->status.owner = ntohl(*bp++); + vnode->status.caller_access = ntohl(*bp++); + vnode->status.anon_access = ntohl(*bp++); + vnode->status.mode = ntohl(*bp++); + vnode->status.parent.vid = desc->fid.vid; + vnode->status.parent.vnode = ntohl(*bp++); + vnode->status.parent.unique = ntohl(*bp++); + bp++; /* seg size */ + vnode->status.mtime_client = ntohl(*bp++); + vnode->status.mtime_server = ntohl(*bp++); + bp++; /* group */ + bp++; /* sync counter */ + vnode->status.version |= ((unsigned long long) ntohl(*bp++)) << 32; + bp++; /* spare2 */ + bp++; /* spare3 */ + bp++; /* spare4 */ + + vnode->cb_version = ntohl(*bp++); + vnode->cb_expiry = ntohl(*bp++); + vnode->cb_type = ntohl(*bp++); + + if (volsync) { + volsync->creation = ntohl(*bp++); + bp++; /* spare2 */ + bp++; /* spare3 */ + bp++; /* spare4 */ + bp++; /* spare5 */ + bp++; /* spare6 */ + } + + /* success */ + ret = 0; + + out_unwait: + set_current_state(TASK_RUNNING); + remove_wait_queue(&call->waitq,&myself); + rxrpc_put_call(call); + out_put_conn: + afs_server_release_callslot(server, &callslot); + out: + _leave(" = %d", ret); + return ret; + + read_failed: + if (ret == -ECONNABORTED) { + ret = call->app_errno; + goto out_unwait; + } + + abort: + set_current_state(TASK_UNINTERRUPTIBLE); + rxrpc_call_abort(call, ret); + schedule(); + goto out_unwait; + +} /* end afs_rxfs_fetch_file_data() */ + +/*****************************************************************************/ +/* + * ask the AFS fileserver to discard a callback request on a file + */ +int afs_rxfs_give_up_callback(struct afs_server *server, + struct afs_vnode *vnode) +{ + struct afs_server_callslot callslot; + struct rxrpc_call *call; + struct kvec piov[1]; + size_t sent; + int ret; + __be32 *bp; + + DECLARE_WAITQUEUE(myself, current); + + _enter("%p,{%u,%u,%u}", + server, vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique); + + /* get hold of the fileserver connection */ + ret = afs_server_request_callslot(server, &callslot); + if (ret < 0) + goto out; + + /* create a call through that connection */ + ret = rxrpc_create_call(callslot.conn, NULL, NULL, afs_rxfs_aemap, &call); + if (ret < 0) { + printk("kAFS: Unable to create call: %d\n", ret); + goto out_put_conn; + } + call->app_opcode = FSGIVEUPCALLBACKS; + + /* we want to get event notifications from the call */ + add_wait_queue(&call->waitq, &myself); + + /* marshall the parameters */ + bp = rxrpc_call_alloc_scratch(call, (1 + 4 + 4) * 4); + + piov[0].iov_len = (1 + 4 + 4) * 4; + piov[0].iov_base = bp; + + *bp++ = htonl(FSGIVEUPCALLBACKS); + *bp++ = htonl(1); + *bp++ = htonl(vnode->fid.vid); + *bp++ = htonl(vnode->fid.vnode); + *bp++ = htonl(vnode->fid.unique); + *bp++ = htonl(1); + *bp++ = htonl(vnode->cb_version); + *bp++ = htonl(vnode->cb_expiry); + *bp++ = htonl(vnode->cb_type); + + /* send the parameters to the server */ + ret = rxrpc_call_write_data(call, 1, piov, RXRPC_LAST_PACKET, GFP_NOFS, + 0, &sent); + if (ret < 0) + goto abort; + + /* wait for the reply to completely arrive */ + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (call->app_call_state != RXRPC_CSTATE_CLNT_RCV_REPLY || + signal_pending(current)) + break; + schedule(); + } + set_current_state(TASK_RUNNING); + + ret = -EINTR; + if (signal_pending(current)) + goto abort; + + switch (call->app_call_state) { + case RXRPC_CSTATE_ERROR: + ret = call->app_errno; + goto out_unwait; + + case RXRPC_CSTATE_CLNT_GOT_REPLY: + ret = 0; + goto out_unwait; + + default: + BUG(); + } + + out_unwait: + set_current_state(TASK_RUNNING); + remove_wait_queue(&call->waitq, &myself); + rxrpc_put_call(call); + out_put_conn: + afs_server_release_callslot(server, &callslot); + out: + _leave(""); + return ret; + + abort: + set_current_state(TASK_UNINTERRUPTIBLE); + rxrpc_call_abort(call, ret); + schedule(); + goto out_unwait; +} /* end afs_rxfs_give_up_callback() */ + +/*****************************************************************************/ +/* + * look a filename up in a directory + * - this operation doesn't seem to work correctly in OpenAFS server 1.2.2 + */ +#if 0 +int afs_rxfs_lookup(struct afs_server *server, + struct afs_vnode *dir, + const char *filename, + struct afs_vnode *vnode, + struct afs_volsync *volsync) +{ + struct rxrpc_connection *conn; + struct rxrpc_call *call; + struct kvec piov[3]; + size_t sent; + int ret; + u32 *bp, zero; + + DECLARE_WAITQUEUE(myself, current); + + kenter("%p,{%u,%u,%u},%s", + server, fid->vid, fid->vnode, fid->unique, filename); + + /* get hold of the fileserver connection */ + ret = afs_server_get_fsconn(server, &conn); + if (ret < 0) + goto out; + + /* create a call through that connection */ + ret = rxrpc_create_call(conn, NULL, NULL, afs_rxfs_aemap, &call); + if (ret < 0) { + printk("kAFS: Unable to create call: %d\n", ret); + goto out_put_conn; + } + call->app_opcode = FSLOOKUP; + + /* we want to get event notifications from the call */ + add_wait_queue(&call->waitq,&myself); + + /* marshall the parameters */ + bp = rxrpc_call_alloc_scratch(call, 20); + + zero = 0; + + piov[0].iov_len = 20; + piov[0].iov_base = bp; + piov[1].iov_len = strlen(filename); + piov[1].iov_base = (char *) filename; + piov[2].iov_len = (4 - (piov[1].iov_len & 3)) & 3; + piov[2].iov_base = &zero; + + *bp++ = htonl(FSLOOKUP); + *bp++ = htonl(dirfid->vid); + *bp++ = htonl(dirfid->vnode); + *bp++ = htonl(dirfid->unique); + *bp++ = htonl(piov[1].iov_len); + + /* send the parameters to the server */ + ret = rxrpc_call_write_data(call, 3, piov, RXRPC_LAST_PACKET, GFP_NOFS, + 0, &sent); + if (ret < 0) + goto abort; + + /* wait for the reply to completely arrive */ + bp = rxrpc_call_alloc_scratch(call, 220); + + ret = rxrpc_call_read_data(call, bp, 220, + RXRPC_CALL_READ_BLOCK | + RXRPC_CALL_READ_ALL); + if (ret < 0) { + if (ret == -ECONNABORTED) { + ret = call->app_errno; + goto out_unwait; + } + goto abort; + } + + /* unmarshall the reply */ + fid->vid = ntohl(*bp++); + fid->vnode = ntohl(*bp++); + fid->unique = ntohl(*bp++); + + vnode->status.if_version = ntohl(*bp++); + vnode->status.type = ntohl(*bp++); + vnode->status.nlink = ntohl(*bp++); + vnode->status.size = ntohl(*bp++); + vnode->status.version = ntohl(*bp++); + vnode->status.author = ntohl(*bp++); + vnode->status.owner = ntohl(*bp++); + vnode->status.caller_access = ntohl(*bp++); + vnode->status.anon_access = ntohl(*bp++); + vnode->status.mode = ntohl(*bp++); + vnode->status.parent.vid = dirfid->vid; + vnode->status.parent.vnode = ntohl(*bp++); + vnode->status.parent.unique = ntohl(*bp++); + bp++; /* seg size */ + vnode->status.mtime_client = ntohl(*bp++); + vnode->status.mtime_server = ntohl(*bp++); + bp++; /* group */ + bp++; /* sync counter */ + vnode->status.version |= ((unsigned long long) ntohl(*bp++)) << 32; + bp++; /* spare2 */ + bp++; /* spare3 */ + bp++; /* spare4 */ + + dir->status.if_version = ntohl(*bp++); + dir->status.type = ntohl(*bp++); + dir->status.nlink = ntohl(*bp++); + dir->status.size = ntohl(*bp++); + dir->status.version = ntohl(*bp++); + dir->status.author = ntohl(*bp++); + dir->status.owner = ntohl(*bp++); + dir->status.caller_access = ntohl(*bp++); + dir->status.anon_access = ntohl(*bp++); + dir->status.mode = ntohl(*bp++); + dir->status.parent.vid = dirfid->vid; + dir->status.parent.vnode = ntohl(*bp++); + dir->status.parent.unique = ntohl(*bp++); + bp++; /* seg size */ + dir->status.mtime_client = ntohl(*bp++); + dir->status.mtime_server = ntohl(*bp++); + bp++; /* group */ + bp++; /* sync counter */ + dir->status.version |= ((unsigned long long) ntohl(*bp++)) << 32; + bp++; /* spare2 */ + bp++; /* spare3 */ + bp++; /* spare4 */ + + callback->fid = *fid; + callback->version = ntohl(*bp++); + callback->expiry = ntohl(*bp++); + callback->type = ntohl(*bp++); + + if (volsync) { + volsync->creation = ntohl(*bp++); + bp++; /* spare2 */ + bp++; /* spare3 */ + bp++; /* spare4 */ + bp++; /* spare5 */ + bp++; /* spare6 */ + } + + /* success */ + ret = 0; + + out_unwait: + set_current_state(TASK_RUNNING); + remove_wait_queue(&call->waitq, &myself); + rxrpc_put_call(call); + out_put_conn: + afs_server_release_fsconn(server, conn); + out: + kleave(""); + return ret; + + abort: + set_current_state(TASK_UNINTERRUPTIBLE); + rxrpc_call_abort(call, ret); + schedule(); + goto out_unwait; +} /* end afs_rxfs_lookup() */ +#endif diff --git a/fs/afs/fsclient.h b/fs/afs/fsclient.h new file mode 100644 index 00000000000..8ba3e749ee3 --- /dev/null +++ b/fs/afs/fsclient.h @@ -0,0 +1,54 @@ +/* fsclient.h: AFS File Server client stub declarations + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_AFS_FSCLIENT_H +#define _LINUX_AFS_FSCLIENT_H + +#include "server.h" + +extern int afs_rxfs_get_volume_info(struct afs_server *server, + const char *name, + struct afs_volume_info *vinfo); + +extern int afs_rxfs_fetch_file_status(struct afs_server *server, + struct afs_vnode *vnode, + struct afs_volsync *volsync); + +struct afs_rxfs_fetch_descriptor { + struct afs_fid fid; /* file ID to fetch */ + size_t size; /* total number of bytes to fetch */ + off_t offset; /* offset in file to start from */ + void *buffer; /* read buffer */ + size_t actual; /* actual size sent back by server */ +}; + +extern int afs_rxfs_fetch_file_data(struct afs_server *server, + struct afs_vnode *vnode, + struct afs_rxfs_fetch_descriptor *desc, + struct afs_volsync *volsync); + +extern int afs_rxfs_give_up_callback(struct afs_server *server, + struct afs_vnode *vnode); + +/* this doesn't appear to work in OpenAFS server */ +extern int afs_rxfs_lookup(struct afs_server *server, + struct afs_vnode *dir, + const char *filename, + struct afs_vnode *vnode, + struct afs_volsync *volsync); + +/* this is apparently mis-implemented in OpenAFS server */ +extern int afs_rxfs_get_root_volume(struct afs_server *server, + char *buf, + size_t *buflen); + + +#endif /* _LINUX_AFS_FSCLIENT_H */ diff --git a/fs/afs/inode.c b/fs/afs/inode.c new file mode 100644 index 00000000000..c476fde33fb --- /dev/null +++ b/fs/afs/inode.c @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2002 Red Hat, Inc. All rights reserved. + * + * This software may be freely redistributed under the terms of the + * GNU General Public License. + * + * You should have received a copy of the GNU General Public License + * |