aboutsummaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs
downloadlinux-linaro-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.gz
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')
-rw-r--r--fs/Kconfig1729
-rw-r--r--fs/Kconfig.binfmt134
-rw-r--r--fs/Makefile97
-rw-r--r--fs/adfs/Makefile7
-rw-r--r--fs/adfs/adfs.h127
-rw-r--r--fs/adfs/dir.c302
-rw-r--r--fs/adfs/dir_f.c460
-rw-r--r--fs/adfs/dir_f.h65
-rw-r--r--fs/adfs/dir_fplus.c179
-rw-r--r--fs/adfs/dir_fplus.h45
-rw-r--r--fs/adfs/file.c43
-rw-r--r--fs/adfs/inode.c395
-rw-r--r--fs/adfs/map.c296
-rw-r--r--fs/adfs/super.c508
-rw-r--r--fs/affs/Changes343
-rw-r--r--fs/affs/Makefile9
-rw-r--r--fs/affs/affs.h304
-rw-r--r--fs/affs/amigaffs.c509
-rw-r--r--fs/affs/bitmap.c390
-rw-r--r--fs/affs/dir.c155
-rw-r--r--fs/affs/file.c920
-rw-r--r--fs/affs/inode.c411
-rw-r--r--fs/affs/namei.c443
-rw-r--r--fs/affs/super.c569
-rw-r--r--fs/affs/symlink.c78
-rw-r--r--fs/afs/Makefile28
-rw-r--r--fs/afs/cache.h27
-rw-r--r--fs/afs/callback.c168
-rw-r--r--fs/afs/cell.c569
-rw-r--r--fs/afs/cell.h78
-rw-r--r--fs/afs/cmservice.c652
-rw-r--r--fs/afs/cmservice.h29
-rw-r--r--fs/afs/dir.c666
-rw-r--r--fs/afs/errors.h34
-rw-r--r--fs/afs/file.c305
-rw-r--r--fs/afs/fsclient.c837
-rw-r--r--fs/afs/fsclient.h54
-rw-r--r--fs/afs/inode.c287
-rw-r--r--fs/afs/internal.h140
-rw-r--r--fs/afs/kafsasyncd.c257
-rw-r--r--fs/afs/kafsasyncd.h52
-rw-r--r--fs/afs/kafstimod.c204
-rw-r--r--fs/afs/kafstimod.h49
-rw-r--r--fs/afs/main.c286
-rw-r--r--fs/afs/misc.c39
-rw-r--r--fs/afs/mntpt.c287
-rw-r--r--fs/afs/mount.h23
-rw-r--r--fs/afs/proc.c857
-rw-r--r--fs/afs/server.c502
-rw-r--r--fs/afs/server.h102
-rw-r--r--fs/afs/super.c441
-rw-r--r--fs/afs/super.h43
-rw-r--r--fs/afs/transport.h21
-rw-r--r--fs/afs/types.h125
-rw-r--r--fs/afs/vlclient.c695
-rw-r--r--fs/afs/vlclient.h93
-rw-r--r--fs/afs/vlocation.c954
-rw-r--r--fs/afs/vnode.c395
-rw-r--r--fs/afs/vnode.h94
-rw-r--r--fs/afs/volume.c520
-rw-r--r--fs/afs/volume.h142
-rw-r--r--fs/aio.c1729
-rw-r--r--fs/attr.c208
-rw-r--r--fs/autofs/Makefile7
-rw-r--r--fs/autofs/autofs_i.h164
-rw-r--r--fs/autofs/dirhash.c249
-rw-r--r--fs/autofs/init.c52
-rw-r--r--fs/autofs/inode.c250
-rw-r--r--fs/autofs/root.c564
-rw-r--r--fs/autofs/symlink.c25
-rw-r--r--fs/autofs/waitq.c206
-rw-r--r--fs/autofs4/Makefile7
-rw-r--r--fs/autofs4/autofs_i.h193
-rw-r--r--fs/autofs4/expire.c358
-rw-r--r--fs/autofs4/init.c42
-rw-r--r--fs/autofs4/inode.c324
-rw-r--r--fs/autofs4/root.c808
-rw-r--r--fs/autofs4/symlink.c25
-rw-r--r--fs/autofs4/waitq.c303
-rw-r--r--fs/bad_inode.c123
-rw-r--r--fs/befs/ChangeLog417
-rw-r--r--fs/befs/Makefile7
-rw-r--r--fs/befs/TODO14
-rw-r--r--fs/befs/attribute.c117
-rw-r--r--fs/befs/befs.h154
-rw-r--r--fs/befs/befs_fs_types.h213
-rw-r--r--fs/befs/btree.c788
-rw-r--r--fs/befs/btree.h13
-rw-r--r--fs/befs/datastream.c528
-rw-r--r--fs/befs/datastream.h19
-rw-r--r--fs/befs/debug.c283
-rw-r--r--fs/befs/endian.h126
-rw-r--r--fs/befs/inode.c53
-rw-r--r--fs/befs/inode.h8
-rw-r--r--fs/befs/io.c83
-rw-r--r--fs/befs/io.h9
-rw-r--r--fs/befs/linuxvfs.c964
-rw-r--r--fs/befs/super.c112
-rw-r--r--fs/befs/super.h8
-rw-r--r--fs/bfs/Makefile7
-rw-r--r--fs/bfs/bfs.h60
-rw-r--r--fs/bfs/dir.c362
-rw-r--r--fs/bfs/file.c162
-rw-r--r--fs/bfs/inode.c420
-rw-r--r--fs/binfmt_aout.c550
-rw-r--r--fs/binfmt_elf.c1677
-rw-r--r--fs/binfmt_elf_fdpic.c1101
-rw-r--r--fs/binfmt_em86.c115
-rw-r--r--fs/binfmt_flat.c901
-rw-r--r--fs/binfmt_misc.c780
-rw-r--r--fs/binfmt_script.c116
-rw-r--r--fs/binfmt_som.c309
-rw-r--r--fs/bio.c1096
-rw-r--r--fs/block_dev.c923
-rw-r--r--fs/buffer.c3152
-rw-r--r--fs/char_dev.c449
-rw-r--r--fs/cifs/AUTHORS42
-rw-r--r--fs/cifs/CHANGES671
-rw-r--r--fs/cifs/Makefile6
-rw-r--r--fs/cifs/README475
-rw-r--r--fs/cifs/TODO104
-rw-r--r--fs/cifs/asn1.c618
-rw-r--r--fs/cifs/cifs_debug.c805
-rw-r--r--fs/cifs/cifs_debug.h66
-rw-r--r--fs/cifs/cifs_fs_sb.h39
-rw-r--r--fs/cifs/cifs_unicode.c87
-rw-r--r--fs/cifs/cifs_unicode.h353
-rw-r--r--fs/cifs/cifs_uniupr.h253
-rw-r--r--fs/cifs/cifsencrypt.c209
-rw-r--r--fs/cifs/cifsencrypt.h34
-rw-r--r--fs/cifs/cifsfs.c913
-rw-r--r--fs/cifs/cifsfs.h98
-rw-r--r--fs/cifs/cifsglob.h439
-rw-r--r--fs/cifs/cifspdu.h1987
-rw-r--r--fs/cifs/cifsproto.h269
-rw-r--r--fs/cifs/cifssmb.c4186
-rw-r--r--fs/cifs/connect.c3064
-rw-r--r--fs/cifs/dir.c523
-rw-r--r--fs/cifs/fcntl.c117
-rw-r--r--fs/cifs/file.c1675
-rw-r--r--fs/cifs/inode.c1096
-rw-r--r--fs/cifs/ioctl.c49
-rw-r--r--fs/cifs/link.c328
-rw-r--r--fs/cifs/md4.c205
-rw-r--r--fs/cifs/md5.c363
-rw-r--r--fs/cifs/md5.h38
-rw-r--r--fs/cifs/misc.c516
-rw-r--r--fs/cifs/netmisc.c904
-rw-r--r--fs/cifs/nterr.c687
-rw-r--r--fs/cifs/nterr.h556
-rw-r--r--fs/cifs/ntlmssp.h101
-rw-r--r--fs/cifs/readdir.c867
-rw-r--r--fs/cifs/rfc1002pdu.h79
-rw-r--r--fs/cifs/smbdes.c412
-rw-r--r--fs/cifs/smbencrypt.c285
-rw-r--r--fs/cifs/smberr.h115
-rw-r--r--fs/cifs/transport.c619
-rw-r--r--fs/cifs/xattr.c334
-rw-r--r--fs/coda/Makefile12
-rw-r--r--fs/coda/cache.c120
-rw-r--r--fs/coda/cnode.c172
-rw-r--r--fs/coda/coda_linux.c197
-rw-r--r--fs/coda/dir.c704
-rw-r--r--fs/coda/file.c300
-rw-r--r--fs/coda/inode.c321
-rw-r--r--fs/coda/pioctl.c95
-rw-r--r--fs/coda/psdev.c462
-rw-r--r--fs/coda/symlink.c55
-rw-r--r--fs/coda/sysctl.c254
-rw-r--r--fs/coda/upcall.c914
-rw-r--r--fs/compat.c1950
-rw-r--r--fs/compat_ioctl.c3082
-rw-r--r--fs/cramfs/Makefile7
-rw-r--r--fs/cramfs/README168
-rw-r--r--fs/cramfs/inode.c525
-rw-r--r--fs/cramfs/uncompress.c77
-rw-r--r--fs/dcache.c1764
-rw-r--r--fs/dcookies.c330
-rw-r--r--fs/debugfs/Makefile4
-rw-r--r--fs/debugfs/file.c262
-rw-r--r--fs/debugfs/inode.c328
-rw-r--r--fs/devfs/Makefile8
-rw-r--r--fs/devfs/base.c2838
-rw-r--r--fs/devfs/util.c97
-rw-r--r--fs/devpts/Makefile8
-rw-r--r--fs/devpts/inode.c242
-rw-r--r--fs/devpts/xattr_security.c47
-rw-r--r--fs/direct-io.c1258
-rw-r--r--fs/dnotify.c183
-rw-r--r--fs/dquot.c1850
-rw-r--r--fs/efs/Makefile7
-rw-r--r--fs/efs/dir.c113
-rw-r--r--fs/efs/file.c60
-rw-r--r--fs/efs/inode.c305
-rw-r--r--fs/efs/namei.c110
-rw-r--r--fs/efs/super.c343
-rw-r--r--fs/efs/symlink.c58
-rw-r--r--fs/eventpoll.c1639
-rw-r--r--fs/exec.c1498
-rw-r--r--fs/exportfs/Makefile6
-rw-r--r--fs/exportfs/expfs.c540
-rw-r--r--fs/ext2/CHANGES157
-rw-r--r--fs/ext2/Makefile12
-rw-r--r--fs/ext2/acl.c518
-rw-r--r--fs/ext2/acl.h82
-rw-r--r--fs/ext2/balloc.c699
-rw-r--r--fs/ext2/bitmap.c25
-rw-r--r--fs/ext2/dir.c673
-rw-r--r--fs/ext2/ext2.h160
-rw-r--r--fs/ext2/file.c68
-rw-r--r--fs/ext2/fsync.c51
-rw-r--r--fs/ext2/ialloc.c735
-rw-r--r--fs/ext2/inode.c1276
-rw-r--r--fs/ext2/ioctl.c81
-rw-r--r--fs/ext2/namei.c418
-rw-r--r--fs/ext2/super.c1161
-rw-r--r--fs/ext2/symlink.c52
-rw-r--r--fs/ext2/xattr.c1043
-rw-r--r--fs/ext2/xattr.h118
-rw-r--r--fs/ext2/xattr_security.c53
-rw-r--r--fs/ext2/xattr_trusted.c64
-rw-r--r--fs/ext2/xattr_user.c77
-rw-r--r--fs/ext3/Makefile12
-rw-r--r--fs/ext3/acl.c547
-rw-r--r--fs/ext3/acl.h84
-rw-r--r--fs/ext3/balloc.c1600
-rw-r--r--fs/ext3/bitmap.c26
-rw-r--r--fs/ext3/dir.c519
-rw-r--r--fs/ext3/file.c131
-rw-r--r--fs/ext3/fsync.c88
-rw-r--r--fs/ext3/hash.c152
-rw-r--r--fs/ext3/ialloc.c794
-rw-r--r--fs/ext3/inode.c3132
-rw-r--r--fs/ext3/ioctl.c243
-rw-r--r--fs/ext3/namei.c2378
-rw-r--r--fs/ext3/resize.c996
-rw-r--r--fs/ext3/super.c2539
-rw-r--r--fs/ext3/symlink.c54
-rw-r--r--fs/ext3/xattr.c1320
-rw-r--r--fs/ext3/xattr.h135
-rw-r--r--fs/ext3/xattr_security.c55
-rw-r--r--fs/ext3/xattr_trusted.c65
-rw-r--r--fs/ext3/xattr_user.c79
-rw-r--r--fs/fat/Makefile7
-rw-r--r--fs/fat/cache.c324
-rw-r--r--fs/fat/dir.c1271
-rw-r--r--fs/fat/fatent.c612
-rw-r--r--fs/fat/file.c308
-rw-r--r--fs/fat/inode.c1351
-rw-r--r--fs/fat/misc.c225
-rw-r--r--fs/fcntl.c601
-rw-r--r--fs/fifo.c155
-rw-r--r--fs/file.c254
-rw-r--r--fs/file_table.c255
-rw-r--r--fs/filesystems.c236
-rw-r--r--fs/freevxfs/Makefile8
-rw-r--r--fs/freevxfs/vxfs.h264
-rw-r--r--fs/freevxfs/vxfs_bmap.c280
-rw-r--r--fs/freevxfs/vxfs_dir.h92
-rw-r--r--fs/freevxfs/vxfs_extern.h76
-rw-r--r--fs/freevxfs/vxfs_fshead.c202
-rw-r--r--fs/freevxfs/vxfs_fshead.h67
-rw-r--r--fs/freevxfs/vxfs_immed.c114
-rw-r--r--fs/freevxfs/vxfs_inode.c351
-rw-r--r--fs/freevxfs/vxfs_inode.h180
-rw-r--r--fs/freevxfs/vxfs_kcompat.h49
-rw-r--r--fs/freevxfs/vxfs_lookup.c328
-rw-r--r--fs/freevxfs/vxfs_olt.c132
-rw-r--r--fs/freevxfs/vxfs_olt.h145
-rw-r--r--fs/freevxfs/vxfs_subr.c190
-rw-r--r--fs/freevxfs/vxfs_super.c278
-rw-r--r--fs/fs-writeback.c695
-rw-r--r--fs/hfs/Makefile10
-rw-r--r--fs/hfs/attr.c121
-rw-r--r--fs/hfs/bfind.c210
-rw-r--r--fs/hfs/bitmap.c243
-rw-r--r--fs/hfs/bnode.c498
-rw-r--r--fs/hfs/brec.c496
-rw-r--r--fs/hfs/btree.c327
-rw-r--r--fs/hfs/btree.h168
-rw-r--r--fs/hfs/catalog.c347
-rw-r--r--fs/hfs/dir.c330
-rw-r--r--fs/hfs/extent.c527
-rw-r--r--fs/hfs/hfs.h287
-rw-r--r--fs/hfs/hfs_fs.h286
-rw-r--r--fs/hfs/inode.c636
-rw-r--r--fs/hfs/mdb.c343
-rw-r--r--fs/hfs/part_tbl.c117
-rw-r--r--fs/hfs/string.c115
-rw-r--r--fs/hfs/super.c395
-rw-r--r--fs/hfs/sysdep.c40
-rw-r--r--fs/hfs/trans.c72
-rw-r--r--fs/hfsplus/Makefile9
-rw-r--r--fs/hfsplus/bfind.c210
-rw-r--r--fs/hfsplus/bitmap.c221
-rw-r--r--fs/hfsplus/bnode.c662
-rw-r--r--fs/hfsplus/brec.c491
-rw-r--r--fs/hfsplus/btree.c319
-rw-r--r--fs/hfsplus/catalog.c358
-rw-r--r--fs/hfsplus/dir.c484
-rw-r--r--fs/hfsplus/extents.c505
-rw-r--r--fs/hfsplus/hfsplus_fs.h414
-rw-r--r--fs/hfsplus/hfsplus_raw.h326
-rw-r--r--fs/hfsplus/inode.c555
-rw-r--r--fs/hfsplus/ioctl.c188
-rw-r--r--fs/hfsplus/options.c162
-rw-r--r--fs/hfsplus/part_tbl.c133
-rw-r--r--fs/hfsplus/super.c502
-rw-r--r--fs/hfsplus/tables.c3245
-rw-r--r--fs/hfsplus/unicode.c271
-rw-r--r--fs/hfsplus/wrapper.c171
-rw-r--r--fs/hostfs/Makefile11
-rw-r--r--fs/hostfs/hostfs.h100
-rw-r--r--fs/hostfs/hostfs_kern.c1045
-rw-r--r--fs/hostfs/hostfs_user.c362
-rw-r--r--fs/hpfs/Makefile8
-rw-r--r--fs/hpfs/alloc.c456
-rw-r--r--fs/hpfs/anode.c491
-rw-r--r--fs/hpfs/buffer.c175
-rw-r--r--fs/hpfs/dentry.c60
-rw-r--r--fs/hpfs/dir.c320
-rw-r--r--fs/hpfs/dnode.c1080
-rw-r--r--fs/hpfs/ea.c363
-rw-r--r--fs/hpfs/file.c140
-rw-r--r--fs/hpfs/hpfs.h493
-rw-r--r--fs/hpfs/hpfs_fn.h338
-rw-r--r--fs/hpfs/inode.c291
-rw-r--r--fs/hpfs/map.c275
-rw-r--r--fs/hpfs/name.c144
-rw-r--r--fs/hpfs/namei.c673
-rw-r--r--fs/hpfs/super.c701
-rw-r--r--fs/hppfs/Makefile9
-rw-r--r--fs/hppfs/hppfs_kern.c815
-rw-r--r--fs/hugetlbfs/Makefile7
-rw-r--r--fs/hugetlbfs/inode.c853
-rw-r--r--fs/inode.c1377
-rw-r--r--fs/ioctl.c186
-rw-r--r--fs/isofs/Makefile10
-rw-r--r--fs/isofs/compress.c359
-rw-r--r--fs/isofs/dir.c280
-rw-r--r--fs/isofs/export.c228
-rw-r--r--fs/isofs/inode.c1503
-rw-r--r--fs/isofs/joliet.c103
-rw-r--r--fs/isofs/namei.c201
-rw-r--r--fs/isofs/rock.c565
-rw-r--r--fs/isofs/rock.h119
-rw-r--r--fs/isofs/util.c83
-rw-r--r--fs/isofs/zisofs.h21
-rw-r--r--fs/jbd/Makefile7
-rw-r--r--fs/jbd/checkpoint.c636
-rw-r--r--fs/jbd/commit.c844
-rw-r--r--fs/jbd/journal.c2003
-rw-r--r--fs/jbd/recovery.c591
-rw-r--r--fs/jbd/revoke.c702
-rw-r--r--fs/jbd/transaction.c2062
-rw-r--r--fs/jffs/Makefile11
-rw-r--r--fs/jffs/inode-v23.c1847
-rw-r--r--fs/jffs/intrep.c3457
-rw-r--r--fs/jffs/intrep.h60
-rw-r--r--fs/jffs/jffs_fm.c795
-rw-r--r--fs/jffs/jffs_fm.h148
-rw-r--r--fs/jffs/jffs_proc.c261
-rw-r--r--fs/jffs/jffs_proc.h28
-rw-r--r--fs/jffs2/LICENCE35
-rw-r--r--fs/jffs2/Makefile18
-rw-r--r--fs/jffs2/README.Locking148
-rw-r--r--fs/jffs2/TODO40
-rw-r--r--fs/jffs2/background.c140
-rw-r--r--fs/jffs2/build.c371
-rw-r--r--fs/jffs2/compr.c469
-rw-r--r--fs/jffs2/compr.h115
-rw-r--r--fs/jffs2/compr_rtime.c132
-rw-r--r--fs/jffs2/compr_rubin.c373
-rw-r--r--fs/jffs2/compr_rubin.h21
-rw-r--r--fs/jffs2/compr_zlib.c218
-rw-r--r--fs/jffs2/comprtest.c307
-rw-r--r--fs/jffs2/dir.c799
-rw-r--r--fs/jffs2/erase.c442
-rw-r--r--fs/jffs2/file.c290
-rw-r--r--fs/jffs2/fs.c677
-rw-r--r--fs/jffs2/gc.c1246
-rw-r--r--fs/jffs2/histo.h3
-rw-r--r--fs/jffs2/histo_mips.h2
-rw-r--r--fs/jffs2/ioctl.c23
-rw-r--r--fs/jffs2/malloc.c205
-rw-r--r--fs/jffs2/nodelist.c681
-rw-r--r--fs/jffs2/nodelist.h473
-rw-r--r--fs/jffs2/nodemgmt.c838
-rw-r--r--fs/jffs2/os-linux.h217
-rw-r--r--fs/jffs2/pushpull.h72
-rw-r--r--fs/jffs2/read.c246
-rw-r--r--fs/jffs2/readinode.c695
-rw-r--r--fs/jffs2/scan.c916
-rw-r--r--fs/jffs2/super.c365
-rw-r--r--fs/jffs2/symlink.c45
-rw-r--r--fs/jffs2/wbuf.c1184
-rw-r--r--fs/jffs2/write.c708
-rw-r--r--fs/jffs2/writev.c50
-rw-r--r--fs/jfs/Makefile15
-rw-r--r--fs/jfs/acl.c234
-rw-r--r--fs/jfs/endian24.h49
-rw-r--r--fs/jfs/file.c119
-rw-r--r--fs/jfs/inode.c384
-rw-r--r--fs/jfs/jfs_acl.h30
-rw-r--r--fs/jfs/jfs_btree.h172
-rw-r--r--fs/jfs/jfs_debug.c154
-rw-r--r--fs/jfs/jfs_debug.h122
-rw-r--r--fs/jfs/jfs_dinode.h151
-rw-r--r--fs/jfs/jfs_dmap.c4272
-rw-r--r--fs/jfs/jfs_dmap.h314
-rw-r--r--fs/jfs/jfs_dtree.c4752
-rw-r--r--fs/jfs/jfs_dtree.h279
-rw-r--r--fs/jfs/jfs_extent.c668
-rw-r--r--fs/jfs/jfs_extent.h31
-rw-r--r--fs/jfs/jfs_filsys.h280
-rw-r--r--fs/jfs/jfs_imap.c3270
-rw-r--r--fs/jfs/jfs_imap.h175
-rw-r--r--fs/jfs/jfs_incore.h197
-rw-r--r--fs/jfs/jfs_inode.c104
-rw-r--r--fs/jfs/jfs_inode.h23
-rw-r--r--fs/jfs/jfs_lock.h51
-rw-r--r--fs/jfs/jfs_logmgr.c2524
-rw-r--r--fs/jfs/jfs_logmgr.h510
-rw-r--r--fs/jfs/jfs_metapage.c580
-rw-r--r--fs/jfs/jfs_metapage.h115
-rw-r--r--fs/jfs/jfs_mount.c512
-rw-r--r--fs/jfs/jfs_superblock.h113
-rw-r--r--fs/jfs/jfs_txnmgr.c3131
-rw-r--r--fs/jfs/jfs_txnmgr.h318
-rw-r--r--fs/jfs/jfs_types.h192
-rw-r--r--fs/jfs/jfs_umount.c178
-rw-r--r--fs/jfs/jfs_unicode.c137
-rw-r--r--fs/jfs/jfs_unicode.h155
-rw-r--r--fs/jfs/jfs_uniupr.c134
-rw-r--r--fs/jfs/jfs_xattr.h64
-rw-r--r--fs/jfs/jfs_xtree.c4485
-rw-r--r--fs/jfs/jfs_xtree.h140
-rw-r--r--fs/jfs/namei.c1540
-rw-r--r--fs/jfs/resize.c537
-rw-r--r--fs/jfs/super.c700
-rw-r--r--fs/jfs/symlink.c39
-rw-r--r--fs/jfs/xattr.c1127
-rw-r--r--fs/libfs.c549
-rw-r--r--fs/lockd/Makefile10
-rw-r--r--fs/lockd/clntlock.c245
-rw-r--r--fs/lockd/clntproc.c820
-rw-r--r--fs/lockd/host.c346
-rw-r--r--fs/lockd/mon.c246
-rw-r--r--fs/lockd/svc.c519
-rw-r--r--fs/lockd/svc4proc.c580
-rw-r--r--fs/lockd/svclock.c686
-rw-r--r--fs/lockd/svcproc.c606
-rw-r--r--fs/lockd/svcshare.c111
-rw-r--r--fs/lockd/svcsubs.c309
-rw-r--r--fs/lockd/xdr.c635
-rw-r--r--fs/lockd/xdr4.c580
-rw-r--r--fs/locks.c2212
-rw-r--r--fs/mbcache.c677
-rw-r--r--fs/minix/Makefile7
-rw-r--r--fs/minix/bitmap.c269
-rw-r--r--fs/minix/dir.c409
-rw-r--r--fs/minix/file.c45
-rw-r--r--fs/minix/inode.c598
-rw-r--r--fs/minix/itree_common.c362
-rw-r--r--fs/minix/itree_v1.c61
-rw-r--r--fs/minix/itree_v2.c66
-rw-r--r--fs/minix/minix.h96
-rw-r--r--fs/minix/namei.c317
-rw-r--r--fs/mpage.c772
-rw-r--r--fs/msdos/Makefile7
-rw-r--r--fs/msdos/namei.c711
-rw-r--r--fs/namei.c2454
-rw-r--r--fs/namespace.c1465
-rw-r--r--fs/ncpfs/Kconfig87
-rw-r--r--fs/ncpfs/Makefile16
-rw-r--r--fs/ncpfs/dir.c1260
-rw-r--r--fs/ncpfs/file.c300
-rw-r--r--fs/ncpfs/getopt.c75
-rw-r--r--fs/ncpfs/getopt.h16
-rw-r--r--fs/ncpfs/inode.c1012
-rw-r--r--fs/ncpfs/ioctl.c649
-rw-r--r--fs/ncpfs/mmap.c128
-rw-r--r--fs/ncpfs/ncplib_kernel.c1355
-rw-r--r--fs/ncpfs/ncplib_kernel.h259
-rw-r--r--fs/ncpfs/ncpsign_kernel.c127
-rw-r--r--fs/ncpfs/ncpsign_kernel.h28
-rw-r--r--fs/ncpfs/sock.c850
-rw-r--r--fs/ncpfs/symlink.c183
-rw-r--r--fs/nfs/Makefile15
-rw-r--r--fs/nfs/callback.c187
-rw-r--r--fs/nfs/callback.h70
-rw-r--r--fs/nfs/callback_proc.c85
-rw-r--r--fs/nfs/callback_xdr.c481
-rw-r--r--fs/nfs/delegation.c342
-rw-r--r--fs/nfs/delegation.h57
-rw-r--r--fs/nfs/dir.c1562
-rw-r--r--fs/nfs/direct.c808
-rw-r--r--fs/nfs/file.c484
-rw-r--r--fs/nfs/idmap.c498
-rw-r--r--fs/nfs/inode.c2003
-rw-r--r--fs/nfs/mount_clnt.c183
-rw-r--r--fs/nfs/nfs2xdr.c711
-rw-r--r--fs/nfs/nfs3proc.c859
-rw-r--r--fs/nfs/nfs3xdr.c1023
-rw-r--r--fs/nfs/nfs4proc.c2786
-rw-r--r--fs/nfs/nfs4renewd.c148
-rw-r--r--fs/nfs/nfs4state.c932
-rw-r--r--fs/nfs/nfs4xdr.c4034
-rw-r--r--fs/nfs/nfsroot.c513
-rw-r--r--fs/nfs/pagelist.c309
-rw-r--r--fs/nfs/proc.c655
-rw-r--r--fs/nfs/read.c618
-rw-r--r--fs/nfs/symlink.c117
-rw-r--r--fs/nfs/unlink.c227
-rw-r--r--fs/nfs/write.c1431
-rw-r--r--fs/nfsctl.c118
-rw-r--r--fs/nfsd/Makefile12
-rw-r--r--fs/nfsd/auth.c63
-rw-r--r--fs/nfsd/export.c1200
-rw-r--r--fs/nfsd/lockd.c79
-rw-r--r--fs/nfsd/nfs3proc.c702
-rw-r--r--fs/nfsd/nfs3xdr.c1092
-rw-r--r--fs/nfsd/nfs4acl.c954
-rw-r--r--fs/nfsd/nfs4callback.c547
-rw-r--r--fs/nfsd/nfs4idmap.c588
-rw-r--r--fs/nfsd/nfs4proc.c984
-rw-r--r--fs/nfsd/nfs4state.c3320
-rw-r--r--fs/nfsd/nfs4xdr.c2536
-rw-r--r--fs/nfsd/nfscache.c328
-rw-r--r--fs/nfsd/nfsctl.c438
-rw-r--r--fs/nfsd/nfsfh.c532
-rw-r--r--fs/nfsd/nfsproc.c605
-rw-r--r--fs/nfsd/nfssvc.c385
-rw-r--r--fs/nfsd/nfsxdr.c511
-rw-r--r--fs/nfsd/stats.c101
-rw-r--r--fs/nfsd/vfs.c1859
-rw-r--r--fs/nls/Kconfig504
-rw-r--r--fs/nls/Makefile46
-rw-r--r--fs/nls/nls_ascii.c167
-rw-r--r--fs/nls/nls_base.c497
-rw-r--r--fs/nls/nls_cp1250.c347
-rw-r--r--fs/nls/nls_cp1251.c302
-rw-r--r--fs/nls/nls_cp1255.c385
-rw-r--r--fs/nls/nls_cp437.c388
-rw-r--r--fs/nls/nls_cp737.c351
-rw-r--r--fs/nls/nls_cp775.c320
-rw-r--r--fs/nls/nls_cp850.c316
-rw-r--r--fs/nls/nls_cp852.c338
-rw-r--r--fs/nls/nls_cp855.c300
-rw-r--r--fs/nls/nls_cp857.c302
-rw-r--r--fs/nls/nls_cp860.c365
-rw-r--r--fs/nls/nls_cp861.c388
-rw-r--r--fs/nls/nls_cp862.c422
-rw-r--r--fs/nls/nls_cp863.c382
-rw-r--r--fs/nls/nls_cp864.c408
-rw-r--r--fs/nls/nls_cp865.c388
-rw-r--r--fs/nls/nls_cp866.c306
-rw-r--r--fs/nls/nls_cp869.c316
-rw-r--r--fs/nls/nls_cp874.c276
-rw-r--r--fs/nls/nls_cp932.c7934
-rw-r--r--fs/nls/nls_cp936.c11019
-rw-r--r--fs/nls/nls_cp949.c13947
-rw-r--r--fs/nls/nls_cp950.c9483
-rw-r--r--fs/nls/nls_euc-jp.c583
-rw-r--r--fs/nls/nls_iso8859-1.c258
-rw-r--r--fs/nls/nls_iso8859-13.c286
-rw-r--r--fs/nls/nls_iso8859-14.c342
-rw-r--r--fs/nls/nls_iso8859-15.c308
-rw-r--r--fs/nls/nls_iso8859-2.c309
-rw-r--r--fs/nls/nls_iso8859-3.c309
-rw-r--r--fs/nls/nls_iso8859-4.c309
-rw-r--r--fs/nls/nls_iso8859-5.c273
-rw-r--r--fs/nls/nls_iso8859-6.c264
-rw-r--r--fs/nls/nls_iso8859-7.c318
-rw-r--r--fs/nls/nls_iso8859-9.c273
-rw-r--r--fs/nls/nls_koi8-r.c324
-rw-r--r--fs/nls/nls_koi8-ru.c83
-rw-r--r--fs/nls/nls_koi8-u.c331
-rw-r--r--fs/nls/nls_utf8.c61
-rw-r--r--fs/ntfs/ChangeLog1350
-rw-r--r--fs/ntfs/Makefile19
-rw-r--r--fs/ntfs/aops.c2324
-rw-r--r--fs/ntfs/aops.h109
-rw-r--r--fs/ntfs/attrib.c1258
-rw-r--r--fs/ntfs/attrib.h100
-rw-r--r--fs/ntfs/bitmap.c192
-rw-r--r--fs/ntfs/bitmap.h118
-rw-r--r--fs/ntfs/collate.c124
-rw-r--r--fs/ntfs/collate.h50
-rw-r--r--fs/ntfs/compress.c957
-rw-r--r--fs/ntfs/debug.c180
-rw-r--r--fs/ntfs/debug.h67
-rw-r--r--fs/ntfs/dir.c1569
-rw-r--r--fs/ntfs/dir.h48
-rw-r--r--fs/ntfs/endian.h93
-rw-r--r--fs/ntfs/file.c155
-rw-r--r--fs/ntfs/index.c461
-rw-r--r--fs/ntfs/index.h148
-rw-r--r--fs/ntfs/inode.c2616
-rw-r--r--fs/ntfs/inode.h321
-rw-r--r--fs/ntfs/layout.h2413
-rw-r--r--fs/ntfs/lcnalloc.c1002
-rw-r--r--fs/ntfs/lcnalloc.h112
-rw-r--r--fs/ntfs/logfile.c705
-rw-r--r--fs/ntfs/logfile.h307
-rw-r--r--fs/ntfs/malloc.h62
-rw-r--r--fs/ntfs/mft.c2829
-rw-r--r--fs/ntfs/mft.h127
-rw-r--r--fs/ntfs/mst.c203
-rw-r--r--fs/ntfs/namei.c498
-rw-r--r--fs/ntfs/ntfs.h129
-rw-r--r--fs/ntfs/quota.c117
-rw-r--r--fs/ntfs/quota.h35
-rw-r--r--fs/ntfs/runlist.c1438
-rw-r--r--fs/ntfs/runlist.h89
-rw-r--r--fs/ntfs/super.c2771
-rw-r--r--fs/ntfs/sysctl.c85
-rw-r--r--fs/ntfs/sysctl.h42
-rw-r--r--fs/ntfs/time.h100
-rw-r--r--fs/ntfs/types.h66
-rw-r--r--fs/ntfs/unistr.c384
-rw-r--r--fs/ntfs/upcase.c90
-rw-r--r--fs/ntfs/volume.h171
-rw-r--r--fs/open.c1076
-rw-r--r--fs/openpromfs/Makefile7
-rw-r--r--fs/openpromfs/inode.c1098
-rw-r--r--fs/partitions/Kconfig228
-rw-r--r--fs/partitions/Makefile20
-rw-r--r--fs/partitions/acorn.c557
-rw-r--r--fs/partitions/acorn.h14
-rw-r--r--fs/partitions/amiga.c128
-rw-r--r--fs/partitions/amiga.h6
-rw-r--r--fs/partitions/atari.c149
-rw-r--r--fs/partitions/atari.h34
-rw-r--r--fs/partitions/check.c445
-rw-r--r--fs/partitions/check.h36
-rw-r--r--fs/partitions/devfs.c130
-rw-r--r--fs/partitions/devfs.h10
-rw-r--r--fs/partitions/efi.c645
-rw-r--r--fs/partitions/efi.h131
-rw-r--r--fs/partitions/ibm.c191
-rw-r--r--fs/partitions/ibm.h1
-rw-r--r--fs/partitions/ldm.c1483
-rw-r--r--fs/partitions/ldm.h220
-rw-r--r--fs/partitions/mac.c130
-rw-r--r--fs/partitions/mac.h44
-rw-r--r--fs/partitions/msdos.c479
-rw-r--r--fs/partitions/msdos.h8
-rw-r--r--fs/partitions/osf.c78
-rw-r--r--fs/partitions/osf.h7
-rw-r--r--fs/partitions/sgi.c82
-rw-r--r--fs/partitions/sgi.h8
-rw-r--r--fs/partitions/sun.c91
-rw-r--r--fs/partitions/sun.h7
-rw-r--r--fs/partitions/ultrix.c47
-rw-r--r--fs/partitions/ultrix.h5
-rw-r--r--fs/pipe.c835
-rw-r--r--fs/posix_acl.c381
-rw-r--r--fs/proc/Makefile14
-rw-r--r--fs/proc/array.c484
-rw-r--r--fs/proc/base.c2056
-rw-r--r--fs/proc/generic.c705
-rw-r--r--fs/proc/inode-alloc.txt14
-rw-r--r--fs/proc/inode.c218
-rw-r--r--fs/proc/internal.h48
-rw-r--r--fs/proc/kcore.c404
-rw-r--r--fs/proc/kmsg.c55
-rw-r--r--fs/proc/mmu.c67
-rw-r--r--fs/proc/nommu.c135
-rw-r--r--fs/proc/proc_devtree.c165
-rw-r--r--fs/proc/proc_misc.c615
-rw-r--r--fs/proc/proc_tty.c242
-rw-r--r--fs/proc/root.c161
-rw-r--r--fs/proc/task_mmu.c235
-rw-r--r--fs/proc/task_nommu.c164
-rw-r--r--fs/qnx4/Makefile7
-rw-r--r--fs/qnx4/README9
-rw-r--r--fs/qnx4/bitmap.c165
-rw-r--r--fs/qnx4/dir.c99
-rw-r--r--fs/qnx4/file.c42
-rw-r--r--fs/qnx4/fsync.c170
-rw-r--r--fs/qnx4/inode.c603
-rw-r--r--fs/qnx4/namei.c249
-rw-r--r--fs/qnx4/truncate.c39
-rw-r--r--fs/quota.c382
-rw-r--r--fs/quota_v1.c200
-rw-r--r--fs/quota_v2.c693
-rw-r--r--fs/ramfs/Makefile7
-rw-r--r--fs/ramfs/inode.c246
-rw-r--r--fs/read_write.c730
-rw-r--r--fs/readdir.c300
-rw-r--r--fs/reiserfs/Makefile36
-rw-r--r--fs/reiserfs/README161
-rw-r--r--fs/reiserfs/bitmap.c1169
-rw-r--r--fs/reiserfs/dir.c275
-rw-r--r--fs/reiserfs/do_balan.c1597
-rw-r--r--fs/reiserfs/file.c1408
-rw-r--r--fs/reiserfs/fix_node.c2518
-rw-r--r--fs/reiserfs/hashes.c209
-rw-r--r--fs/reiserfs/ibalance.c1058
-rw-r--r--fs/reiserfs/inode.c2846
-rw-r--r--fs/reiserfs/ioctl.c151
-rw-r--r--fs/reiserfs/item_ops.c788
-rw-r--r--fs/reiserfs/journal.c3876
-rw-r--r--fs/reiserfs/lbalance.c1222
-rw-r--r--fs/reiserfs/namei.c1491
-rw-r--r--fs/reiserfs/objectid.c206
-rw-r--r--fs/reiserfs/prints.c727
-rw-r--r--fs/reiserfs/procfs.c664
-rw-r--r--fs/reiserfs/resize.c182
-rw-r--r--fs/reiserfs/stree.c2073
-rw-r--r--fs/reiserfs/super.c2148
-rw-r--r--fs/reiserfs/tail_conversion.c276
-rw-r--r--fs/reiserfs/xattr.c1450
-rw-r--r--fs/reiserfs/xattr_acl.c571
-rw-r--r--fs/reiserfs/xattr_security.c69
-rw-r--r--fs/reiserfs/xattr_trusted.c81
-rw-r--r--fs/reiserfs/xattr_user.c99
-rw-r--r--fs/romfs/Makefile7
-rw-r--r--fs/romfs/inode.c648
-rw-r--r--fs/select.c534
-rw-r--r--fs/seq_file.c440
-rw-r--r--fs/smbfs/Makefile39
-rw-r--r--fs/smbfs/cache.c209
-rw-r--r--fs/smbfs/dir.c693
-rw-r--r--fs/smbfs/file.c423
-rw-r--r--fs/smbfs/getopt.c64
-rw-r--r--fs/smbfs/getopt.h14
-rw-r--r--fs/smbfs/inode.c849
-rw-r--r--fs/smbfs/ioctl.c67
-rw-r--r--fs/smbfs/proc.c3509
-rw-r--r--fs/smbfs/proto.h87
-rw-r--r--fs/smbfs/request.c823
-rw-r--r--fs/smbfs/request.h70
-rw-r--r--fs/smbfs/smb_debug.h34
-rw-r--r--fs/smbfs/smbiod.c341
-rw-r--r--fs/smbfs/sock.c388
-rw-r--r--fs/smbfs/symlink.c70
-rw-r--r--fs/stat.c410
-rw-r--r--fs/super.c860
-rw-r--r--fs/sysfs/Makefile6
-rw-r--r--fs/sysfs/bin.c204
-rw-r--r--fs/sysfs/dir.c475
-rw-r--r--fs/sysfs/file.c447
-rw-r--r--fs/sysfs/group.c84
-rw-r--r--fs/sysfs/inode.c165
-rw-r--r--fs/sysfs/mount.c107
-rw-r--r--fs/sysfs/symlink.c180
-rw-r--r--fs/sysfs/sysfs.h95
-rw-r--r--fs/sysv/CHANGES60
-rw-r--r--fs/sysv/ChangeLog106
-rw-r--r--fs/sysv/INTRO182
-rw-r--r--fs/sysv/Makefile8
-rw-r--r--fs/sysv/balloc.c239
-rw-r--r--fs/sysv/dir.c388
-rw-r--r--fs/sysv/file.c49
-rw-r--r--fs/sysv/ialloc.c240
-rw-r--r--fs/sysv/inode.c354
-rw-r--r--fs/sysv/itree.c475
-rw-r--r--fs/sysv/namei.c318
-rw-r--r--fs/sysv/super.c572
-rw-r--r--fs/sysv/symlink.c20
-rw-r--r--fs/sysv/sysv.h244
-rw-r--r--fs/udf/Makefile9
-rw-r--r--fs/udf/balloc.c959
-rw-r--r--fs/udf/crc.c178
-rw-r--r--fs/udf/dir.c268
-rw-r--r--fs/udf/directory.c343
-rw-r--r--fs/udf/ecma_167.h864
-rw-r--r--fs/udf/file.c270
-rw-r--r--fs/udf/fsync.c56
-rw-r--r--fs/udf/ialloc.c170
-rw-r--r--fs/udf/inode.c2010
-rw-r--r--fs/udf/lowlevel.c77
-rw-r--r--fs/udf/misc.c313
-rw-r--r--fs/udf/namei.c1334
-rw-r--r--fs/udf/osta_udf.h296
-rw-r--r--fs/udf/partition.c226
-rw-r--r--fs/udf/super.c1934
-rw-r--r--fs/udf/symlink.c123
-rw-r--r--fs/udf/truncate.c284
-rw-r--r--fs/udf/udf_i.h26
-rw-r--r--fs/udf/udf_sb.h139
-rw-r--r--fs/udf/udfdecl.h167
-rw-r--r--fs/udf/udfend.h81
-rw-r--r--fs/udf/udftime.c174
-rw-r--r--fs/udf/unicode.c516
-rw-r--r--fs/ufs/Makefile8
-rw-r--r--fs/ufs/balloc.c818
-rw-r--r--fs/ufs/cylinder.c209
-rw-r--r--fs/ufs/dir.c627
-rw-r--r--fs/ufs/file.c55
-rw-r--r--fs/ufs/ialloc.c302
-rw-r--r--fs/ufs/inode.c816
-rw-r--r--fs/ufs/namei.c375
-rw-r--r--fs/ufs/super.c1347
-rw-r--r--fs/ufs/swab.h133
-rw-r--r--fs/ufs/symlink.c42
-rw-r--r--fs/ufs/truncate.c477
-rw-r--r--fs/ufs/util.c257
-rw-r--r--fs/ufs/util.h526
-rw-r--r--fs/umsdos/notes17
-rw-r--r--fs/vfat/Makefile7
-rw-r--r--fs/vfat/namei.c1082
-rw-r--r--fs/xattr.c480
-rw-r--r--fs/xattr_acl.c99
-rw-r--r--fs/xfs/Kconfig85
-rw-r--r--fs/xfs/Makefile150
-rw-r--r--fs/xfs/linux-2.6/kmem.c134
-rw-r--r--fs/xfs/linux-2.6/kmem.h157
-rw-r--r--fs/xfs/linux-2.6/mrlock.h106
-rw-r--r--fs/xfs/linux-2.6/mutex.h53
-rw-r--r--fs/xfs/linux-2.6/sema.h67
-rw-r--r--fs/xfs/linux-2.6/spin.h56
-rw-r--r--fs/xfs/linux-2.6/sv.h89
-rw-r--r--fs/xfs/linux-2.6/time.h51
-rw-r--r--fs/xfs/linux-2.6/xfs_aops.c1275
-rw-r--r--fs/xfs/linux-2.6/xfs_buf.c1980
-rw-r--r--fs/xfs/linux-2.6/xfs_buf.h591
-rw-r--r--fs/xfs/linux-2.6/xfs_cred.h50
-rw-r--r--fs/xfs/linux-2.6/xfs_export.c205
-rw-r--r--fs/xfs/linux-2.6/xfs_export.h122
-rw-r--r--fs/xfs/linux-2.6/xfs_file.c573
-rw-r--r--fs/xfs/linux-2.6/xfs_fs_subr.c124
-rw-r--r--fs/xfs/linux-2.6/xfs_fs_subr.h48
-rw-r--r--fs/xfs/linux-2.6/xfs_globals.c74
-rw-r--r--fs/xfs/linux-2.6/xfs_globals.h44
-rw-r--r--fs/xfs/linux-2.6/xfs_ioctl.c1336
-rw-r--r--fs/xfs/linux-2.6/xfs_ioctl32.c163
-rw-r--r--fs/xfs/linux-2.6/xfs_ioctl32.h34
-rw-r--r--fs/xfs/linux-2.6/xfs_iops.c680
-rw-r--r--fs/xfs/linux-2.6/xfs_iops.h51
-rw-r--r--fs/xfs/linux-2.6/xfs_linux.h374
-rw-r--r--fs/xfs/linux-2.6/xfs_lrw.c1082
-rw-r--r--fs/xfs/linux-2.6/xfs_lrw.h116
-rw-r--r--fs/xfs/linux-2.6/xfs_stats.c132
-rw-r--r--fs/xfs/linux-2.6/xfs_stats.h166
-rw-r--r--fs/xfs/linux-2.6/xfs_super.c912
-rw-r--r--fs/xfs/linux-2.6/xfs_super.h138
-rw-r--r--fs/xfs/linux-2.6/xfs_sysctl.c174
-rw-r--r--fs/xfs/linux-2.6/xfs_sysctl.h114
-rw-r--r--fs/xfs/linux-2.6/xfs_version.h44
-rw-r--r--fs/xfs/linux-2.6/xfs_vfs.c330
-rw-r--r--fs/xfs/linux-2.6/xfs_vfs.h223
-rw-r--r--fs/xfs/linux-2.6/xfs_vnode.c455
-rw-r--r--fs/xfs/linux-2.6/xfs_vnode.h666
-rw-r--r--fs/xfs/quota/xfs_dquot.c1648
-rw-r--r--fs/xfs/quota/xfs_dquot.h224
-rw-r--r--fs/xfs/quota/xfs_dquot_item.c715
-rw-r--r--fs/xfs/quota/xfs_dquot_item.h66
-rw-r--r--fs/xfs/quota/xfs_qm.c2848
-rw-r--r--fs/xfs/quota/xfs_qm.h236
-rw-r--r--fs/xfs/quota/xfs_qm_bhv.c410
-rw-r--r--fs/xfs/quota/xfs_qm_stats.c149
-rw-r--r--fs/xfs/quota/xfs_qm_stats.h68
-rw-r--r--fs/xfs/quota/xfs_qm_syscalls.c1458
-rw-r--r--fs/xfs/quota/xfs_quota_priv.h192
-rw-r--r--fs/xfs/quota/xfs_trans_dquot.c941
-rw-r--r--fs/xfs/support/debug.c127
-rw-r--r--fs/xfs/support/debug.h72
-rw-r--r--fs/xfs/support/ktrace.c346
-rw-r--r--fs/xfs/support/ktrace.h101
-rw-r--r--fs/xfs/support/move.c66
-rw-r--r--fs/xfs/support/move.h84
-rw-r--r--fs/xfs/support/qsort.c155
-rw-r--r--fs/xfs/support/qsort.h41
-rw-r--r--fs/xfs/support/uuid.c151
-rw-r--r--fs/xfs/support/uuid.h48
-rw-r--r--fs/xfs/xfs.h40
-rw-r--r--fs/xfs/xfs_acl.c937
-rw-r--r--fs/xfs/xfs_acl.h116
-rw-r--r--fs/xfs/xfs_ag.h345
-rw-r--r--fs/xfs/xfs_alloc.c2623
-rw-r--r--fs/xfs/xfs_alloc.h203
-rw-r--r--fs/xfs/xfs_alloc_btree.c2204
-rw-r--r--fs/xfs/xfs_alloc_btree.h257
-rw-r--r--fs/xfs/xfs_arch.h213
-rw-r--r--fs/xfs/xfs_attr.c2660
-rw-r--r--fs/xfs/xfs_attr.h193
-rw-r--r--fs/xfs/xfs_attr_leaf.c3050
-rw-r--r--fs/xfs/xfs_attr_leaf.h308
-rw-r--r--fs/xfs/xfs_attr_sf.h149
-rw-r--r--fs/xfs/xfs_behavior.c218
-rw-r--r--fs/xfs/xfs_behavior.h204
-rw-r--r--fs/xfs/xfs_bit.c312
-rw-r--r--fs/xfs/xfs_bit.h85
-rw-r--r--fs/xfs/xfs_bmap.c6246
-rw-r--r--fs/xfs/xfs_bmap.h379
-rw-r--r--fs/xfs/xfs_bmap_btree.c2807
-rw-r--r--fs/xfs/xfs_bmap_btree.h701
-rw-r--r--fs/xfs/xfs_btree.c949
-rw-r--r--fs/xfs/xfs_btree.h592
-rw-r--r--fs/xfs/xfs_buf_item.c1221
-rw-r--r--fs/xfs/xfs_buf_item.h171
-rw-r--r--fs/xfs/xfs_cap.h84
-rw-r--r--fs/xfs/xfs_clnt.h110
-rw-r--r--fs/xfs/xfs_da_btree.c2648
-rw-r--r--fs/xfs/xfs_da_btree.h335
-rw-r--r--fs/xfs/xfs_dfrag.c387
-rw-r--r--fs/xfs/xfs_dfrag.h67
-rw-r--r--fs/xfs/xfs_dinode.h418
-rw-r--r--fs/xfs/xfs_dir.c1223
-rw-r--r--fs/xfs/xfs_dir.h154
-rw-r--r--fs/xfs/xfs_dir2.c859
-rw-r--r--fs/xfs/xfs_dir2.h109
-rw-r--r--fs/xfs/xfs_dir2_block.c1248
-rw-r--r--fs/xfs/xfs_dir2_block.h126
-rw-r--r--fs/xfs/xfs_dir2_data.c855
-rw-r--r--fs/xfs/xfs_dir2_data.h231
-rw-r--r--fs/xfs/xfs_dir2_leaf.c1896
-rw-r--r--fs/xfs/xfs_dir2_leaf.h360
-rw-r--r--fs/xfs/xfs_dir2_node.c2020
-rw-r--r--fs/xfs/xfs_dir2_node.h159
-rw-r--r--fs/xfs/xfs_dir2_sf.c1317
-rw-r--r--fs/xfs/xfs_dir2_sf.h243
-rw-r--r--fs/xfs/xfs_dir2_trace.c235
-rw-r--r--fs/xfs/xfs_dir2_trace.h86
-rw-r--r--fs/xfs/xfs_dir_leaf.c2231
-rw-r--r--fs/xfs/xfs_dir_leaf.h248
-rw-r--r--fs/xfs/xfs_dir_sf.h172
-rw-r--r--fs/xfs/xfs_dmapi.h212
-rw-r--r--fs/xfs/xfs_dmops.c52
-rw-r--r--fs/xfs/xfs_error.c327
-rw-r--r--fs/xfs/xfs_error.h196
-rw-r--r--fs/xfs/xfs_extfree_item.c668
-rw-r--r--fs/xfs/xfs_extfree_item.h123
-rw-r--r--fs/xfs/xfs_fs.h527
-rw-r--r--fs/xfs/xfs_fsops.c616
-rw-r--r--fs/xfs/xfs_fsops.h67
-rw-r--r--fs/xfs/xfs_ialloc.c1401
-rw-r--r--fs/xfs/xfs_ialloc.h184
-rw-r--r--fs/xfs/xfs_ialloc_btree.c2094
-rw-r--r--fs/xfs/xfs_ialloc_btree.h314
-rw-r--r--fs/xfs/xfs_iget.c1022
-rw-r--r--fs/xfs/xfs_imap.h54
-rw-r--r--fs/xfs/xfs_inode.c3876
-rw-r--r--fs/xfs/xfs_inode.h554
-rw-r--r--fs/xfs/xfs_inode_item.c1092
-rw-r--r--fs/xfs/xfs_inode_item.h197
-rw-r--r--fs/xfs/xfs_inum.h173
-rw-r--r--fs/xfs/xfs_iocore.c133
-rw-r--r--fs/xfs/xfs_iomap.c1000
-rw-r--r--fs/xfs/xfs_iomap.h107
-rw-r--r--fs/xfs/xfs_itable.c858
-rw-r--r--fs/xfs/xfs_itable.h106
-rw-r--r--fs/xfs/xfs_log.c3560
-rw-r--r--fs/xfs/xfs_log.h182
-rw-r--r--fs/xfs/xfs_log_priv.h561
-rw-r--r--fs/xfs/xfs_log_recover.c4098
-rw-r--r--fs/xfs/xfs_log_recover.h81
-rw-r--r--fs/xfs/xfs_mac.h120
-rw-r--r--fs/xfs/xfs_macros.c2136
-rw-r--r--fs/xfs/xfs_macros.h104
-rw-r--r--fs/xfs/xfs_mount.c1586
-rw-r--r--fs/xfs/xfs_mount.h573
-rw-r--r--fs/xfs/xfs_qmops.c71
-rw-r--r--fs/xfs/xfs_quota.h356
-rw-r--r--fs/xfs/xfs_refcache.h66
-rw-r--r--fs/xfs/xfs_rename.c673
-rw-r--r--fs/xfs/xfs_rtalloc.c2469
-rw-r--r--fs/xfs/xfs_rtalloc.h187
-rw-r--r--fs/xfs/xfs_rw.c356
-rw-r--r--fs/xfs/xfs_rw.h154
-rw-r--r--fs/xfs/xfs_sb.h583
-rw-r--r--fs/xfs/xfs_trans.c1315
-rw-r--r--fs/xfs/xfs_trans.h1042
-rw-r--r--fs/xfs/xfs_trans_ail.c596
-rw-r--r--fs/xfs/xfs_trans_buf.c1093
-rw-r--r--fs/xfs/xfs_trans_extfree.c156
-rw-r--r--fs/xfs/xfs_trans_inode.c342
-rw-r--r--fs/xfs/xfs_trans_item.c553
-rw-r--r--fs/xfs/xfs_trans_priv.h73
-rw-r--r--fs/xfs/xfs_trans_space.h105
-rw-r--r--fs/xfs/xfs_types.h182
-rw-r--r--fs/xfs/xfs_utils.c488
-rw-r--r--fs/xfs/xfs_utils.h52
-rw-r--r--fs/xfs/xfs_vfsops.c1941
-rw-r--r--fs/xfs/xfs_vnodeops.c4712
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;