diff options
| author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
| commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
| tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/affs | |
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/affs')
| -rw-r--r-- | fs/affs/Changes | 343 | ||||
| -rw-r--r-- | fs/affs/Makefile | 9 | ||||
| -rw-r--r-- | fs/affs/affs.h | 304 | ||||
| -rw-r--r-- | fs/affs/amigaffs.c | 509 | ||||
| -rw-r--r-- | fs/affs/bitmap.c | 390 | ||||
| -rw-r--r-- | fs/affs/dir.c | 155 | ||||
| -rw-r--r-- | fs/affs/file.c | 920 | ||||
| -rw-r--r-- | fs/affs/inode.c | 411 | ||||
| -rw-r--r-- | fs/affs/namei.c | 443 | ||||
| -rw-r--r-- | fs/affs/super.c | 569 | ||||
| -rw-r--r-- | fs/affs/symlink.c | 78 |
11 files changed, 4131 insertions, 0 deletions
diff --git a/fs/affs/Changes b/fs/affs/Changes new file mode 100644 index 000000000000..a29409c1ffe0 --- /dev/null +++ b/fs/affs/Changes | |||
| @@ -0,0 +1,343 @@ | |||
| 1 | (Note: I consider version numbers as cheap. That means | ||
| 2 | that I do not like numbers like 0.1 and the like for | ||
| 3 | things that can be used since quite some time. But | ||
| 4 | then, 3.1 doesn't mean 'perfectly stable', too.) | ||
| 5 | |||
| 6 | Known bugs: | ||
| 7 | ----------- | ||
| 8 | |||
| 9 | - Doesn't work on the alpha. The only 64/32-bit | ||
| 10 | problem that I'm aware of (pointer/int conversion | ||
| 11 | in readdir()) gives compiler warnings but is | ||
| 12 | apparently not causing the failure, as directory | ||
| 13 | reads basically work (but all files are of size 0). | ||
| 14 | Alas, I've got no alpha to debug. :-( | ||
| 15 | |||
| 16 | - The partition checker (drivers/block/genhd.c) | ||
| 17 | doesn't work with devices which have 256 byte | ||
| 18 | blocks (some very old SCSI drives). | ||
| 19 | |||
| 20 | - The feature to automatically make the fs clean | ||
| 21 | might leave a trashed file system with the | ||
| 22 | bitmap flag set valid. | ||
| 23 | |||
| 24 | - When a file is truncated to a size that is not | ||
| 25 | a multiple of the blocksize, the rest of the | ||
| 26 | last allocated block is not cleared. Well, | ||
| 27 | this fs never claimed to be Posix conformant. | ||
| 28 | |||
| 29 | Please direct bug reports to: zippel@linux-m68k.org | ||
| 30 | |||
| 31 | Version 3.20 | ||
| 32 | ------------ | ||
| 33 | - kill kernel lock | ||
| 34 | - fix for a possible bitmap corruption | ||
| 35 | |||
| 36 | Version 3.19 | ||
| 37 | ------------ | ||
| 38 | |||
| 39 | - sizeof changes from Kernel Janitor Project | ||
| 40 | - several bug fixes found with fsx | ||
| 41 | |||
| 42 | Version 3.18 | ||
| 43 | ------------ | ||
| 44 | |||
| 45 | - change to global min macro + warning fixes | ||
| 46 | - add module tags | ||
| 47 | |||
| 48 | Version 3.17 | ||
| 49 | ------------ | ||
| 50 | |||
| 51 | - locking fixes | ||
| 52 | - wrong sign in __affs_hash_dentry | ||
| 53 | - remove unnecessary check in affs_new_inode | ||
| 54 | - enable international mode for dircache fs | ||
| 55 | |||
| 56 | Version 3.16 | ||
| 57 | ------------ | ||
| 58 | |||
| 59 | - use mark_buffer_dirty_inode instead of mark_buffer_dirty. | ||
| 60 | - introduce affs_lock_{link|dir|ext}. | ||
| 61 | |||
| 62 | Version 3.15 | ||
| 63 | ------------ | ||
| 64 | |||
| 65 | - disable link to directories until we can properly support them. | ||
| 66 | - locking fixes for link creation/removal. | ||
| 67 | |||
| 68 | Version 3.14 | ||
| 69 | ------------ | ||
| 70 | |||
| 71 | - correctly cut off long file names for compares | ||
| 72 | - correctly initialize s_last_bmap | ||
| 73 | |||
| 74 | Version 3.13 | ||
| 75 | ------------ | ||
| 76 | |||
| 77 | Major cleanup for 2.4 [Roman Zippel] | ||
| 78 | - new extended block handling | ||
| 79 | - new bitmap allocation functions | ||
| 80 | - locking should be safe for the future | ||
| 81 | - cleanup of some interfaces | ||
| 82 | |||
| 83 | Version 3.12 | ||
| 84 | ------------ | ||
| 85 | |||
| 86 | more 2.4 fixes: [Roman Zippel] | ||
| 87 | - s_lock changes | ||
| 88 | - increased getblock mess | ||
| 89 | - clear meta blocks | ||
| 90 | |||
| 91 | Version 3.11 | ||
| 92 | ------------ | ||
| 93 | |||
| 94 | - Converted to use 2.3.x page cache [Dave Jones <dave@powertweak.com>] | ||
| 95 | - Corruption in truncate() bugfix [Ken Tyler <kent@werple.net.au>] | ||
| 96 | |||
| 97 | Version 3.10 | ||
| 98 | ------------ | ||
| 99 | |||
| 100 | - Changed partition checker to allow devices | ||
| 101 | with physical blocks != 512 bytes. | ||
| 102 | |||
| 103 | - The partition checker now also ignores the | ||
| 104 | word at 0xd0 that Windows likes to write to. | ||
| 105 | |||
| 106 | Version 3.9 | ||
| 107 | ----------- | ||
| 108 | |||
| 109 | - Moved cleanup from release_file() to put_inode(). | ||
| 110 | This makes the first one obsolete. | ||
| 111 | |||
| 112 | - truncate() zeroes the unused remainder of a | ||
| 113 | partially used last block when a file is truncated. | ||
| 114 | It also marks the inode dirty now (which is not | ||
| 115 | really necessary as notify_change() will do | ||
| 116 | it anyway). | ||
| 117 | |||
| 118 | - Added a few comments, fixed some typos (and | ||
| 119 | introduced some new ones), made the debug messages | ||
| 120 | more consistent. Changed a bad example in the | ||
| 121 | doc file (affs.txt). | ||
| 122 | |||
| 123 | - Sets the NOEXEC flag in read_super() for old file | ||
| 124 | systems, since you can't run programs on them. | ||
| 125 | |||
| 126 | Version 3.8 | ||
| 127 | ----------- | ||
| 128 | Bill Hawes kindly reviewed the affs and sent me the | ||
| 129 | patches he did. They're marked (BH). Thanks, Bill! | ||
| 130 | |||
| 131 | - Cleanup of error handling in read_super(). | ||
| 132 | Didn't release all resources in case of an | ||
| 133 | error. (BH) | ||
| 134 | |||
| 135 | - put_inode() releases the ext cache only if it's | ||
| 136 | no longer needed. (BH) | ||
| 137 | |||
| 138 | - One set of dentry callbacks is enough. (BH) | ||
| 139 | |||
| 140 | - Cleanup of error handling in namei.c. (BH) | ||
| 141 | |||
| 142 | - Cleanup of error handling in file.c. (BH) | ||
| 143 | |||
| 144 | - The original blocksize of the device is | ||
| 145 | restored when the fs is unmounted. (BH) | ||
| 146 | |||
| 147 | - getblock() did not invalidate the key cache | ||
| 148 | when it allocated a new block. | ||
| 149 | |||
| 150 | - Removed some unnecessary locks as Bill | ||
| 151 | suggested. | ||
| 152 | |||
| 153 | - Simplified match_name(), changed all hashing | ||
| 154 | and case insensitive name comparisons to use | ||
| 155 | uppercase. This makes the tolower() routines | ||
| 156 | obsolete. | ||
| 157 | |||
| 158 | - Added mount option 'mufs' to force muFS | ||
| 159 | uid/gid interpretation. | ||
| 160 | |||
| 161 | - File mode changes were not updated on disk. | ||
| 162 | This was fixed before, but somehow got lost. | ||
| 163 | |||
| 164 | Version 3.7 | ||
| 165 | ----------- | ||
| 166 | |||
| 167 | - Added dentry callbacks to allow the dcache to | ||
| 168 | operate case insensitive and length ignorant | ||
| 169 | like the affs itself. | ||
| 170 | |||
| 171 | - getblock() didn't update the lastblock field in the | ||
| 172 | inode if the fs was not an OFS. This bug only shows | ||
| 173 | up if a file was enlarged via truncate() and there | ||
| 174 | was not enough space. | ||
| 175 | |||
| 176 | - Remove some more superfluous code left over from | ||
| 177 | the old link days ... | ||
| 178 | |||
| 179 | - Fixed some oversights which were in patch 2.1.78. | ||
| 180 | |||
| 181 | - Fixed a few typos. | ||
| 182 | |||
| 183 | Version 3.6 | ||
| 184 | ----------- | ||
| 185 | |||
| 186 | - dentry changes. (Thanks to Jes Sorensen for his help.) | ||
| 187 | |||
| 188 | - Fixed bug in balloc(): Superblock was not set dirty after | ||
| 189 | the bitmap was changed, so the bitmap wasn't sync'd. | ||
| 190 | |||
| 191 | - Fixed nasty bug in find_new_zone(): If the current | ||
| 192 | zone number was zero, the loop didn't terminate, | ||
| 193 | causing a solid lock-up. | ||
| 194 | |||
| 195 | - Removed support for old-style directory reads. | ||
| 196 | |||
| 197 | - Fixed bug in add_entry(): When doing a sorted insert, | ||
| 198 | the pointer to the next entry in the hash chain wasn't | ||
| 199 | correctly byte-swapped. Since most of the users of the | ||
| 200 | affs use it on a 68k, they didn't notice. But why did | ||
| 201 | I not find this during my tests? | ||
| 202 | |||
| 203 | - Fixed some oversights (version wasn't updated on some | ||
| 204 | directory changes). | ||
| 205 | |||
| 206 | - Handling of hard links rewritten. To the VFS | ||
| 207 | they appear now as normal Unix links. They are | ||
| 208 | now resolved only once in lookup(). The backside | ||
| 209 | is that unlink(), rename() and rmdir() have to | ||
| 210 | be smart about them, but the result is worth the | ||
| 211 | effort. This also led to some code cleanup. | ||
| 212 | |||
| 213 | - Changed name type to unsigned char; the test for | ||
| 214 | invalid filenames didn't work correctly. | ||
| 215 | (Thanks to Michael Krause for pointing at this.) | ||
| 216 | |||
| 217 | - Changed mapping of executable flag. | ||
| 218 | |||
| 219 | - Changed all network byte-order macros to the | ||
| 220 | recommended ones. | ||
| 221 | |||
| 222 | - Added a remount function, so attempts to remount | ||
| 223 | a dircache filesystem or one with errors read/write | ||
| 224 | can be trapped. Previously, ro remounts didn't | ||
| 225 | flush the super block, and rw remounts didn't | ||
| 226 | create allocation zones ... | ||
| 227 | |||
| 228 | - Call shrink_dcache_parent() in rmdir(). | ||
| 229 | (Thanks to Bill Hawes.) | ||
| 230 | |||
| 231 | - Permission checks in unlink(). | ||
| 232 | |||
| 233 | - Allow mounting of volumes with superfluous | ||
| 234 | bitmap pointers read only, also allows them | ||
| 235 | to be remounted read/write. | ||
| 236 | |||
| 237 | - Owner/Group defaults now to the fs user (i.e. | ||
| 238 | the one that mounted it) instead of root. This | ||
| 239 | obsoletes the mount options uid and gid. | ||
| 240 | |||
| 241 | - Argument to volume option could overflow the | ||
| 242 | name buffer. It is now silently truncated to | ||
| 243 | 30 characters. (Damn it! This kind of bug | ||
| 244 | is too embarrassing.) | ||
| 245 | |||
| 246 | - Split inode.c into 2 files, the superblock | ||
| 247 | routines desperately wanted their own file. | ||
| 248 | |||
| 249 | - truncate() didn't allocate an extension block | ||
| 250 | cache. If a file was extended by means of | ||
| 251 | truncate(), this led to an Oops. | ||
| 252 | |||
| 253 | - fsuser is now checked last. | ||
| 254 | |||
| 255 | - rename() will not ignore changes in filename | ||
| 256 | casing any more (though mv(1) still won't allow | ||
| 257 | you to do "mv oldname OldName"). | ||
| 258 | |||
| 259 | Version 3.5 | ||
| 260 | ----------- | ||
| 261 | |||
| 262 | - Extension block caches are now allocated on | ||
| 263 | demand instead of when a file is opened, as | ||
| 264 | files can be read and written without opening | ||
| 265 | them (e. g. the loopback device does this). | ||
| 266 | |||
| 267 | - Removed an unused function. | ||
| 268 | |||
| 269 | Version 3.4 | ||
| 270 | ----------- | ||
| 271 | |||
| 272 | - Hash chains are now sorted by block numbers. | ||
| 273 | (Thanks to Kars de Jong for finding this.) | ||
| 274 | - Removed all unnecessary external symbols. | ||
| 275 | |||
| 276 | Version 3.3 | ||
| 277 | ----------- | ||
| 278 | |||
| 279 | - Tried to make all types 'correct' and consistent. | ||
| 280 | - Errors and warnings are now reported via a | ||
| 281 | function. They are all prefixed by a severity | ||
| 282 | and have the same appearance: | ||
| 283 | "AFFS: <function>: <error message>" | ||
| 284 | (There's one exception to this, as in that function | ||
| 285 | is no pointer to the super block available.) | ||
| 286 | - The filesystem is remounted read-only after an | ||
| 287 | error. | ||
| 288 | - The names of newly created filesystem objects are | ||
| 289 | now checked for validity. | ||
| 290 | - Minor cleanups in comments. | ||
| 291 | - Added this Changes file. At last! | ||
| 292 | |||
| 293 | Version 3.2 | ||
| 294 | ----------- | ||
| 295 | |||
| 296 | - Extension block cache: Reading/writing of huge files | ||
| 297 | (several MB) is much faster (of course the added | ||
| 298 | overhead slows down opening, but this is hardly | ||
| 299 | noticeable). | ||
| 300 | - The same get_block()-routine can now be used for | ||
| 301 | both OFS and FFS. | ||
| 302 | - The super block is now searched in the block that | ||
| 303 | was calculated and in the one following. This | ||
| 304 | should remedy the round-off error introduced by | ||
| 305 | the 1-k blocks that Linux uses. | ||
| 306 | - Minor changes to adhere to the new VFS interface. | ||
| 307 | - The number of used blocks is now also calculated | ||
| 308 | if the filesystem is mounted read-only. | ||
| 309 | - Prefixed some constants with AFFS_ to avoid name | ||
| 310 | clashes. | ||
| 311 | - Removed 'EXPERIMENTAL' status. | ||
| 312 | |||
| 313 | Version 3.1 | ||
| 314 | ----------- | ||
| 315 | |||
| 316 | - Fixed a nasty bug which didn't allow read-only | ||
| 317 | mounts. | ||
| 318 | - Allow dir-cache filesystems to be mounted | ||
| 319 | read only. | ||
| 320 | - OFS support. | ||
| 321 | - Several other changes I just cannot remember | ||
| 322 | any more. | ||
| 323 | |||
| 324 | Version 3.0 | ||
| 325 | ----------- | ||
| 326 | |||
| 327 | - Almost complete rewrite for the new VFS | ||
| 328 | interface in Linux 1.3. | ||
| 329 | - Write support. | ||
| 330 | - Support for hard and symbolic links. | ||
| 331 | - Lots of things I remember even less ... | ||
| 332 | |||
| 333 | Version 2.0 | ||
| 334 | ----------- | ||
| 335 | |||
| 336 | - Fixed a few things to get it compiled. | ||
| 337 | - Automatic root block calculation. | ||
| 338 | - Partition checker for genhd.c | ||
| 339 | |||
| 340 | ======================================== | ||
| 341 | |||
| 342 | Let's just call Ray Burr's original affs | ||
| 343 | 'Version 1.0'. | ||
diff --git a/fs/affs/Makefile b/fs/affs/Makefile new file mode 100644 index 000000000000..b2c4f54446f3 --- /dev/null +++ b/fs/affs/Makefile | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # | ||
| 2 | # Makefile for the Linux affs filesystem routines. | ||
| 3 | # | ||
| 4 | |||
| 5 | #EXTRA_CFLAGS=-DDEBUG=1 | ||
| 6 | |||
| 7 | obj-$(CONFIG_AFFS_FS) += affs.o | ||
| 8 | |||
| 9 | 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 000000000000..0c6799f2137a --- /dev/null +++ b/fs/affs/affs.h | |||
| @@ -0,0 +1,304 @@ | |||
| 1 | #include <linux/types.h> | ||
| 2 | #include <linux/fs.h> | ||
| 3 | #include <linux/buffer_head.h> | ||
| 4 | #include <linux/affs_fs.h> | ||
| 5 | #include <linux/amigaffs.h> | ||
| 6 | |||
| 7 | /* AmigaOS allows file names with up to 30 characters length. | ||
| 8 | * Names longer than that will be silently truncated. If you | ||
| 9 | * want to disallow this, comment out the following #define. | ||
| 10 | * Creating filesystem objects with longer names will then | ||
| 11 | * result in an error (ENAMETOOLONG). | ||
| 12 | */ | ||
| 13 | /*#define AFFS_NO_TRUNCATE */ | ||
| 14 | |||
| 15 | /* Ugly macros make the code more pretty. */ | ||
| 16 | |||
| 17 | #define GET_END_PTR(st,p,sz) ((st *)((char *)(p)+((sz)-sizeof(st)))) | ||
| 18 | #define AFFS_GET_HASHENTRY(data,hashkey) be32_to_cpu(((struct dir_front *)data)->hashtable[hashkey]) | ||
| 19 | #define AFFS_BLOCK(sb, bh, blk) (AFFS_HEAD(bh)->table[AFFS_SB(sb)->s_hashsize-1-(blk)]) | ||
| 20 | |||
| 21 | #ifdef __LITTLE_ENDIAN | ||
| 22 | #define BO_EXBITS 0x18UL | ||
| 23 | #elif defined(__BIG_ENDIAN) | ||
| 24 | #define BO_EXBITS 0x00UL | ||
| 25 | #else | ||
| 26 | #error Endianness must be known for affs to work. | ||
| 27 | #endif | ||
| 28 | |||
| 29 | #define AFFS_HEAD(bh) ((struct affs_head *)(bh)->b_data) | ||
| 30 | #define AFFS_TAIL(sb, bh) ((struct affs_tail *)((bh)->b_data+(sb)->s_blocksize-sizeof(struct affs_tail))) | ||
| 31 | #define AFFS_ROOT_HEAD(bh) ((struct affs_root_head *)(bh)->b_data) | ||
| 32 | #define AFFS_ROOT_TAIL(sb, bh) ((struct affs_root_tail *)((bh)->b_data+(sb)->s_blocksize-sizeof(struct affs_root_tail))) | ||
| 33 | #define AFFS_DATA_HEAD(bh) ((struct affs_data_head *)(bh)->b_data) | ||
| 34 | #define AFFS_DATA(bh) (((struct affs_data_head *)(bh)->b_data)->data) | ||
| 35 | |||
| 36 | #define AFFS_CACHE_SIZE PAGE_SIZE | ||
| 37 | |||
| 38 | #define AFFS_MAX_PREALLOC 32 | ||
| 39 | #define AFFS_LC_SIZE (AFFS_CACHE_SIZE/sizeof(u32)/2) | ||
| 40 | #define AFFS_AC_SIZE (AFFS_CACHE_SIZE/sizeof(struct affs_ext_key)/2) | ||
| 41 | #define AFFS_AC_MASK (AFFS_AC_SIZE-1) | ||
| 42 | |||
| 43 | struct affs_ext_key { | ||
| 44 | u32 ext; /* idx of the extended block */ | ||
| 45 | u32 key; /* block number */ | ||
| 46 | }; | ||
| 47 | |||
| 48 | /* | ||
| 49 | * affs fs inode data in memory | ||
| 50 | */ | ||
| 51 | struct affs_inode_info { | ||
| 52 | u32 i_opencnt; | ||
| 53 | struct semaphore i_link_lock; /* Protects internal inode access. */ | ||
| 54 | struct semaphore i_ext_lock; /* Protects internal inode access. */ | ||
| 55 | #define i_hash_lock i_ext_lock | ||
| 56 | u32 i_blkcnt; /* block count */ | ||
| 57 | u32 i_extcnt; /* extended block count */ | ||
| 58 | u32 *i_lc; /* linear cache of extended blocks */ | ||
| 59 | u32 i_lc_size; | ||
| 60 | u32 i_lc_shift; | ||
| 61 | u32 i_lc_mask; | ||
| 62 | struct affs_ext_key *i_ac; /* associative cache of extended blocks */ | ||
| 63 | u32 i_ext_last; /* last accessed extended block */ | ||
| 64 | struct buffer_head *i_ext_bh; /* bh of last extended block */ | ||
| 65 | loff_t mmu_private; | ||
| 66 | u32 i_protect; /* unused attribute bits */ | ||
| 67 | u32 i_lastalloc; /* last allocated block */ | ||
| 68 | int i_pa_cnt; /* number of preallocated blocks */ | ||
| 69 | struct inode vfs_inode; | ||
| 70 | }; | ||
| 71 | |||
| 72 | /* short cut to get to the affs specific inode data */ | ||
| 73 | static inline struct affs_inode_info *AFFS_I(struct inode *inode) | ||
| 74 | { | ||
| 75 | return list_entry(inode, struct affs_inode_info, vfs_inode); | ||
| 76 | } | ||
| 77 | |||
| 78 | /* | ||
| 79 | * super-block data in memory | ||
| 80 | * | ||
| 81 | * Block numbers are adjusted for their actual size | ||
| 82 | * | ||
| 83 | */ | ||
| 84 | |||
| 85 | struct affs_bm_info { | ||
| 86 | u32 bm_key; /* Disk block number */ | ||
| 87 | u32 bm_free; /* Free blocks in here */ | ||
| 88 | }; | ||
| 89 | |||
| 90 | struct affs_sb_info { | ||
| 91 | int s_partition_size; /* Partition size in blocks. */ | ||
| 92 | int s_reserved; /* Number of reserved blocks. */ | ||
| 93 | //u32 s_blksize; /* Initial device blksize */ | ||
| 94 | u32 s_data_blksize; /* size of the data block w/o header */ | ||
| 95 | u32 s_root_block; /* FFS root block number. */ | ||
| 96 | int s_hashsize; /* Size of hash table. */ | ||
| 97 | unsigned long s_flags; /* See below. */ | ||
| 98 | uid_t s_uid; /* uid to override */ | ||
| 99 | gid_t s_gid; /* gid to override */ | ||
| 100 | umode_t s_mode; /* mode to override */ | ||
| 101 | struct buffer_head *s_root_bh; /* Cached root block. */ | ||
| 102 | struct semaphore s_bmlock; /* Protects bitmap access. */ | ||
| 103 | struct affs_bm_info *s_bitmap; /* Bitmap infos. */ | ||
| 104 | u32 s_bmap_count; /* # of bitmap blocks. */ | ||
| 105 | u32 s_bmap_bits; /* # of bits in one bitmap blocks */ | ||
| 106 | u32 s_last_bmap; | ||
| 107 | struct buffer_head *s_bmap_bh; | ||
| 108 | char *s_prefix; /* Prefix for volumes and assigns. */ | ||
| 109 | int s_prefix_len; /* Length of prefix. */ | ||
| 110 | char s_volume[32]; /* Volume prefix for absolute symlinks. */ | ||
| 111 | }; | ||
| 112 | |||
| 113 | #define SF_INTL 0x0001 /* International filesystem. */ | ||
| 114 | #define SF_BM_VALID 0x0002 /* Bitmap is valid. */ | ||
| 115 | #define SF_IMMUTABLE 0x0004 /* Protection bits cannot be changed */ | ||
| 116 | #define SF_QUIET 0x0008 /* chmod errors will be not reported */ | ||
| 117 | #define SF_SETUID 0x0010 /* Ignore Amiga uid */ | ||
| 118 | #define SF_SETGID 0x0020 /* Ignore Amiga gid */ | ||
| 119 | #define SF_SETMODE 0x0040 /* Ignore Amiga protection bits */ | ||
| 120 | #define SF_MUFS 0x0100 /* Use MUFS uid/gid mapping */ | ||
| 121 | #define SF_OFS 0x0200 /* Old filesystem */ | ||
| 122 | #define SF_PREFIX 0x0400 /* Buffer for prefix is allocated */ | ||
| 123 | #define SF_VERBOSE 0x0800 /* Talk about fs when mounting */ | ||
| 124 | |||
| 125 | /* short cut to get to the affs specific sb data */ | ||
| 126 | static inline struct affs_sb_info *AFFS_SB(struct super_block *sb) | ||
| 127 | { | ||
| 128 | return sb->s_fs_info; | ||
| 129 | } | ||
| 130 | |||
| 131 | /* amigaffs.c */ | ||
| 132 | |||
| 133 | extern int affs_insert_hash(struct inode *inode, struct buffer_head *bh); | ||
| 134 | extern int affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh); | ||
| 135 | extern int affs_remove_header(struct dentry *dentry); | ||
| 136 | extern u32 affs_checksum_block(struct super_block *sb, struct buffer_head *bh); | ||
| 137 | extern void affs_fix_checksum(struct super_block *sb, struct buffer_head *bh); | ||
| 138 | extern void secs_to_datestamp(time_t secs, struct affs_date *ds); | ||
| 139 | extern mode_t prot_to_mode(u32 prot); | ||
| 140 | extern void mode_to_prot(struct inode *inode); | ||
| 141 | extern void affs_error(struct super_block *sb, const char *function, const char *fmt, ...); | ||
| 142 | extern void affs_warning(struct super_block *sb, const char *function, const char *fmt, ...); | ||
| 143 | extern int affs_check_name(const unsigned char *name, int len); | ||
| 144 | extern int affs_copy_name(unsigned char *bstr, struct dentry *dentry); | ||
| 145 | |||
| 146 | /* bitmap. c */ | ||
| 147 | |||
| 148 | extern u32 affs_count_free_blocks(struct super_block *s); | ||
| 149 | extern void affs_free_block(struct super_block *sb, u32 block); | ||
| 150 | extern u32 affs_alloc_block(struct inode *inode, u32 goal); | ||
| 151 | extern int affs_init_bitmap(struct super_block *sb, int *flags); | ||
| 152 | extern void affs_free_bitmap(struct super_block *sb); | ||
| 153 | |||
| 154 | /* namei.c */ | ||
| 155 | |||
| 156 | extern int affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len); | ||
| 157 | extern struct dentry *affs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *); | ||
| 158 | extern int affs_unlink(struct inode *dir, struct dentry *dentry); | ||
| 159 | extern int affs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *); | ||
| 160 | extern int affs_mkdir(struct inode *dir, struct dentry *dentry, int mode); | ||
| 161 | extern int affs_rmdir(struct inode *dir, struct dentry *dentry); | ||
| 162 | extern int affs_link(struct dentry *olddentry, struct inode *dir, | ||
| 163 | struct dentry *dentry); | ||
| 164 | extern int affs_symlink(struct inode *dir, struct dentry *dentry, | ||
| 165 | const char *symname); | ||
| 166 | extern int affs_rename(struct inode *old_dir, struct dentry *old_dentry, | ||
| 167 | struct inode *new_dir, struct dentry *new_dentry); | ||
| 168 | |||
| 169 | /* inode.c */ | ||
| 170 | |||
| 171 | extern unsigned long affs_parent_ino(struct inode *dir); | ||
| 172 | extern struct inode *affs_new_inode(struct inode *dir); | ||
| 173 | extern int affs_notify_change(struct dentry *dentry, struct iattr *attr); | ||
| 174 | extern void affs_put_inode(struct inode *inode); | ||
| 175 | extern void affs_delete_inode(struct inode *inode); | ||
| 176 | extern void affs_clear_inode(struct inode *inode); | ||
| 177 | extern void affs_read_inode(struct inode *inode); | ||
| 178 | extern int affs_write_inode(struct inode *inode, int); | ||
| 179 | extern int affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s32 type); | ||
| 180 | |||
| 181 | /* file.c */ | ||
| 182 | |||
| 183 | void affs_free_prealloc(struct inode *inode); | ||
| 184 | extern void affs_truncate(struct inode *); | ||
| 185 | |||
| 186 | /* dir.c */ | ||
| 187 | |||
| 188 | extern void affs_dir_truncate(struct inode *); | ||
| 189 | |||
| 190 | /* jump tables */ | ||
| 191 | |||
| 192 | extern struct inode_operations affs_file_inode_operations; | ||
| 193 | extern struct inode_operations affs_dir_inode_operations; | ||
| 194 | extern struct inode_operations affs_symlink_inode_operations; | ||
| 195 | extern struct file_operations affs_file_operations; | ||
| 196 | extern struct file_operations affs_file_operations_ofs; | ||
| 197 | extern struct file_operations affs_dir_operations; | ||
| 198 | extern struct address_space_operations affs_symlink_aops; | ||
| 199 | extern struct address_space_operations affs_aops; | ||
| 200 | extern struct address_space_operations affs_aops_ofs; | ||
| 201 | |||
| 202 | extern struct dentry_operations affs_dentry_operations; | ||
| 203 | extern struct dentry_operations affs_dentry_operations_intl; | ||
| 204 | |||
| 205 | static inline void | ||
| 206 | affs_set_blocksize(struct super_block *sb, int size) | ||
| 207 | { | ||
| 208 | sb_set_blocksize(sb, size); | ||
| 209 | } | ||
| 210 | static inline struct buffer_head * | ||
| 211 | affs_bread(struct super_block *sb, int block) | ||
| 212 | { | ||
| 213 | pr_debug("affs_bread: %d\n", block); | ||
| 214 | if (block >= AFFS_SB(sb)->s_reserved && block < AFFS_SB(sb)->s_partition_size) | ||
| 215 | return sb_bread(sb, block); | ||
| 216 | return NULL; | ||
| 217 | } | ||
| 218 | static inline struct buffer_head * | ||
| 219 | affs_getblk(struct super_block *sb, int block) | ||
| 220 | { | ||
| 221 | pr_debug("affs_getblk: %d\n", block); | ||
| 222 | if (block >= AFFS_SB(sb)->s_reserved && block < AFFS_SB(sb)->s_partition_size) | ||
| 223 | return sb_getblk(sb, block); | ||
| 224 | return NULL; | ||
| 225 | } | ||
| 226 | static inline struct buffer_head * | ||
| 227 | affs_getzeroblk(struct super_block *sb, int block) | ||
| 228 | { | ||
| 229 | struct buffer_head *bh; | ||
| 230 | pr_debug("affs_getzeroblk: %d\n", block); | ||
| 231 | if (block >= AFFS_SB(sb)->s_reserved && block < AFFS_SB(sb)->s_partition_size) { | ||
| 232 | bh = sb_getblk(sb, block); | ||
| 233 | lock_buffer(bh); | ||
| 234 | memset(bh->b_data, 0 , sb->s_blocksize); | ||
| 235 | set_buffer_uptodate(bh); | ||
| 236 | unlock_buffer(bh); | ||
| 237 | return bh; | ||
| 238 | } | ||
| 239 | return NULL; | ||
| 240 | } | ||
| 241 | static inline struct buffer_head * | ||
| 242 | affs_getemptyblk(struct super_block *sb, int block) | ||
| 243 | { | ||
| 244 | struct buffer_head *bh; | ||
| 245 | pr_debug("affs_getemptyblk: %d\n", block); | ||
| 246 | if (block >= AFFS_SB(sb)->s_reserved && block < AFFS_SB(sb)->s_partition_size) { | ||
| 247 | bh = sb_getblk(sb, block); | ||
| 248 | wait_on_buffer(bh); | ||
| 249 | set_buffer_uptodate(bh); | ||
| 250 | return bh; | ||
| 251 | } | ||
| 252 | return NULL; | ||
| 253 | } | ||
| 254 | static inline void | ||
| 255 | affs_brelse(struct buffer_head *bh) | ||
| 256 | { | ||
| 257 | if (bh) | ||
| 258 | pr_debug("affs_brelse: %lld\n", (long long) bh->b_blocknr); | ||
| 259 | brelse(bh); | ||
| 260 | } | ||
| 261 | |||
| 262 | static inline void | ||
| 263 | affs_adjust_checksum(struct buffer_head *bh, u32 val) | ||
| 264 | { | ||
| 265 | u32 tmp = be32_to_cpu(((__be32 *)bh->b_data)[5]); | ||
| 266 | ((__be32 *)bh->b_data)[5] = cpu_to_be32(tmp - val); | ||
| 267 | } | ||
| 268 | static inline void | ||
| 269 | affs_adjust_bitmapchecksum(struct buffer_head *bh, u32 val) | ||
| 270 | { | ||
| 271 | u32 tmp = be32_to_cpu(((__be32 *)bh->b_data)[0]); | ||
| 272 | ((__be32 *)bh->b_data)[0] = cpu_to_be32(tmp - val); | ||
| 273 | } | ||
| 274 | |||
| 275 | static inline void | ||
| 276 | affs_lock_link(struct inode *inode) | ||
| 277 | { | ||
| 278 | down(&AFFS_I(inode)->i_link_lock); | ||
| 279 | } | ||
| 280 | static inline void | ||
| 281 | affs_unlock_link(struct inode *inode) | ||
| 282 | { | ||
| 283 | up(&AFFS_I(inode)->i_link_lock); | ||
| 284 | } | ||
| 285 | static inline void | ||
| 286 | affs_lock_dir(struct inode *inode) | ||
| 287 | { | ||
| 288 | down(&AFFS_I(inode)->i_hash_lock); | ||
| 289 | } | ||
| 290 | static inline void | ||
| 291 | affs_unlock_dir(struct inode *inode) | ||
| 292 | { | ||
| 293 | up(&AFFS_I(inode)->i_hash_lock); | ||
| 294 | } | ||
| 295 | static inline void | ||
| 296 | affs_lock_ext(struct inode *inode) | ||
| 297 | { | ||
| 298 | down(&AFFS_I(inode)->i_ext_lock); | ||
| 299 | } | ||
| 300 | static inline void | ||
| 301 | affs_unlock_ext(struct inode *inode) | ||
| 302 | { | ||
| 303 | up(&AFFS_I(inode)->i_ext_lock); | ||
| 304 | } | ||
diff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c new file mode 100644 index 000000000000..ccd624ef4272 --- /dev/null +++ b/fs/affs/amigaffs.c | |||
| @@ -0,0 +1,509 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/affs/amigaffs.c | ||
| 3 | * | ||
| 4 | * (c) 1996 Hans-Joachim Widmaier - Rewritten | ||
| 5 | * | ||
| 6 | * (C) 1993 Ray Burr - Amiga FFS filesystem. | ||
| 7 | * | ||
| 8 | * Please send bug reports to: hjw@zvw.de | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include "affs.h" | ||
| 12 | |||
| 13 | extern struct timezone sys_tz; | ||
| 14 | |||
| 15 | static char ErrorBuffer[256]; | ||
| 16 | |||
| 17 | /* | ||
| 18 | * Functions for accessing Amiga-FFS structures. | ||
| 19 | */ | ||
| 20 | |||
| 21 | |||
| 22 | /* Insert a header block bh into the directory dir | ||
| 23 | * caller must hold AFFS_DIR->i_hash_lock! | ||
| 24 | */ | ||
| 25 | |||
| 26 | int | ||
| 27 | affs_insert_hash(struct inode *dir, struct buffer_head *bh) | ||
| 28 | { | ||
| 29 | struct super_block *sb = dir->i_sb; | ||
| 30 | struct buffer_head *dir_bh; | ||
| 31 | u32 ino, hash_ino; | ||
| 32 | int offset; | ||
| 33 | |||
| 34 | ino = bh->b_blocknr; | ||
| 35 | offset = affs_hash_name(sb, AFFS_TAIL(sb, bh)->name + 1, AFFS_TAIL(sb, bh)->name[0]); | ||
| 36 | |||
| 37 | pr_debug("AFFS: insert_hash(dir=%u, ino=%d)\n", (u32)dir->i_ino, ino); | ||
| 38 | |||
| 39 | dir_bh = affs_bread(sb, dir->i_ino); | ||
| 40 | if (!dir_bh) | ||
| 41 | return -EIO; | ||
| 42 | |||
| 43 | hash_ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[offset]); | ||
| 44 | while (hash_ino) { | ||
| 45 | affs_brelse(dir_bh); | ||
| 46 | dir_bh = affs_bread(sb, hash_ino); | ||
| 47 | if (!dir_bh) | ||
| 48 | return -EIO; | ||
| 49 | hash_ino = be32_to_cpu(AFFS_TAIL(sb, dir_bh)->hash_chain); | ||
| 50 | } | ||
| 51 | AFFS_TAIL(sb, bh)->parent = cpu_to_be32(dir->i_ino); | ||
| 52 | AFFS_TAIL(sb, bh)->hash_chain = 0; | ||
| 53 | affs_fix_checksum(sb, bh); | ||
| 54 | |||
| 55 | if (dir->i_ino == dir_bh->b_blocknr) | ||
| 56 | AFFS_HEAD(dir_bh)->table[offset] = cpu_to_be32(ino); | ||
| 57 | else | ||
| 58 | AFFS_TAIL(sb, dir_bh)->hash_chain = cpu_to_be32(ino); | ||
| 59 | |||
| 60 | affs_adjust_checksum(dir_bh, ino); | ||
| 61 | mark_buffer_dirty_inode(dir_bh, dir); | ||
| 62 | affs_brelse(dir_bh); | ||
| 63 | |||
| 64 | dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; | ||
| 65 | dir->i_version++; | ||
| 66 | mark_inode_dirty(dir); | ||
| 67 | |||
| 68 | return 0; | ||
| 69 | } | ||
| 70 | |||
| 71 | /* Remove a header block from its directory. | ||
| 72 | * caller must hold AFFS_DIR->i_hash_lock! | ||
| 73 | */ | ||
| 74 | |||
| 75 | int | ||
| 76 | affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh) | ||
| 77 | { | ||
| 78 | struct super_block *sb; | ||
| 79 | struct buffer_head *bh; | ||
| 80 | u32 rem_ino, hash_ino; | ||
| 81 | __be32 ino; | ||
| 82 | int offset, retval; | ||
| 83 | |||
| 84 | sb = dir->i_sb; | ||
| 85 | rem_ino = rem_bh->b_blocknr; | ||
| 86 | offset = affs_hash_name(sb, AFFS_TAIL(sb, rem_bh)->name+1, AFFS_TAIL(sb, rem_bh)->name[0]); | ||
| 87 | pr_debug("AFFS: remove_hash(dir=%d, ino=%d, hashval=%d)\n", (u32)dir->i_ino, rem_ino, offset); | ||
| 88 | |||
| 89 | bh = affs_bread(sb, dir->i_ino); | ||
| 90 | if (!bh) | ||
| 91 | return -EIO; | ||
| 92 | |||
| 93 | retval = -ENOENT; | ||
| 94 | hash_ino = be32_to_cpu(AFFS_HEAD(bh)->table[offset]); | ||
| 95 | while (hash_ino) { | ||
| 96 | if (hash_ino == rem_ino) { | ||
| 97 | ino = AFFS_TAIL(sb, rem_bh)->hash_chain; | ||
| 98 | if (dir->i_ino == bh->b_blocknr) | ||
| 99 | AFFS_HEAD(bh)->table[offset] = ino; | ||
| 100 | else | ||
| 101 | AFFS_TAIL(sb, bh)->hash_chain = ino; | ||
| 102 | affs_adjust_checksum(bh, be32_to_cpu(ino) - hash_ino); | ||
| 103 | mark_buffer_dirty_inode(bh, dir); | ||
| 104 | AFFS_TAIL(sb, rem_bh)->parent = 0; | ||
| 105 | retval = 0; | ||
| 106 | break; | ||
| 107 | } | ||
| 108 | affs_brelse(bh); | ||
| 109 | bh = affs_bread(sb, hash_ino); | ||
| 110 | if (!bh) | ||
| 111 | return -EIO; | ||
| 112 | hash_ino = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain); | ||
| 113 | } | ||
| 114 | |||
| 115 | affs_brelse(bh); | ||
| 116 | |||
| 117 | dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; | ||
| 118 | dir->i_version++; | ||
| 119 | mark_inode_dirty(dir); | ||
| 120 | |||
| 121 | return retval; | ||
| 122 | } | ||
| 123 | |||
| 124 | static void | ||
| 125 | affs_fix_dcache(struct dentry *dentry, u32 entry_ino) | ||
| 126 | { | ||
| 127 | struct inode *inode = dentry->d_inode; | ||
| 128 | void *data = dentry->d_fsdata; | ||
| 129 | struct list_head *head, *next; | ||
| 130 | |||
| 131 | spin_lock(&dcache_lock); | ||
| 132 | head = &inode->i_dentry; | ||
| 133 | next = head->next; | ||
| 134 | while (next != head) { | ||
| 135 | dentry = list_entry(next, struct dentry, d_alias); | ||
| 136 | if (entry_ino == (u32)(long)dentry->d_fsdata) { | ||
| 137 | dentry->d_fsdata = data; | ||
| 138 | break; | ||
| 139 | } | ||
| 140 | next = next->next; | ||
| 141 | } | ||
| 142 | spin_unlock(&dcache_lock); | ||
| 143 | } | ||
| 144 | |||
| 145 | |||
| 146 | /* Remove header from link chain */ | ||
| 147 | |||
| 148 | static int | ||
| 149 | affs_remove_link(struct dentry *dentry) | ||
| 150 | { | ||
| 151 | struct inode *dir, *inode = dentry->d_inode; | ||
| 152 | struct super_block *sb = inode->i_sb; | ||
| 153 | struct buffer_head *bh = NULL, *link_bh = NULL; | ||
| 154 | u32 link_ino, ino; | ||
| 155 | int retval; | ||
| 156 | |||
| 157 | pr_debug("AFFS: remove_link(key=%ld)\n", inode->i_ino); | ||
| 158 | retval = -EIO; | ||
| 159 | bh = affs_bread(sb, inode->i_ino); | ||
| 160 | if (!bh) | ||
| 161 | goto done; | ||
| 162 | |||
| 163 | link_ino = (u32)(long)dentry->d_fsdata; | ||
| 164 | if (inode->i_ino == link_ino) { | ||
| 165 | /* we can't remove the head of the link, as its blocknr is still used as ino, | ||
| 166 | * so we remove the block of the first link instead. | ||
| 167 | */ | ||
| 168 | link_ino = be32_to_cpu(AFFS_TAIL(sb, bh)->link_chain); | ||
| 169 | link_bh = affs_bread(sb, link_ino); | ||
| 170 | if (!link_bh) | ||
| 171 | goto done; | ||
| 172 | |||
| 173 | dir = iget(sb, be32_to_cpu(AFFS_TAIL(sb, link_bh)->parent)); | ||
| 174 | if (!dir) | ||
| 175 | goto done; | ||
| 176 | |||
| 177 | affs_lock_dir(dir); | ||
| 178 | affs_fix_dcache(dentry, link_ino); | ||
| 179 | retval = affs_remove_hash(dir, link_bh); | ||
| 180 | if (retval) | ||
| 181 | goto done; | ||
| 182 | mark_buffer_dirty_inode(link_bh, inode); | ||
| 183 | |||
| 184 | memcpy(AFFS_TAIL(sb, bh)->name, AFFS_TAIL(sb, link_bh)->name, 32); | ||
| 185 | retval = affs_insert_hash(dir, bh); | ||
| 186 | if (retval) | ||
| 187 | goto done; | ||
| 188 | mark_buffer_dirty_inode(bh, inode); | ||
| 189 | |||
| 190 | affs_unlock_dir(dir); | ||
| 191 | iput(dir); | ||
| 192 | } else { | ||
| 193 | link_bh = affs_bread(sb, link_ino); | ||
| 194 | if (!link_bh) | ||
| 195 | goto done; | ||
| 196 | } | ||
| 197 | |||
| 198 | while ((ino = be32_to_cpu(AFFS_TAIL(sb, bh)->link_chain)) != 0) { | ||
| 199 | if (ino == link_ino) { | ||
| 200 | __be32 ino2 = AFFS_TAIL(sb, link_bh)->link_chain; | ||
| 201 | AFFS_TAIL(sb, bh)->link_chain = ino2; | ||
| 202 | affs_adjust_checksum(bh, be32_to_cpu(ino2) - link_ino); | ||
| 203 | mark_buffer_dirty_inode(bh, inode); | ||
| 204 | retval = 0; | ||
| 205 | /* Fix the link count, if bh is a normal header block without links */ | ||
| 206 | switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) { | ||
| 207 | case ST_LINKDIR: | ||
| 208 | case ST_LINKFILE: | ||
| 209 | break; | ||
| 210 | default: | ||
| 211 | if (!AFFS_TAIL(sb, bh)->link_chain) | ||
| 212 | inode->i_nlink = 1; | ||
| 213 | } | ||
| 214 | affs_free_block(sb, link_ino); | ||
| 215 | goto done; | ||
| 216 | } | ||
| 217 | affs_brelse(bh); | ||
| 218 | bh = affs_bread(sb, ino); | ||
| 219 | if (!bh) | ||
| 220 | goto done; | ||
| 221 | } | ||
| 222 | retval = -ENOENT; | ||
| 223 | done: | ||
| 224 | affs_brelse(link_bh); | ||
| 225 | affs_brelse(bh); | ||
| 226 | return retval; | ||
| 227 | } | ||
| 228 | |||
| 229 | |||
| 230 | static int | ||
| 231 | affs_empty_dir(struct inode *inode) | ||
| 232 | { | ||
| 233 | struct super_block *sb = inode->i_sb; | ||
| 234 | struct buffer_head *bh; | ||
| 235 | int retval, size; | ||
| 236 | |||
| 237 | retval = -EIO; | ||
| 238 | bh = affs_bread(sb, inode->i_ino); | ||
| 239 | if (!bh) | ||
| 240 | goto done; | ||
| 241 | |||
| 242 | retval = -ENOTEMPTY; | ||
| 243 | for (size = AFFS_SB(sb)->s_hashsize - 1; size >= 0; size--) | ||
| 244 | if (AFFS_HEAD(bh)->table[size]) | ||
| 245 | goto not_empty; | ||
| 246 | retval = 0; | ||
| 247 | not_empty: | ||
| 248 | affs_brelse(bh); | ||
| 249 | done: | ||
| 250 | return retval; | ||
| 251 | } | ||
| 252 | |||
| 253 | |||
| 254 | /* Remove a filesystem object. If the object to be removed has | ||
| 255 | * links to it, one of the links must be changed to inherit | ||
| 256 | * the file or directory. As above, any inode will do. | ||
| 257 | * The buffer will not be freed. If the header is a link, the | ||
| 258 | * block will be marked as free. | ||
| 259 | * This function returns a negative error number in case of | ||
| 260 | * an error, else 0 if the inode is to be deleted or 1 if not. | ||
| 261 | */ | ||
| 262 | |||
| 263 | int | ||
| 264 | affs_remove_header(struct dentry *dentry) | ||
| 265 | { | ||
| 266 | struct super_block *sb; | ||
| 267 | struct inode *inode, *dir; | ||
| 268 | struct buffer_head *bh = NULL; | ||
| 269 | int retval; | ||
| 270 | |||
| 271 | dir = dentry->d_parent->d_inode; | ||
| 272 | sb = dir->i_sb; | ||
| 273 | |||
| 274 | retval = -ENOENT; | ||
| 275 | inode = dentry->d_inode; | ||
| 276 | if (!inode) | ||
| 277 | goto done; | ||
| 278 | |||
| 279 | pr_debug("AFFS: remove_header(key=%ld)\n", inode->i_ino); | ||
| 280 | retval = -EIO; | ||
| 281 | bh = affs_bread(sb, (u32)(long)dentry->d_fsdata); | ||
| 282 | if (!bh) | ||
| 283 | goto done; | ||
| 284 | |||
| 285 | affs_lock_link(inode); | ||
| 286 | affs_lock_dir(dir); | ||
| 287 | switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) { | ||
| 288 | case ST_USERDIR: | ||
| 289 | /* if we ever want to support links to dirs | ||
| 290 | * i_hash_lock of the inode must only be | ||
| 291 | * taken after some checks | ||
| 292 | */ | ||
| 293 | affs_lock_dir(inode); | ||
| 294 | retval = affs_empty_dir(inode); | ||
| 295 | affs_unlock_dir(inode); | ||
| 296 | if (retval) | ||
| 297 | goto done_unlock; | ||
| 298 | break; | ||
| 299 | default: | ||
| 300 | break; | ||
| 301 | } | ||
| 302 | |||
| 303 | retval = affs_remove_hash(dir, bh); | ||
| 304 | if (retval) | ||
| 305 | goto done_unlock; | ||
| 306 | mark_buffer_dirty_inode(bh, inode); | ||
| 307 | |||
| 308 | affs_unlock_dir(dir); | ||
| 309 | |||
| 310 | if (inode->i_nlink > 1) | ||
| 311 | retval = affs_remove_link(dentry); | ||
| 312 | else | ||
| 313 | inode->i_nlink = 0; | ||
| 314 | affs_unlock_link(inode); | ||
| 315 | inode->i_ctime = CURRENT_TIME_SEC; | ||
| 316 | mark_inode_dirty(inode); | ||
| 317 | |||
| 318 | done: | ||
| 319 | affs_brelse(bh); | ||
| 320 | return retval; | ||
| 321 | |||
| 322 | done_unlock: | ||
| 323 | affs_unlock_dir(dir); | ||
| 324 | affs_unlock_link(inode); | ||
| 325 | goto done; | ||
| 326 | } | ||
| 327 | |||
| 328 | /* Checksum a block, do various consistency checks and optionally return | ||
| 329 | the blocks type number. DATA points to the block. If their pointers | ||
| 330 | are non-null, *PTYPE and *STYPE are set to the primary and secondary | ||
| 331 | block types respectively, *HASHSIZE is set to the size of the hashtable | ||
| 332 | (which lets us calculate the block size). | ||
| 333 | Returns non-zero if the block is not consistent. */ | ||
| 334 | |||
| 335 | u32 | ||
| 336 | affs_checksum_block(struct super_block *sb, struct buffer_head *bh) | ||
| 337 | { | ||
| 338 | __be32 *ptr = (__be32 *)bh->b_data; | ||
| 339 | u32 sum; | ||
| 340 | int bsize; | ||
| 341 | |||
| 342 | sum = 0; | ||
| 343 | for (bsize = sb->s_blocksize / sizeof(__be32); bsize > 0; bsize--) | ||
| 344 | sum += be32_to_cpu(*ptr++); | ||
| 345 | return sum; | ||
| 346 | } | ||
| 347 | |||
| 348 | /* | ||
| 349 | * Calculate the checksum of a disk block and store it | ||
| 350 | * at the indicated position. | ||
| 351 | */ | ||
| 352 | |||
| 353 | void | ||
| 354 | affs_fix_checksum(struct super_block *sb, struct buffer_head *bh) | ||
| 355 | { | ||
| 356 | int cnt = sb->s_blocksize / sizeof(__be32); | ||
| 357 | __be32 *ptr = (__be32 *)bh->b_data; | ||
| 358 | u32 checksum; | ||
| 359 | __be32 *checksumptr; | ||
| 360 | |||
| 361 | checksumptr = ptr + 5; | ||
| 362 | *checksumptr = 0; | ||
| 363 | for (checksum = 0; cnt > 0; ptr++, cnt--) | ||
| 364 | checksum += be32_to_cpu(*ptr); | ||
| 365 | *checksumptr = cpu_to_be32(-checksum); | ||
| 366 | } | ||
| 367 | |||
| 368 | void | ||
| 369 | secs_to_datestamp(time_t secs, struct affs_date *ds) | ||
| 370 | { | ||
| 371 | u32 days; | ||
| 372 | u32 minute; | ||
| 373 | |||
| 374 | secs -= sys_tz.tz_minuteswest * 60 + ((8 * 365 + 2) * 24 * 60 * 60); | ||
| 375 | if (secs < 0) | ||
| 376 | secs = 0; | ||
| 377 | days = secs / 86400; | ||
| 378 | secs -= days * 86400; | ||
| 379 | minute = secs / 60; | ||
| 380 | secs -= minute * 60; | ||
| 381 | |||
| 382 | ds->days = cpu_to_be32(days); | ||
| 383 | ds->mins = cpu_to_be32(minute); | ||
| 384 | ds->ticks = cpu_to_be32(secs * 50); | ||
| 385 | } | ||
| 386 | |||
| 387 | mode_t | ||
| 388 | prot_to_mode(u32 prot) | ||
| 389 | { | ||
| 390 | int mode = 0; | ||
| 391 | |||
| 392 | if (!(prot & FIBF_NOWRITE)) | ||
| 393 | mode |= S_IWUSR; | ||
| 394 | if (!(prot & FIBF_NOREAD)) | ||
| 395 | mode |= S_IRUSR; | ||
| 396 | if (!(prot & FIBF_NOEXECUTE)) | ||
| 397 | mode |= S_IXUSR; | ||
| 398 | if (prot & FIBF_GRP_WRITE) | ||
| 399 | mode |= S_IWGRP; | ||
| 400 | if (prot & FIBF_GRP_READ) | ||
| 401 | mode |= S_IRGRP; | ||
| 402 | if (prot & FIBF_GRP_EXECUTE) | ||
| 403 | mode |= S_IXGRP; | ||
| 404 | if (prot & FIBF_OTR_WRITE) | ||
| 405 | mode |= S_IWOTH; | ||
| 406 | if (prot & FIBF_OTR_READ) | ||
| 407 | mode |= S_IROTH; | ||
| 408 | if (prot & FIBF_OTR_EXECUTE) | ||
| 409 | mode |= S_IXOTH; | ||
| 410 | |||
| 411 | return mode; | ||
| 412 | } | ||
| 413 | |||
| 414 | void | ||
| 415 | mode_to_prot(struct inode *inode) | ||
| 416 | { | ||
| 417 | u32 prot = AFFS_I(inode)->i_protect; | ||
| 418 | mode_t mode = inode->i_mode; | ||
| 419 | |||
| 420 | if (!(mode & S_IXUSR)) | ||
| 421 | prot |= FIBF_NOEXECUTE; | ||
| 422 | if (!(mode & S_IRUSR)) | ||
| 423 | prot |= FIBF_NOREAD; | ||
| 424 | if (!(mode & S_IWUSR)) | ||
| 425 | prot |= FIBF_NOWRITE; | ||
| 426 | if (mode & S_IXGRP) | ||
| 427 | prot |= FIBF_GRP_EXECUTE; | ||
| 428 | if (mode & S_IRGRP) | ||
| 429 | prot |= FIBF_GRP_READ; | ||
| 430 | if (mode & S_IWGRP) | ||
| 431 | prot |= FIBF_GRP_WRITE; | ||
| 432 | if (mode & S_IXOTH) | ||
| 433 | prot |= FIBF_OTR_EXECUTE; | ||
| 434 | if (mode & S_IROTH) | ||
| 435 | prot |= FIBF_OTR_READ; | ||
| 436 | if (mode & S_IWOTH) | ||
| 437 | prot |= FIBF_OTR_WRITE; | ||
| 438 | |||
| 439 | AFFS_I(inode)->i_protect = prot; | ||
| 440 | } | ||
| 441 | |||
| 442 | void | ||
| 443 | affs_error(struct super_block *sb, const char *function, const char *fmt, ...) | ||
| 444 | { | ||
| 445 | va_list args; | ||
| 446 | |||
| 447 | va_start(args,fmt); | ||
| 448 | vsprintf(ErrorBuffer,fmt,args); | ||
| 449 | va_end(args); | ||
| 450 | |||
| 451 | printk(KERN_CRIT "AFFS error (device %s): %s(): %s\n", sb->s_id, | ||
| 452 | function,ErrorBuffer); | ||
| 453 | if (!(sb->s_flags & MS_RDONLY)) | ||
| 454 | printk(KERN_WARNING "AFFS: Remounting filesystem read-only\n"); | ||
| 455 | sb->s_flags |= MS_RDONLY; | ||
| 456 | } | ||
| 457 | |||
| 458 | void | ||
| 459 | affs_warning(struct super_block *sb, const char *function, const char *fmt, ...) | ||
| 460 | { | ||
| 461 | va_list args; | ||
| 462 | |||
| 463 | va_start(args,fmt); | ||
| 464 | vsprintf(ErrorBuffer,fmt,args); | ||
| 465 | va_end(args); | ||
| 466 | |||
| 467 | printk(KERN_WARNING "AFFS warning (device %s): %s(): %s\n", sb->s_id, | ||
| 468 | function,ErrorBuffer); | ||
| 469 | } | ||
| 470 | |||
| 471 | /* Check if the name is valid for a affs object. */ | ||
| 472 | |||
| 473 | int | ||
| 474 | affs_check_name(const unsigned char *name, int len) | ||
| 475 | { | ||
| 476 | int i; | ||
| 477 | |||
| 478 | if (len > 30) | ||
| 479 | #ifdef AFFS_NO_TRUNCATE | ||
| 480 | return -ENAMETOOLONG; | ||
| 481 | #else | ||
| 482 | len = 30; | ||
| 483 | #endif | ||
| 484 | |||
| 485 | for (i = 0; i < len; i++) { | ||
| 486 | if (name[i] < ' ' || name[i] == ':' | ||
| 487 | || (name[i] > 0x7e && name[i] < 0xa0)) | ||
| 488 | return -EINVAL; | ||
| 489 | } | ||
| 490 | |||
| 491 | return 0; | ||
| 492 | } | ||
| 493 | |||
| 494 | /* This function copies name to bstr, with at most 30 | ||
| 495 | * characters length. The bstr will be prepended by | ||
| 496 | * a length byte. | ||
| 497 | * NOTE: The name will must be already checked by | ||
| 498 | * affs_check_name()! | ||
| 499 | */ | ||
| 500 | |||
| 501 | int | ||
| 502 | affs_copy_name(unsigned char *bstr, struct dentry *dentry) | ||
| 503 | { | ||
| 504 | int len = min(dentry->d_name.len, 30u); | ||
| 505 | |||
| 506 | *bstr++ = len; | ||
| 507 | memcpy(bstr, dentry->d_name.name, len); | ||
| 508 | return len; | ||
| 509 | } | ||
diff --git a/fs/affs/bitmap.c b/fs/affs/bitmap.c new file mode 100644 index 000000000000..b0b953683c1a --- /dev/null +++ b/fs/affs/bitmap.c | |||
| @@ -0,0 +1,390 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/affs/bitmap.c | ||
| 3 | * | ||
| 4 | * (c) 1996 Hans-Joachim Widmaier | ||
| 5 | * | ||
| 6 | * bitmap.c contains the code that handles all bitmap related stuff - | ||
| 7 | * block allocation, deallocation, calculation of free space. | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include "affs.h" | ||
| 11 | |||
| 12 | /* This is, of course, shamelessly stolen from fs/minix */ | ||
| 13 | |||
| 14 | static int nibblemap[] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 }; | ||
| 15 | |||
| 16 | static u32 | ||
| 17 | affs_count_free_bits(u32 blocksize, const void *data) | ||
| 18 | { | ||
| 19 | const u32 *map; | ||
| 20 | u32 free; | ||
| 21 | u32 tmp; | ||
| 22 | |||
| 23 | map = data; | ||
| 24 | free = 0; | ||
| 25 | for (blocksize /= 4; blocksize > 0; blocksize--) { | ||
| 26 | tmp = *map++; | ||
| 27 | while (tmp) { | ||
| 28 | free += nibblemap[tmp & 0xf]; | ||
| 29 | tmp >>= 4; | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | return free; | ||
| 34 | } | ||
| 35 | |||
| 36 | u32 | ||
| 37 | affs_count_free_blocks(struct super_block *sb) | ||
| 38 | { | ||
| 39 | struct affs_bm_info *bm; | ||
| 40 | u32 free; | ||
| 41 | int i; | ||
| 42 | |||
| 43 | pr_debug("AFFS: count_free_blocks()\n"); | ||
| 44 | |||
| 45 | if (sb->s_flags & MS_RDONLY) | ||
| 46 | return 0; | ||
| 47 | |||
| 48 | down(&AFFS_SB(sb)->s_bmlock); | ||
| 49 | |||
| 50 | bm = AFFS_SB(sb)->s_bitmap; | ||
| 51 | free = 0; | ||
| 52 | for (i = AFFS_SB(sb)->s_bmap_count; i > 0; bm++, i--) | ||
| 53 | free += bm->bm_free; | ||
| 54 | |||
| 55 | up(&AFFS_SB(sb)->s_bmlock); | ||
| 56 | |||
| 57 | return free; | ||
| 58 | } | ||
| 59 | |||
| 60 | void | ||
| 61 | affs_free_block(struct super_block *sb, u32 block) | ||
| 62 | { | ||
| 63 | struct affs_sb_info *sbi = AFFS_SB(sb); | ||
| 64 | struct affs_bm_info *bm; | ||
| 65 | struct buffer_head *bh; | ||
| 66 | u32 blk, bmap, bit, mask, tmp; | ||
| 67 | __be32 *data; | ||
| 68 | |||
| 69 | pr_debug("AFFS: free_block(%u)\n", block); | ||
| 70 | |||
| 71 | if (block > sbi->s_partition_size) | ||
| 72 | goto err_range; | ||
| 73 | |||
| 74 | blk = block - sbi->s_reserved; | ||
| 75 | bmap = blk / sbi->s_bmap_bits; | ||
| 76 | bit = blk % sbi->s_bmap_bits; | ||
| 77 | bm = &sbi->s_bitmap[bmap]; | ||
| 78 | |||
| 79 | down(&sbi->s_bmlock); | ||
| 80 | |||
| 81 | bh = sbi->s_bmap_bh; | ||
| 82 | if (sbi->s_last_bmap != bmap) { | ||
| 83 | affs_brelse(bh); | ||
| 84 | bh = affs_bread(sb, bm->bm_key); | ||
| 85 | if (!bh) | ||
| 86 | goto err_bh_read; | ||
| 87 | sbi->s_bmap_bh = bh; | ||
| 88 | sbi->s_last_bmap = bmap; | ||
| 89 | } | ||
| 90 | |||
| 91 | mask = 1 << (bit & 31); | ||
| 92 | data = (__be32 *)bh->b_data + bit / 32 + 1; | ||
| 93 | |||
| 94 | /* mark block free */ | ||
| 95 | tmp = be32_to_cpu(*data); | ||
| 96 | if (tmp & mask) | ||
| 97 | goto err_free; | ||
| 98 | *data = cpu_to_be32(tmp | mask); | ||
| 99 | |||
| 100 | /* fix checksum */ | ||
| 101 | tmp = be32_to_cpu(*(__be32 *)bh->b_data); | ||
| 102 | *(__be32 *)bh->b_data = cpu_to_be32(tmp - mask); | ||
| 103 | |||
| 104 | mark_buffer_dirty(bh); | ||
| 105 | sb->s_dirt = 1; | ||
| 106 | bm->bm_free++; | ||
| 107 | |||
| 108 | up(&sbi->s_bmlock); | ||
| 109 | return; | ||
| 110 | |||
| 111 | err_free: | ||
| 112 | affs_warning(sb,"affs_free_block","Trying to free block %u which is already free", block); | ||
| 113 | up(&sbi->s_bmlock); | ||
| 114 | return; | ||
| 115 | |||
| 116 | err_bh_read: | ||
| 117 | affs_error(sb,"affs_free_block","Cannot read bitmap block %u", bm->bm_key); | ||
| 118 | sbi->s_bmap_bh = NULL; | ||
| 119 | sbi->s_last_bmap = ~0; | ||
| 120 | up(&sbi->s_bmlock); | ||
| 121 | return; | ||
| 122 | |||
| 123 | err_range: | ||
| 124 | affs_error(sb, "affs_free_block","Block %u outside partition", block); | ||
| 125 | return; | ||
| 126 | } | ||
| 127 | |||
| 128 | /* | ||
| 129 | * Allocate a block in the given allocation zone. | ||
| 130 | * Since we have to byte-swap the bitmap on little-endian | ||
| 131 | * machines, this is rather expensive. Therefor we will | ||
| 132 | * preallocate up to 16 blocks from the same word, if | ||
| 133 | * possible. We are not doing preallocations in the | ||
| 134 | * header zone, though. | ||
| 135 | */ | ||
| 136 | |||
| 137 | u32 | ||
| 138 | affs_alloc_block(struct inode *inode, u32 goal) | ||
| 139 | { | ||
| 140 | struct super_block *sb; | ||
| 141 | struct affs_sb_info *sbi; | ||
| 142 | struct affs_bm_info *bm; | ||
| 143 | struct buffer_head *bh; | ||
| 144 | __be32 *data, *enddata; | ||
| 145 | u32 blk, bmap, bit, mask, mask2, tmp; | ||
| 146 | int i; | ||
| 147 | |||
| 148 | sb = inode->i_sb; | ||
| 149 | sbi = AFFS_SB(sb); | ||
| 150 | |||
| 151 | pr_debug("AFFS: balloc(inode=%lu,goal=%u): ", inode->i_ino, goal); | ||
| 152 | |||
| 153 | if (AFFS_I(inode)->i_pa_cnt) { | ||
| 154 | pr_debug("%d\n", AFFS_I(inode)->i_lastalloc+1); | ||
| 155 | AFFS_I(inode)->i_pa_cnt--; | ||
| 156 | return ++AFFS_I(inode)->i_lastalloc; | ||
| 157 | } | ||
| 158 | |||
| 159 | if (!goal || goal > sbi->s_partition_size) { | ||
| 160 | if (goal) | ||
| 161 | affs_warning(sb, "affs_balloc", "invalid goal %d", goal); | ||
| 162 | //if (!AFFS_I(inode)->i_last_block) | ||
| 163 | // affs_warning(sb, "affs_balloc", "no last alloc block"); | ||
| 164 | goal = sbi->s_reserved; | ||
| 165 | } | ||
| 166 | |||
| 167 | blk = goal - sbi->s_reserved; | ||
| 168 | bmap = blk / sbi->s_bmap_bits; | ||
| 169 | bm = &sbi->s_bitmap[bmap]; | ||
| 170 | |||
| 171 | down(&sbi->s_bmlock); | ||
| 172 | |||
| 173 | if (bm->bm_free) | ||
| 174 | goto find_bmap_bit; | ||
| 175 | |||
| 176 | find_bmap: | ||
| 177 | /* search for the next bmap buffer with free bits */ | ||
| 178 | i = sbi->s_bmap_count; | ||
| 179 | do { | ||
| 180 | if (--i < 0) | ||
| 181 | goto err_full; | ||
| 182 | bmap++; | ||
| 183 | bm++; | ||
| 184 | if (bmap < sbi->s_bmap_count) | ||
| 185 | continue; | ||
| 186 | /* restart search at zero */ | ||
| 187 | bmap = 0; | ||
| 188 | bm = sbi->s_bitmap; | ||
| 189 | } while (!bm->bm_free); | ||
| 190 | blk = bmap * sbi->s_bmap_bits; | ||
| 191 | |||
| 192 | find_bmap_bit: | ||
| 193 | |||
| 194 | bh = sbi->s_bmap_bh; | ||
| 195 | if (sbi->s_last_bmap != bmap) { | ||
| 196 | affs_brelse(bh); | ||
| 197 | bh = affs_bread(sb, bm->bm_key); | ||
| 198 | if (!bh) | ||
| 199 | goto err_bh_read; | ||
| 200 | sbi->s_bmap_bh = bh; | ||
| 201 | sbi->s_last_bmap = bmap; | ||
| 202 | } | ||
| 203 | |||
| 204 | /* find an unused block in this bitmap block */ | ||
| 205 | bit = blk % sbi->s_bmap_bits; | ||
| 206 | data = (__be32 *)bh->b_data + bit / 32 + 1; | ||
| 207 | enddata = (__be32 *)((u8 *)bh->b_data + sb->s_blocksize); | ||
| 208 | mask = ~0UL << (bit & 31); | ||
| 209 | blk &= ~31UL; | ||
| 210 | |||
| 211 | tmp = be32_to_cpu(*data); | ||
| 212 | if (tmp & mask) | ||
| 213 | goto find_bit; | ||
| 214 | |||
| 215 | /* scan the rest of the buffer */ | ||
| 216 | do { | ||
| 217 | blk += 32; | ||
| 218 | if (++data >= enddata) | ||
| 219 | /* didn't find something, can only happen | ||
| 220 | * if scan didn't start at 0, try next bmap | ||
| 221 | */ | ||
| 222 | goto find_bmap; | ||
| 223 | } while (!*data); | ||
| 224 | tmp = be32_to_cpu(*data); | ||
| 225 | mask = ~0; | ||
| 226 | |||
| 227 | find_bit: | ||
| 228 | /* finally look for a free bit in the word */ | ||
| 229 | bit = ffs(tmp & mask) - 1; | ||
| 230 | blk += bit + sbi->s_reserved; | ||
| 231 | mask2 = mask = 1 << (bit & 31); | ||
| 232 | AFFS_I(inode)->i_lastalloc = blk; | ||
| 233 | |||
| 234 | /* prealloc as much as possible within this word */ | ||
| 235 | while ((mask2 <<= 1)) { | ||
| 236 | if (!(tmp & mask2)) | ||
| 237 | break; | ||
| 238 | AFFS_I(inode)->i_pa_cnt++; | ||
| 239 | mask |= mask2; | ||
| 240 | } | ||
| 241 | bm->bm_free -= AFFS_I(inode)->i_pa_cnt + 1; | ||
| 242 | |||
| 243 | *data = cpu_to_be32(tmp & ~mask); | ||
| 244 | |||
| 245 | /* fix checksum */ | ||
| 246 | tmp = be32_to_cpu(*(__be32 *)bh->b_data); | ||
| 247 | *(__be32 *)bh->b_data = cpu_to_be32(tmp + mask); | ||
| 248 | |||
| 249 | mark_buffer_dirty(bh); | ||
| 250 | sb->s_dirt = 1; | ||
| 251 | |||
| 252 | up(&sbi->s_bmlock); | ||
| 253 | |||
| 254 | pr_debug("%d\n", blk); | ||
| 255 | return blk; | ||
| 256 | |||
| 257 | err_bh_read: | ||
| 258 | affs_error(sb,"affs_read_block","Cannot read bitmap block %u", bm->bm_key); | ||
| 259 | sbi->s_bmap_bh = NULL; | ||
| 260 | sbi->s_last_bmap = ~0; | ||
| 261 | err_full: | ||
| 262 | up(&sbi->s_bmlock); | ||
| 263 | pr_debug("failed\n"); | ||
| 264 | return 0; | ||
| 265 | } | ||
| 266 | |||
| 267 | int affs_init_bitmap(struct super_block *sb, int *flags) | ||
| 268 | { | ||
| 269 | struct affs_bm_info *bm; | ||
| 270 | struct buffer_head *bmap_bh = NULL, *bh = NULL; | ||
| 271 | __be32 *bmap_blk; | ||
| 272 | u32 size, blk, end, offset, mask; | ||
| 273 | int i, res = 0; | ||
| 274 | struct affs_sb_info *sbi = AFFS_SB(sb); | ||
| 275 | |||
| 276 | if (*flags & MS_RDONLY) | ||
| 277 | return 0; | ||
| 278 | |||
| 279 | if (!AFFS_ROOT_TAIL(sb, sbi->s_root_bh)->bm_flag) { | ||
| 280 | printk(KERN_NOTICE "AFFS: Bitmap invalid - mounting %s read only\n", | ||
| 281 | sb->s_id); | ||
| 282 | *flags |= MS_RDONLY; | ||
| 283 | return 0; | ||
| 284 | } | ||
| 285 | |||
| 286 | sbi->s_last_bmap = ~0; | ||
| 287 | sbi->s_bmap_bh = NULL; | ||
| 288 | sbi->s_bmap_bits = sb->s_blocksize * 8 - 32; | ||
| 289 | sbi->s_bmap_count = (sbi->s_partition_size - sbi->s_reserved + | ||
| 290 | sbi->s_bmap_bits - 1) / sbi->s_bmap_bits; | ||
| 291 | size = sbi->s_bmap_count * sizeof(*bm); | ||
| 292 | bm = sbi->s_bitmap = kmalloc(size, GFP_KERNEL); | ||
| 293 | if (!sbi->s_bitmap) { | ||
| 294 | printk(KERN_ERR "AFFS: Bitmap allocation failed\n"); | ||
| 295 | return -ENOMEM; | ||
| 296 | } | ||
| 297 | memset(sbi->s_bitmap, 0, size); | ||
| 298 | |||
| 299 | bmap_blk = (__be32 *)sbi->s_root_bh->b_data; | ||
| 300 | blk = sb->s_blocksize / 4 - 49; | ||
| 301 | end = blk + 25; | ||
| 302 | |||
| 303 | for (i = sbi->s_bmap_count; i > 0; bm++, i--) { | ||
| 304 | affs_brelse(bh); | ||
| 305 | |||
| 306 | bm->bm_key = be32_to_cpu(bmap_blk[blk]); | ||
| 307 | bh = affs_bread(sb, bm->bm_key); | ||
| 308 | if (!bh) { | ||
| 309 | printk(KERN_ERR "AFFS: Cannot read bitmap\n"); | ||
| 310 | res = -EIO; | ||
| 311 | goto out; | ||
| 312 | } | ||
| 313 | if (affs_checksum_block(sb, bh)) { | ||
| 314 | printk(KERN_WARNING "AFFS: Bitmap %u invalid - mounting %s read only.\n", | ||
| 315 | bm->bm_key, sb->s_id); | ||
| 316 | *flags |= MS_RDONLY; | ||
| 317 | goto out; | ||
| 318 | } | ||
| 319 | pr_debug("AFFS: read bitmap block %d: %d\n", blk, bm->bm_key); | ||
| 320 | bm->bm_free = affs_count_free_bits(sb->s_blocksize - 4, bh->b_data + 4); | ||
| 321 | |||
| 322 | /* Don't try read the extension if this is the last block, | ||
| 323 | * but we also need the right bm pointer below | ||
| 324 | */ | ||
| 325 | if (++blk < end || i == 1) | ||
| 326 | continue; | ||
| 327 | if (bmap_bh) | ||
| 328 | affs_brelse(bmap_bh); | ||
| 329 | bmap_bh = affs_bread(sb, be32_to_cpu(bmap_blk[blk])); | ||
| 330 | if (!bmap_bh) { | ||
| 331 | printk(KERN_ERR "AFFS: Cannot read bitmap extension\n"); | ||
| 332 | res = -EIO; | ||
| 333 | goto out; | ||
| 334 | } | ||
| 335 | bmap_blk = (__be32 *)bmap_bh->b_data; | ||
| 336 | blk = 0; | ||
| 337 | end = sb->s_blocksize / 4 - 1; | ||
| 338 | } | ||
| 339 | |||
| 340 | offset = (sbi->s_partition_size - sbi->s_reserved) % sbi->s_bmap_bits; | ||
| 341 | mask = ~(0xFFFFFFFFU << (offset & 31)); | ||
| 342 | pr_debug("last word: %d %d %d\n", offset, offset / 32 + 1, mask); | ||
| 343 | offset = offset / 32 + 1; | ||
| 344 | |||
| 345 | if (mask) { | ||
| 346 | u32 old, new; | ||
| 347 | |||
| 348 | /* Mark unused bits in the last word as allocated */ | ||
| 349 | old = be32_to_cpu(((__be32 *)bh->b_data)[offset]); | ||
| 350 | new = old & mask; | ||
| 351 | //if (old != new) { | ||
| 352 | ((__be32 *)bh->b_data)[offset] = cpu_to_be32(new); | ||
| 353 | /* fix checksum */ | ||
| 354 | //new -= old; | ||
| 355 | //old = be32_to_cpu(*(__be32 *)bh->b_data); | ||
| 356 | //*(__be32 *)bh->b_data = cpu_to_be32(old - new); | ||
| 357 | //mark_buffer_dirty(bh); | ||
| 358 | //} | ||
| 359 | /* correct offset for the bitmap count below */ | ||
| 360 | //offset++; | ||
| 361 | } | ||
| 362 | while (++offset < sb->s_blocksize / 4) | ||
| 363 | ((__be32 *)bh->b_data)[offset] = 0; | ||
| 364 | ((__be32 *)bh->b_data)[0] = 0; | ||
| 365 | ((__be32 *)bh->b_data)[0] = cpu_to_be32(-affs_checksum_block(sb, bh)); | ||
| 366 | mark_buffer_dirty(bh); | ||
| 367 | |||
| 368 | /* recalculate bitmap count for last block */ | ||
| 369 | bm--; | ||
| 370 | bm->bm_free = affs_count_free_bits(sb->s_blocksize - 4, bh->b_data + 4); | ||
| 371 | |||
| 372 | out: | ||
| 373 | affs_brelse(bh); | ||
| 374 | affs_brelse(bmap_bh); | ||
| 375 | return res; | ||
| 376 | } | ||
| 377 | |||
| 378 | void affs_free_bitmap(struct super_block *sb) | ||
| 379 | { | ||
| 380 | struct affs_sb_info *sbi = AFFS_SB(sb); | ||
| 381 | |||
| 382 | if (!sbi->s_bitmap) | ||
| 383 | return; | ||
| 384 | |||
| 385 | affs_brelse(sbi->s_bmap_bh); | ||
| 386 | sbi->s_bmap_bh = NULL; | ||
| 387 | sbi->s_last_bmap = ~0; | ||
| 388 | kfree(sbi->s_bitmap); | ||
| 389 | sbi->s_bitmap = NULL; | ||
| 390 | } | ||
diff --git a/fs/affs/dir.c b/fs/affs/dir.c new file mode 100644 index 000000000000..548efd0ee98c --- /dev/null +++ b/fs/affs/dir.c | |||
| @@ -0,0 +1,155 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/affs/dir.c | ||
| 3 | * | ||
| 4 | * (c) 1996 Hans-Joachim Widmaier - Rewritten | ||
| 5 | * | ||
| 6 | * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem. | ||
| 7 | * | ||
| 8 | * (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem. | ||
| 9 | * | ||
| 10 | * (C) 1991 Linus Torvalds - minix filesystem | ||
| 11 | * | ||
| 12 | * affs directory handling functions | ||
| 13 | * | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include "affs.h" | ||
| 17 | |||
| 18 | static int affs_readdir(struct file *, void *, filldir_t); | ||
| 19 | |||
| 20 | struct file_operations affs_dir_operations = { | ||
| 21 | .read = generic_read_dir, | ||
| 22 | .readdir = affs_readdir, | ||
| 23 | .fsync = file_fsync, | ||
| 24 | }; | ||
| 25 | |||
| 26 | /* | ||
| 27 | * directories can handle most operations... | ||
| 28 | */ | ||
| 29 | struct inode_operations affs_dir_inode_operations = { | ||
| 30 | .create = affs_create, | ||
| 31 | .lookup = affs_lookup, | ||
| 32 | .link = affs_link, | ||
| 33 | .unlink = affs_unlink, | ||
| 34 | .symlink = affs_symlink, | ||
| 35 | .mkdir = affs_mkdir, | ||
| 36 | .rmdir = affs_rmdir, | ||
| 37 | .rename = affs_rename, | ||
| 38 | .setattr = affs_notify_change, | ||
| 39 | }; | ||
| 40 | |||
| 41 | static int | ||
| 42 | affs_readdir(struct file *filp, void *dirent, filldir_t filldir) | ||
| 43 | { | ||
| 44 | struct inode *inode = filp->f_dentry->d_inode; | ||
| 45 | struct super_block *sb = inode->i_sb; | ||
| 46 | struct buffer_head *dir_bh; | ||
| 47 | struct buffer_head *fh_bh; | ||
| 48 | unsigned char *name; | ||
| 49 | int namelen; | ||
| 50 | u32 i; | ||
| 51 | int hash_pos; | ||
| 52 | int chain_pos; | ||
| 53 | u32 f_pos; | ||
| 54 | u32 ino; | ||
| 55 | int stored; | ||
| 56 | int res; | ||
| 57 | |||
| 58 | pr_debug("AFFS: readdir(ino=%lu,f_pos=%lx)\n",inode->i_ino,(unsigned long)filp->f_pos); | ||
| 59 | |||
| 60 | stored = 0; | ||
| 61 | res = -EIO; | ||
| 62 | dir_bh = NULL; | ||
| 63 | fh_bh = NULL; | ||
| 64 | f_pos = filp->f_pos; | ||
| 65 | |||
| 66 | if (f_pos == 0) { | ||
| 67 | filp->private_data = (void *)0; | ||
| 68 | if (filldir(dirent, ".", 1, f_pos, inode->i_ino, DT_DIR) < 0) | ||
| 69 | return 0; | ||
| 70 | filp->f_pos = f_pos = 1; | ||
| 71 | stored++; | ||
| 72 | } | ||
| 73 | if (f_pos == 1) { | ||
| 74 | if (filldir(dirent, "..", 2, f_pos, parent_ino(filp->f_dentry), DT_DIR) < 0) | ||
| 75 | return stored; | ||
| 76 | filp->f_pos = f_pos = 2; | ||
| 77 | stored++; | ||
| 78 | } | ||
| 79 | |||
| 80 | affs_lock_dir(inode); | ||
| 81 | chain_pos = (f_pos - 2) & 0xffff; | ||
| 82 | hash_pos = (f_pos - 2) >> 16; | ||
| 83 | if (chain_pos == 0xffff) { | ||
| 84 | affs_warning(sb, "readdir", "More than 65535 entries in chain"); | ||
| 85 | chain_pos = 0; | ||
| 86 | hash_pos++; | ||
| 87 | filp->f_pos = ((hash_pos << 16) | chain_pos) + 2; | ||
| 88 | } | ||
| 89 | dir_bh = affs_bread(sb, inode->i_ino); | ||
| 90 | if (!dir_bh) | ||
| 91 | goto readdir_out; | ||
| 92 | |||
| 93 | /* If the directory hasn't changed since the last call to readdir(), | ||
| 94 | * we can jump directly to where we left off. | ||
| 95 | */ | ||
| 96 | ino = (u32)(long)filp->private_data; | ||
| 97 | if (ino && filp->f_version == inode->i_version) { | ||
| 98 | pr_debug("AFFS: readdir() left off=%d\n", ino); | ||
| 99 | goto inside; | ||
| 100 | } | ||
| 101 | |||
| 102 | ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[hash_pos]); | ||
| 103 | for (i = 0; ino && i < chain_pos; i++) { | ||
| 104 | fh_bh = affs_bread(sb, ino); | ||
| 105 | if (!fh_bh) { | ||
| 106 | affs_error(sb, "readdir","Cannot read block %d", i); | ||
| 107 | goto readdir_out; | ||
| 108 | } | ||
| 109 | ino = be32_to_cpu(AFFS_TAIL(sb, fh_bh)->hash_chain); | ||
| 110 | affs_brelse(fh_bh); | ||
| 111 | fh_bh = NULL; | ||
| 112 | } | ||
| 113 | if (ino) | ||
| 114 | goto inside; | ||
| 115 | hash_pos++; | ||
| 116 | |||
| 117 | for (; hash_pos < AFFS_SB(sb)->s_hashsize; hash_pos++) { | ||
| 118 | ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[hash_pos]); | ||
| 119 | if (!ino) | ||
| 120 | continue; | ||
| 121 | f_pos = (hash_pos << 16) + 2; | ||
| 122 | inside: | ||
| 123 | do { | ||
| 124 | fh_bh = affs_bread(sb, ino); | ||
| 125 | if (!fh_bh) { | ||
| 126 | affs_error(sb, "readdir","Cannot read block %d", ino); | ||
| 127 | goto readdir_done; | ||
| 128 | } | ||
| 129 | |||
| 130 | namelen = min(AFFS_TAIL(sb, fh_bh)->name[0], (u8)30); | ||
| 131 | name = AFFS_TAIL(sb, fh_bh)->name + 1; | ||
| 132 | pr_debug("AFFS: readdir(): filldir(\"%.*s\", ino=%u), hash=%d, f_pos=%x\n", | ||
| 133 | namelen, name, ino, hash_pos, f_pos); | ||
| 134 | if (filldir(dirent, name, namelen, f_pos, ino, DT_UNKNOWN) < 0) | ||
| 135 | goto readdir_done; | ||
| 136 | stored++; | ||
| 137 | f_pos++; | ||
| 138 | ino = be32_to_cpu(AFFS_TAIL(sb, fh_bh)->hash_chain); | ||
| 139 | affs_brelse(fh_bh); | ||
| 140 | fh_bh = NULL; | ||
| 141 | } while (ino); | ||
| 142 | } | ||
| 143 | readdir_done: | ||
| 144 | filp->f_pos = f_pos; | ||
| 145 | filp->f_version = inode->i_version; | ||
| 146 | filp->private_data = (void *)(long)ino; | ||
| 147 | res = stored; | ||
| 148 | |||
| 149 | readdir_out: | ||
| 150 | affs_brelse(dir_bh); | ||
| 151 | affs_brelse(fh_bh); | ||
| 152 | affs_unlock_dir(inode); | ||
| 153 | pr_debug("AFFS: readdir()=%d\n", stored); | ||
| 154 | return res; | ||
| 155 | } | ||
diff --git a/fs/affs/file.c b/fs/affs/file.c new file mode 100644 index 000000000000..6744924b6905 --- /dev/null +++ b/fs/affs/file.c | |||
| @@ -0,0 +1,920 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/affs/file.c | ||
| 3 | * | ||
| 4 | * (c) 1996 Hans-Joachim Widmaier - Rewritten | ||
| 5 | * | ||
| 6 | * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem. | ||
| 7 | * | ||
| 8 | * (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem. | ||
| 9 | * | ||
| 10 | * (C) 1991 Linus Torvalds - minix filesystem | ||
| 11 | * | ||
| 12 | * affs regular file handling primitives | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include "affs.h" | ||
| 16 | |||
| 17 | #if PAGE_SIZE < 4096 | ||
| 18 | #error PAGE_SIZE must be at least 4096 | ||
| 19 | #endif | ||
| 20 | |||
| 21 | static int affs_grow_extcache(struct inode *inode, u32 lc_idx); | ||
| 22 | static struct buffer_head *affs_alloc_extblock(struct inode *inode, struct buffer_head *bh, u32 ext); | ||
| 23 | static inline struct buffer_head *affs_get_extblock(struct inode *inode, u32 ext); | ||
| 24 | static struct buffer_head *affs_get_extblock_slow(struct inode *inode, u32 ext); | ||
| 25 | static ssize_t affs_file_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos); | ||
| 26 | static int affs_file_open(struct inode *inode, struct file *filp); | ||
| 27 | static int affs_file_release(struct inode *inode, struct file *filp); | ||
| 28 | |||
| 29 | struct file_operations affs_file_operations = { | ||
| 30 | .llseek = generic_file_llseek, | ||
| 31 | .read = generic_file_read, | ||
| 32 | .write = affs_file_write, | ||
| 33 | .mmap = generic_file_mmap, | ||
| 34 | .open = affs_file_open, | ||
| 35 | .release = affs_file_release, | ||
| 36 | .fsync = file_fsync, | ||
| 37 | .sendfile = generic_file_sendfile, | ||
| 38 | }; | ||
| 39 | |||
| 40 | struct inode_operations affs_file_inode_operations = { | ||
| 41 | .truncate = affs_truncate, | ||
| 42 | .setattr = affs_notify_change, | ||
| 43 | }; | ||
| 44 | |||
| 45 | static int | ||
| 46 | affs_file_open(struct inode *inode, struct file *filp) | ||
| 47 | { | ||
| 48 | if (atomic_read(&filp->f_count) != 1) | ||
| 49 | return 0; | ||
| 50 | pr_debug("AFFS: open(%d)\n", AFFS_I(inode)->i_opencnt); | ||
| 51 | AFFS_I(inode)->i_opencnt++; | ||
| 52 | return 0; | ||
| 53 | } | ||
| 54 | |||
| 55 | static int | ||
| 56 | affs_file_release(struct inode *inode, struct file *filp) | ||
| 57 | { | ||
| 58 | if (atomic_read(&filp->f_count) != 0) | ||
| 59 | return 0; | ||
| 60 | pr_debug("AFFS: release(%d)\n", AFFS_I(inode)->i_opencnt); | ||
| 61 | AFFS_I(inode)->i_opencnt--; | ||
| 62 | if (!AFFS_I(inode)->i_opencnt) | ||
| 63 | affs_free_prealloc(inode); | ||
| 64 | |||
| 65 | return 0; | ||
| 66 | } | ||
| 67 | |||
| 68 | static int | ||
| 69 | affs_grow_extcache(struct inode *inode, u32 lc_idx) | ||
| 70 | { | ||
| 71 | struct super_block *sb = inode->i_sb; | ||
| 72 | struct buffer_head *bh; | ||
| 73 | u32 lc_max; | ||
| 74 | int i, j, key; | ||
| 75 | |||
| 76 | if (!AFFS_I(inode)->i_lc) { | ||
| 77 | char *ptr = (char *)get_zeroed_page(GFP_NOFS); | ||
| 78 | if (!ptr) | ||
| 79 | return -ENOMEM; | ||
| 80 | AFFS_I(inode)->i_lc = (u32 *)ptr; | ||
| 81 | AFFS_I(inode)->i_ac = (struct affs_ext_key *)(ptr + AFFS_CACHE_SIZE / 2); | ||
| 82 | } | ||
| 83 | |||
| 84 | lc_max = AFFS_LC_SIZE << AFFS_I(inode)->i_lc_shift; | ||
| 85 | |||
| 86 | if (AFFS_I(inode)->i_extcnt > lc_max) { | ||
| 87 | u32 lc_shift, lc_mask, tmp, off; | ||
| 88 | |||
| 89 | /* need to recalculate linear cache, start from old size */ | ||
| 90 | lc_shift = AFFS_I(inode)->i_lc_shift; | ||
| 91 | tmp = (AFFS_I(inode)->i_extcnt / AFFS_LC_SIZE) >> lc_shift; | ||
| 92 | for (; tmp; tmp >>= 1) | ||
| 93 | lc_shift++; | ||
| 94 | lc_mask = (1 << lc_shift) - 1; | ||
| 95 | |||
| 96 | /* fix idx and old size to new shift */ | ||
| 97 | lc_idx >>= (lc_shift - AFFS_I(inode)->i_lc_shift); | ||
| 98 | AFFS_I(inode)->i_lc_size >>= (lc_shift - AFFS_I(inode)->i_lc_shift); | ||
| 99 | |||
| 100 | /* first shrink old cache to make more space */ | ||
| 101 | off = 1 << (lc_shift - AFFS_I(inode)->i_lc_shift); | ||
| 102 | for (i = 1, j = off; j < AFFS_LC_SIZE; i++, j += off) | ||
| 103 | AFFS_I(inode)->i_ac[i] = AFFS_I(inode)->i_ac[j]; | ||
| 104 | |||
| 105 | AFFS_I(inode)->i_lc_shift = lc_shift; | ||
| 106 | AFFS_I(inode)->i_lc_mask = lc_mask; | ||
| 107 | } | ||
| 108 | |||
| 109 | /* fill cache to the needed index */ | ||
| 110 | i = AFFS_I(inode)->i_lc_size; | ||
| 111 | AFFS_I(inode)->i_lc_size = lc_idx + 1; | ||
| 112 | for (; i <= lc_idx; i++) { | ||
| 113 | if (!i) { | ||
| 114 | AFFS_I(inode)->i_lc[0] = inode->i_ino; | ||
| 115 | continue; | ||
| 116 | } | ||
| 117 | key = AFFS_I(inode)->i_lc[i - 1]; | ||
| 118 | j = AFFS_I(inode)->i_lc_mask + 1; | ||
| 119 | // unlock cache | ||
| 120 | for (; j > 0; j--) { | ||
| 121 | bh = affs_bread(sb, key); | ||
| 122 | if (!bh) | ||
| 123 | goto err; | ||
| 124 | key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension); | ||
| 125 | affs_brelse(bh); | ||
| 126 | } | ||
| 127 | // lock cache | ||
| 128 | AFFS_I(inode)->i_lc[i] = key; | ||
| 129 | } | ||
| 130 | |||
| 131 | return 0; | ||
| 132 | |||
| 133 | err: | ||
| 134 | // lock cache | ||
| 135 | return -EIO; | ||
| 136 | } | ||
| 137 | |||
| 138 | static struct buffer_head * | ||
| 139 | affs_alloc_extblock(struct inode *inode, struct buffer_head *bh, u32 ext) | ||
| 140 | { | ||
| 141 | struct super_block *sb = inode->i_sb; | ||
| 142 | struct buffer_head *new_bh; | ||
| 143 | u32 blocknr, tmp; | ||
| 144 | |||
| 145 | blocknr = affs_alloc_block(inode, bh->b_blocknr); | ||
| 146 | if (!blocknr) | ||
| 147 | return ERR_PTR(-ENOSPC); | ||
| 148 | |||
| 149 | new_bh = affs_getzeroblk(sb, blocknr); | ||
| 150 | if (!new_bh) { | ||
| 151 | affs_free_block(sb, blocknr); | ||
| 152 | return ERR_PTR(-EIO); | ||
| 153 | } | ||
| 154 | |||
| 155 | AFFS_HEAD(new_bh)->ptype = cpu_to_be32(T_LIST); | ||
| 156 | AFFS_HEAD(new_bh)->key = cpu_to_be32(blocknr); | ||
| 157 | AFFS_TAIL(sb, new_bh)->stype = cpu_to_be32(ST_FILE); | ||
| 158 | AFFS_TAIL(sb, new_bh)->parent = cpu_to_be32(inode->i_ino); | ||
| 159 | affs_fix_checksum(sb, new_bh); | ||
| 160 | |||
| 161 | mark_buffer_dirty_inode(new_bh, inode); | ||
| 162 | |||
| 163 | tmp = be32_to_cpu(AFFS_TAIL(sb, bh)->extension); | ||
| 164 | if (tmp) | ||
| 165 | affs_warning(sb, "alloc_ext", "previous extension set (%x)", tmp); | ||
| 166 | AFFS_TAIL(sb, bh)->extension = cpu_to_be32(blocknr); | ||
| 167 | affs_adjust_checksum(bh, blocknr - tmp); | ||
| 168 | mark_buffer_dirty_inode(bh, inode); | ||
| 169 | |||
| 170 | AFFS_I(inode)->i_extcnt++; | ||
| 171 | mark_inode_dirty(inode); | ||
| 172 | |||
| 173 | return new_bh; | ||
| 174 | } | ||
| 175 | |||
| 176 | static inline struct buffer_head * | ||
| 177 | affs_get_extblock(struct inode *inode, u32 ext) | ||
| 178 | { | ||
| 179 | /* inline the simplest case: same extended block as last time */ | ||
| 180 | struct buffer_head *bh = AFFS_I(inode)->i_ext_bh; | ||
| 181 | if (ext == AFFS_I(inode)->i_ext_last) | ||
| 182 | atomic_inc(&bh->b_count); | ||
| 183 | else | ||
| 184 | /* we have to do more (not inlined) */ | ||
| 185 | bh = affs_get_extblock_slow(inode, ext); | ||
| 186 | |||
| 187 | return bh; | ||
| 188 | } | ||
| 189 | |||
| 190 | static struct buffer_head * | ||
| 191 | affs_get_extblock_slow(struct inode *inode, u32 ext) | ||
| 192 | { | ||
| 193 | struct super_block *sb = inode->i_sb; | ||
| 194 | struct buffer_head *bh; | ||
| 195 | u32 ext_key; | ||
| 196 | u32 lc_idx, lc_off, ac_idx; | ||
| 197 | u32 tmp, idx; | ||
| 198 | |||
| 199 | if (ext == AFFS_I(inode)->i_ext_last + 1) { | ||
| 200 | /* read the next extended block from the current one */ | ||
| 201 | bh = AFFS_I(inode)->i_ext_bh; | ||
| 202 | ext_key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension); | ||
| 203 | if (ext < AFFS_I(inode)->i_extcnt) | ||
| 204 | goto read_ext; | ||
| 205 | if (ext > AFFS_I(inode)->i_extcnt) | ||
| 206 | BUG(); | ||
| 207 | bh = affs_alloc_extblock(inode, bh, ext); | ||
| 208 | if (IS_ERR(bh)) | ||
| 209 | return bh; | ||
| 210 | goto store_ext; | ||
| 211 | } | ||
| 212 | |||
| 213 | if (ext == 0) { | ||
| 214 | /* we seek back to the file header block */ | ||
| 215 | ext_key = inode->i_ino; | ||
| 216 | goto read_ext; | ||
| 217 | } | ||
| 218 | |||
| 219 | if (ext >= AFFS_I(inode)->i_extcnt) { | ||
| 220 | struct buffer_head *prev_bh; | ||
| 221 | |||
| 222 | /* allocate a new extended block */ | ||
| 223 | if (ext > AFFS_I(inode)->i_extcnt) | ||
| 224 | BUG(); | ||
| 225 | |||
| 226 | /* get previous extended block */ | ||
| 227 | prev_bh = affs_get_extblock(inode, ext - 1); | ||
| 228 | if (IS_ERR(prev_bh)) | ||
| 229 | return prev_bh; | ||
| 230 | bh = affs_alloc_extblock(inode, prev_bh, ext); | ||
| 231 | affs_brelse(prev_bh); | ||
| 232 | if (IS_ERR(bh)) | ||
| 233 | return bh; | ||
| 234 | goto store_ext; | ||
| 235 | } | ||
| 236 | |||
| 237 | again: | ||
| 238 | /* check if there is an extended cache and whether it's large enough */ | ||
| 239 | lc_idx = ext >> AFFS_I(inode)->i_lc_shift; | ||
| 240 | lc_off = ext & AFFS_I(inode)->i_lc_mask; | ||
| 241 | |||
| 242 | if (lc_idx >= AFFS_I(inode)->i_lc_size) { | ||
| 243 | int err; | ||
| 244 | |||
| 245 | err = affs_grow_extcache(inode, lc_idx); | ||
| 246 | if (err) | ||
| 247 | return ERR_PTR(err); | ||
| 248 | goto again; | ||
| 249 | } | ||
| 250 | |||
| 251 | /* every n'th key we find in the linear cache */ | ||
| 252 | if (!lc_off) { | ||
| 253 | ext_key = AFFS_I(inode)->i_lc[lc_idx]; | ||
| 254 | goto read_ext; | ||
| 255 | } | ||
| 256 | |||
| 257 | /* maybe it's still in the associative cache */ | ||
| 258 | ac_idx = (ext - lc_idx - 1) & AFFS_AC_MASK; | ||
| 259 | if (AFFS_I(inode)->i_ac[ac_idx].ext == ext) { | ||
| 260 | ext_key = AFFS_I(inode)->i_ac[ac_idx].key; | ||
| 261 | goto read_ext; | ||
| 262 | } | ||
| 263 | |||
| 264 | /* try to find one of the previous extended blocks */ | ||
| 265 | tmp = ext; | ||
| 266 | idx = ac_idx; | ||
| 267 | while (--tmp, --lc_off > 0) { | ||
| 268 | idx = (idx - 1) & AFFS_AC_MASK; | ||
| 269 | if (AFFS_I(inode)->i_ac[idx].ext == tmp) { | ||
| 270 | ext_key = AFFS_I(inode)->i_ac[idx].key; | ||
| 271 | goto find_ext; | ||
| 272 | } | ||
| 273 | } | ||
| 274 | |||
| 275 | /* fall back to the linear cache */ | ||
| 276 | ext_key = AFFS_I(inode)->i_lc[lc_idx]; | ||
| 277 | find_ext: | ||
| 278 | /* read all extended blocks until we find the one we need */ | ||
| 279 | //unlock cache | ||
| 280 | do { | ||
| 281 | bh = affs_bread(sb, ext_key); | ||
| 282 | if (!bh) | ||
| 283 | goto err_bread; | ||
| 284 | ext_key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension); | ||
| 285 | affs_brelse(bh); | ||
| 286 | tmp++; | ||
| 287 | } while (tmp < ext); | ||
| 288 | //lock cache | ||
| 289 | |||
| 290 | /* store it in the associative cache */ | ||
| 291 | // recalculate ac_idx? | ||
| 292 | AFFS_I(inode)->i_ac[ac_idx].ext = ext; | ||
| 293 | AFFS_I(inode)->i_ac[ac_idx].key = ext_key; | ||
| 294 | |||
| 295 | read_ext: | ||
| 296 | /* finally read the right extended block */ | ||
| 297 | //unlock cache | ||
| 298 | bh = affs_bread(sb, ext_key); | ||
| 299 | if (!bh) | ||
| 300 | goto err_bread; | ||
| 301 | //lock cache | ||
| 302 | |||
| 303 | store_ext: | ||
| 304 | /* release old cached extended block and store the new one */ | ||
| 305 | affs_brelse(AFFS_I(inode)->i_ext_bh); | ||
| 306 | AFFS_I(inode)->i_ext_last = ext; | ||
| 307 | AFFS_I(inode)->i_ext_bh = bh; | ||
| 308 | atomic_inc(&bh->b_count); | ||
| 309 | |||
| 310 | return bh; | ||
| 311 | |||
| 312 | err_bread: | ||
| 313 | affs_brelse(bh); | ||
| 314 | return ERR_PTR(-EIO); | ||
| 315 | } | ||
| 316 | |||
| 317 | static int | ||
| 318 | affs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_result, int create) | ||
| 319 | { | ||
| 320 | struct super_block *sb = inode->i_sb; | ||
| 321 | struct buffer_head *ext_bh; | ||
| 322 | u32 ext; | ||
| 323 | |||
| 324 | pr_debug("AFFS: get_block(%u, %lu)\n", (u32)inode->i_ino, (unsigned long)block); | ||
| 325 | |||
| 326 | |||
| 327 | if (block > (sector_t)0x7fffffffUL) | ||
| 328 | BUG(); | ||
| 329 | |||
| 330 | if (block >= AFFS_I(inode)->i_blkcnt) { | ||
| 331 | if (block > AFFS_I(inode)->i_blkcnt || !create) | ||
| 332 | goto err_big; | ||
| 333 | } else | ||
| 334 | create = 0; | ||
| 335 | |||
| 336 | //lock cache | ||
| 337 | affs_lock_ext(inode); | ||
| 338 | |||
| 339 | ext = (u32)block / AFFS_SB(sb)->s_hashsize; | ||
| 340 | block -= ext * AFFS_SB(sb)->s_hashsize; | ||
| 341 | ext_bh = affs_get_extblock(inode, ext); | ||
| 342 | if (IS_ERR(ext_bh)) | ||
| 343 | goto err_ext; | ||
| 344 | map_bh(bh_result, sb, (sector_t)be32_to_cpu(AFFS_BLOCK(sb, ext_bh, block))); | ||
| 345 | |||
| 346 | if (create) { | ||
| 347 | u32 blocknr = affs_alloc_block(inode, ext_bh->b_blocknr); | ||
| 348 | if (!blocknr) | ||
| 349 | goto err_alloc; | ||
| 350 | set_buffer_new(bh_result); | ||
| 351 | AFFS_I(inode)->mmu_private += AFFS_SB(sb)->s_data_blksize; | ||
| 352 | AFFS_I(inode)->i_blkcnt++; | ||
| 353 | |||
| 354 | /* store new block */ | ||
| 355 | if (bh_result->b_blocknr) | ||
| 356 | affs_warning(sb, "get_block", "block already set (%x)", bh_result->b_blocknr); | ||
| 357 | AFFS_BLOCK(sb, ext_bh, block) = cpu_to_be32(blocknr); | ||
| 358 | AFFS_HEAD(ext_bh)->block_count = cpu_to_be32(block + 1); | ||
| 359 | affs_adjust_checksum(ext_bh, blocknr - bh_result->b_blocknr + 1); | ||
| 360 | bh_result->b_blocknr = blocknr; | ||
| 361 | |||
| 362 | if (!block) { | ||
| 363 | /* insert first block into header block */ | ||
| 364 | u32 tmp = be32_to_cpu(AFFS_HEAD(ext_bh)->first_data); | ||
| 365 | if (tmp) | ||
| 366 | affs_warning(sb, "get_block", "first block already set (%d)", tmp); | ||
| 367 | AFFS_HEAD(ext_bh)->first_data = cpu_to_be32(blocknr); | ||
| 368 | affs_adjust_checksum(ext_bh, blocknr - tmp); | ||
| 369 | } | ||
| 370 | } | ||
| 371 | |||
| 372 | affs_brelse(ext_bh); | ||
| 373 | //unlock cache | ||
| 374 | affs_unlock_ext(inode); | ||
| 375 | return 0; | ||
| 376 | |||
| 377 | err_big: | ||
| 378 | affs_error(inode->i_sb,"get_block","strange block request %d", block); | ||
| 379 | return -EIO; | ||
| 380 | err_ext: | ||
| 381 | // unlock cache | ||
| 382 | affs_unlock_ext(inode); | ||
| 383 | return PTR_ERR(ext_bh); | ||
| 384 | err_alloc: | ||
| 385 | brelse(ext_bh); | ||
| 386 | clear_buffer_mapped(bh_result); | ||
| 387 | bh_result->b_bdev = NULL; | ||
| 388 | // unlock cache | ||
| 389 | affs_unlock_ext(inode); | ||
| 390 | return -ENOSPC; | ||
| 391 | } | ||
| 392 | |||
| 393 | static int affs_writepage(struct page *page, struct writeback_control *wbc) | ||
| 394 | { | ||
| 395 | return block_write_full_page(page, affs_get_block, wbc); | ||
| 396 | } | ||
| 397 | static int affs_readpage(struct file *file, struct page *page) | ||
| 398 | { | ||
| 399 | return block_read_full_page(page, affs_get_block); | ||
| 400 | } | ||
| 401 | static int affs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) | ||
| 402 | { | ||
| 403 | return cont_prepare_write(page, from, to, affs_get_block, | ||
| 404 | &AFFS_I(page->mapping->host)->mmu_private); | ||
| 405 | } | ||
| 406 | static sector_t _affs_bmap(struct address_space *mapping, sector_t block) | ||
| 407 | { | ||
| 408 | return generic_block_bmap(mapping,block,affs_get_block); | ||
| 409 | } | ||
| 410 | struct address_space_operations affs_aops = { | ||
| 411 | .readpage = affs_readpage, | ||
| 412 | .writepage = affs_writepage, | ||
| 413 | .sync_page = block_sync_page, | ||
| 414 | .prepare_write = affs_prepare_write, | ||
| 415 | .commit_write = generic_commit_write, | ||
| 416 | .bmap = _affs_bmap | ||
| 417 | }; | ||
| 418 | |||
| 419 | static inline struct buffer_head * | ||
| 420 | affs_bread_ino(struct inode *inode, int block, int create) | ||
| 421 | { | ||
| 422 | struct buffer_head *bh, tmp_bh; | ||
| 423 | int err; | ||
| 424 | |||
| 425 | tmp_bh.b_state = 0; | ||
| 426 | err = affs_get_block(inode, block, &tmp_bh, create); | ||
| 427 | if (!err) { | ||
| 428 | bh = affs_bread(inode->i_sb, tmp_bh.b_blocknr); | ||
| 429 | if (bh) { | ||
| 430 | bh->b_state |= tmp_bh.b_state; | ||
| 431 | return bh; | ||
| 432 | } | ||
| 433 | err = -EIO; | ||
| 434 | } | ||
| 435 | return ERR_PTR(err); | ||
| 436 | } | ||
| 437 | |||
| 438 | static inline struct buffer_head * | ||
| 439 | affs_getzeroblk_ino(struct inode *inode, int block) | ||
| 440 | { | ||
| 441 | struct buffer_head *bh, tmp_bh; | ||
| 442 | int err; | ||
| 443 | |||
| 444 | tmp_bh.b_state = 0; | ||
| 445 | err = affs_get_block(inode, block, &tmp_bh, 1); | ||
| 446 | if (!err) { | ||
| 447 | bh = affs_getzeroblk(inode->i_sb, tmp_bh.b_blocknr); | ||
| 448 | if (bh) { | ||
| 449 | bh->b_state |= tmp_bh.b_state; | ||
| 450 | return bh; | ||
| 451 | } | ||
| 452 | err = -EIO; | ||
| 453 | } | ||
| 454 | return ERR_PTR(err); | ||
| 455 | } | ||
| 456 | |||
| 457 | static inline struct buffer_head * | ||
| 458 | affs_getemptyblk_ino(struct inode *inode, int block) | ||
| 459 | { | ||
| 460 | struct buffer_head *bh, tmp_bh; | ||
| 461 | int err; | ||
| 462 | |||
| 463 | tmp_bh.b_state = 0; | ||
| 464 | err = affs_get_block(inode, block, &tmp_bh, 1); | ||
| 465 | if (!err) { | ||
| 466 | bh = affs_getemptyblk(inode->i_sb, tmp_bh.b_blocknr); | ||
| 467 | if (bh) { | ||
| 468 | bh->b_state |= tmp_bh.b_state; | ||
| 469 | return bh; | ||
| 470 | } | ||
| 471 | err = -EIO; | ||
| 472 | } | ||
| 473 | return ERR_PTR(err); | ||
| 474 | } | ||
| 475 | |||
| 476 | static ssize_t | ||
| 477 | affs_file_write(struct file *file, const char __user *buf, | ||
| 478 | size_t count, loff_t *ppos) | ||
| 479 | { | ||
| 480 | ssize_t retval; | ||
| 481 | |||
| 482 | retval = generic_file_write (file, buf, count, ppos); | ||
| 483 | if (retval >0) { | ||
| 484 | struct inode *inode = file->f_dentry->d_inode; | ||
| 485 | inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; | ||
| 486 | mark_inode_dirty(inode); | ||
| 487 | } | ||
| 488 | return retval; | ||
| 489 | } | ||
| 490 | |||
| 491 | static int | ||
| 492 | affs_do_readpage_ofs(struct file *file, struct page *page, unsigned from, unsigned to) | ||
| 493 | { | ||
| 494 | struct inode *inode = page->mapping->host; | ||
| 495 | struct super_block *sb = inode->i_sb; | ||
| 496 | struct buffer_head *bh; | ||
| 497 | char *data; | ||
| 498 | u32 bidx, boff, bsize; | ||
| 499 | u32 tmp; | ||
| 500 | |||
| 501 | pr_debug("AFFS: read_page(%u, %ld, %d, %d)\n", (u32)inode->i_ino, page->index, from, to); | ||
| 502 | if (from > to || to > PAGE_CACHE_SIZE) | ||
| 503 | BUG(); | ||
| 504 | kmap(page); | ||
| 505 | data = page_address(page); | ||
| 506 | bsize = AFFS_SB(sb)->s_data_blksize; | ||
| 507 | tmp = (page->index << PAGE_CACHE_SHIFT) + from; | ||
| 508 | bidx = tmp / bsize; | ||
| 509 | boff = tmp % bsize; | ||
| 510 | |||
| 511 | while (from < to) { | ||
| 512 | bh = affs_bread_ino(inode, bidx, 0); | ||
| 513 | if (IS_ERR(bh)) | ||
| 514 | return PTR_ERR(bh); | ||
| 515 | tmp = min(bsize - boff, to - from); | ||
| 516 | if (from + tmp > to || tmp > bsize) | ||
| 517 | BUG(); | ||
| 518 | memcpy(data + from, AFFS_DATA(bh) + boff, tmp); | ||
| 519 | affs_brelse(bh); | ||
| 520 | bidx++; | ||
| 521 | from += tmp; | ||
| 522 | boff = 0; | ||
| 523 | } | ||
| 524 | flush_dcache_page(page); | ||
| 525 | kunmap(page); | ||
| 526 | return 0; | ||
| 527 | } | ||
| 528 | |||
| 529 | static int | ||
| 530 | affs_extent_file_ofs(struct inode *inode, u32 newsize) | ||
| 531 | { | ||
| 532 | struct super_block *sb = inode->i_sb; | ||
| 533 | struct buffer_head *bh, *prev_bh; | ||
| 534 | u32 bidx, boff; | ||
| 535 | u32 size, bsize; | ||
| 536 | u32 tmp; | ||
| 537 | |||
| 538 | pr_debug("AFFS: extent_file(%u, %d)\n", (u32)inode->i_ino, newsize); | ||
| 539 | bsize = AFFS_SB(sb)->s_data_blksize; | ||
| 540 | bh = NULL; | ||
| 541 | size = AFFS_I(inode)->mmu_private; | ||
| 542 | bidx = size / bsize; | ||
| 543 | boff = size % bsize; | ||
| 544 | if (boff) { | ||
| 545 | bh = affs_bread_ino(inode, bidx, 0); | ||
| 546 | if (IS_ERR(bh)) | ||
| 547 | return PTR_ERR(bh); | ||
| 548 | tmp = min(bsize - boff, newsize - size); | ||
| 549 | if (boff + tmp > bsize || tmp > bsize) | ||
| 550 | BUG(); | ||
| 551 | memset(AFFS_DATA(bh) + boff, 0, tmp); | ||
| 552 | AFFS_DATA_HEAD(bh)->size = cpu_to_be32(be32_to_cpu(AFFS_DATA_HEAD(bh)->size) + tmp); | ||
| 553 | affs_fix_checksum(sb, bh); | ||
| 554 | mark_buffer_dirty_inode(bh, inode); | ||
| 555 | size += tmp; | ||
| 556 | bidx++; | ||
| 557 | } else if (bidx) { | ||
| 558 | bh = affs_bread_ino(inode, bidx - 1, 0); | ||
| 559 | if (IS_ERR(bh)) | ||
| 560 | return PTR_ERR(bh); | ||
| 561 | } | ||
| 562 | |||
| 563 | while (size < newsize) { | ||
| 564 | prev_bh = bh; | ||
| 565 | bh = affs_getzeroblk_ino(inode, bidx); | ||
| 566 | if (IS_ERR(bh)) | ||
| 567 | goto out; | ||
| 568 | tmp = min(bsize, newsize - size); | ||
| 569 | if (tmp > bsize) | ||
| 570 | BUG(); | ||
| 571 | AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA); | ||
| 572 | AFFS_DATA_HEAD(bh)->key = cpu_to_be32(inode->i_ino); | ||
| 573 | AFFS_DATA_HEAD(bh)->sequence = cpu_to_be32(bidx); | ||
| 574 | AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp); | ||
| 575 | affs_fix_checksum(sb, bh); | ||
| 576 | bh->b_state &= ~(1UL << BH_New); | ||
| 577 | mark_buffer_dirty_inode(bh, inode); | ||
| 578 | if (prev_bh) { | ||
| 579 | u32 tmp = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next); | ||
| 580 | if (tmp) | ||
| 581 | affs_warning(sb, "extent_file_ofs", "next block already set for %d (%d)", bidx, tmp); | ||
| 582 | AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr); | ||
| 583 | affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp); | ||
| 584 | mark_buffer_dirty_inode(prev_bh, inode); | ||
| 585 | affs_brelse(prev_bh); | ||
| 586 | } | ||
| 587 | size += bsize; | ||
| 588 | bidx++; | ||
| 589 | } | ||
| 590 | affs_brelse(bh); | ||
| 591 | inode->i_size = AFFS_I(inode)->mmu_private = newsize; | ||
| 592 | return 0; | ||
| 593 | |||
| 594 | out: | ||
| 595 | inode->i_size = AFFS_I(inode)->mmu_private = newsize; | ||
| 596 | return PTR_ERR(bh); | ||
| 597 | } | ||
| 598 | |||
| 599 | static int | ||
| 600 | affs_readpage_ofs(struct file *file, struct page *page) | ||
| 601 | { | ||
| 602 | struct inode *inode = page->mapping->host; | ||
| 603 | u32 to; | ||
| 604 | int err; | ||
| 605 | |||
| 606 | pr_debug("AFFS: read_page(%u, %ld)\n", (u32)inode->i_ino, page->index); | ||
| 607 | to = PAGE_CACHE_SIZE; | ||
| 608 | if (((page->index + 1) << PAGE_CACHE_SHIFT) > inode->i_size) { | ||
| 609 | to = inode->i_size & ~PAGE_CACHE_MASK; | ||
| 610 | memset(page_address(page) + to, 0, PAGE_CACHE_SIZE - to); | ||
| 611 | } | ||
| 612 | |||
| 613 | err = affs_do_readpage_ofs(file, page, 0, to); | ||
| 614 | if (!err) | ||
| 615 | SetPageUptodate(page); | ||
| 616 | unlock_page(page); | ||
| 617 | return err; | ||
| 618 | } | ||
| 619 | |||
| 620 | static int affs_prepare_write_ofs(struct file *file, struct page *page, unsigned from, unsigned to) | ||
| 621 | { | ||
| 622 | struct inode *inode = page->mapping->host; | ||
| 623 | u32 size, offset; | ||
| 624 | u32 tmp; | ||
| 625 | int err = 0; | ||
| 626 | |||
| 627 | pr_debug("AFFS: prepare_write(%u, %ld, %d, %d)\n", (u32)inode->i_ino, page->index, from, to); | ||
| 628 | offset = page->index << PAGE_CACHE_SHIFT; | ||
| 629 | if (offset + from > AFFS_I(inode)->mmu_private) { | ||
| 630 | err = affs_extent_file_ofs(inode, offset + from); | ||
| 631 | if (err) | ||
| 632 | return err; | ||
| 633 | } | ||
| 634 | size = inode->i_size; | ||
| 635 | |||
| 636 | if (PageUptodate(page)) | ||
| 637 | return 0; | ||
| 638 | |||
| 639 | if (from) { | ||
| 640 | err = affs_do_readpage_ofs(file, page, 0, from); | ||
| 641 | if (err) | ||
| 642 | return err; | ||
| 643 | } | ||
| 644 | if (to < PAGE_CACHE_SIZE) { | ||
| 645 | char *kaddr = kmap_atomic(page, KM_USER0); | ||
| 646 | |||
| 647 | memset(kaddr + to, 0, PAGE_CACHE_SIZE - to); | ||
| 648 | flush_dcache_page(page); | ||
| 649 | kunmap_atomic(kaddr, KM_USER0); | ||
| 650 | if (size > offset + to) { | ||
| 651 | if (size < offset + PAGE_CACHE_SIZE) | ||
| 652 | tmp = size & ~PAGE_CACHE_MASK; | ||
| 653 | else | ||
| 654 | tmp = PAGE_CACHE_SIZE; | ||
| 655 | err = affs_do_readpage_ofs(file, page, to, tmp); | ||
| 656 | } | ||
| 657 | } | ||
| 658 | return err; | ||
| 659 | } | ||
| 660 | |||
| 661 | static int affs_commit_write_ofs(struct file *file, struct page *page, unsigned from, unsigned to) | ||
| 662 | { | ||
| 663 | struct inode *inode = page->mapping->host; | ||
| 664 | struct super_block *sb = inode->i_sb; | ||
| 665 | struct buffer_head *bh, *prev_bh; | ||
| 666 | char *data; | ||
| 667 | u32 bidx, boff, bsize; | ||
| 668 | u32 tmp; | ||
| 669 | int written; | ||
| 670 | |||
| 671 | pr_debug("AFFS: commit_write(%u, %ld, %d, %d)\n", (u32)inode->i_ino, page->index, from, to); | ||
| 672 | bsize = AFFS_SB(sb)->s_data_blksize; | ||
| 673 | data = page_address(page); | ||
| 674 | |||
| 675 | bh = NULL; | ||
| 676 | written = 0; | ||
| 677 | tmp = (page->index << PAGE_CACHE_SHIFT) + from; | ||
| 678 | bidx = tmp / bsize; | ||
| 679 | boff = tmp % bsize; | ||
| 680 | if (boff) { | ||
| 681 | bh = affs_bread_ino(inode, bidx, 0); | ||
| 682 | if (IS_ERR(bh)) | ||
| 683 | return PTR_ERR(bh); | ||
| 684 | tmp = min(bsize - boff, to - from); | ||
| 685 | if (boff + tmp > bsize || tmp > bsize) | ||
| 686 | BUG(); | ||
| 687 | memcpy(AFFS_DATA(bh) + boff, data + from, tmp); | ||
| 688 | AFFS_DATA_HEAD(bh)->size = cpu_to_be32(be32_to_cpu(AFFS_DATA_HEAD(bh)->size) + tmp); | ||
| 689 | affs_fix_checksum(sb, bh); | ||
| 690 | mark_buffer_dirty_inode(bh, inode); | ||
| 691 | written += tmp; | ||
| 692 | from += tmp; | ||
| 693 | bidx++; | ||
| 694 | } else if (bidx) { | ||
| 695 | bh = affs_bread_ino(inode, bidx - 1, 0); | ||
| 696 | if (IS_ERR(bh)) | ||
| 697 | return PTR_ERR(bh); | ||
| 698 | } | ||
| 699 | while (from + bsize <= to) { | ||
| 700 | prev_bh = bh; | ||
| 701 | bh = affs_getemptyblk_ino(inode, bidx); | ||
| 702 | if (IS_ERR(bh)) | ||
| 703 | goto out; | ||
| 704 | memcpy(AFFS_DATA(bh), data + from, bsize); | ||
| 705 | if (buffer_new(bh)) { | ||
| 706 | AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA); | ||
| 707 | AFFS_DATA_HEAD(bh)->key = cpu_to_be32(inode->i_ino); | ||
| 708 | AFFS_DATA_HEAD(bh)->sequence = cpu_to_be32(bidx); | ||
| 709 | AFFS_DATA_HEAD(bh)->size = cpu_to_be32(bsize); | ||
| 710 | AFFS_DATA_HEAD(bh)->next = 0; | ||
| 711 | bh->b_state &= ~(1UL << BH_New); | ||
| 712 | if (prev_bh) { | ||
| 713 | u32 tmp = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next); | ||
| 714 | if (tmp) | ||
| 715 | affs_warning(sb, "commit_write_ofs", "next block already set for %d (%d)", bidx, tmp); | ||
| 716 | AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr); | ||
| 717 | affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp); | ||
| 718 | mark_buffer_dirty_inode(prev_bh, inode); | ||
| 719 | } | ||
| 720 | } | ||
| 721 | affs_brelse(prev_bh); | ||
| 722 | affs_fix_checksum(sb, bh); | ||
| 723 | mark_buffer_dirty_inode(bh, inode); | ||
| 724 | written += bsize; | ||
| 725 | from += bsize; | ||
| 726 | bidx++; | ||
| 727 | } | ||
| 728 | if (from < to) { | ||
| 729 | prev_bh = bh; | ||
| 730 | bh = affs_bread_ino(inode, bidx, 1); | ||
| 731 | if (IS_ERR(bh)) | ||
| 732 | goto out; | ||
| 733 | tmp = min(bsize, to - from); | ||
| 734 | if (tmp > bsize) | ||
| 735 | BUG(); | ||
| 736 | memcpy(AFFS_DATA(bh), data + from, tmp); | ||
| 737 | if (buffer_new(bh)) { | ||
| 738 | AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA); | ||
| 739 | AFFS_DATA_HEAD(bh)->key = cpu_to_be32(inode->i_ino); | ||
| 740 | AFFS_DATA_HEAD(bh)->sequence = cpu_to_be32(bidx); | ||
| 741 | AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp); | ||
| 742 | AFFS_DATA_HEAD(bh)->next = 0; | ||
| 743 | bh->b_state &= ~(1UL << BH_New); | ||
| 744 | if (prev_bh) { | ||
| 745 | u32 tmp = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next); | ||
| 746 | if (tmp) | ||
| 747 | affs_warning(sb, "commit_write_ofs", "next block already set for %d (%d)", bidx, tmp); | ||
| 748 | AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr); | ||
| 749 | affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp); | ||
| 750 | mark_buffer_dirty_inode(prev_bh, inode); | ||
| 751 | } | ||
| 752 | } else if (be32_to_cpu(AFFS_DATA_HEAD(bh)->size) < tmp) | ||
| 753 | AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp); | ||
| 754 | affs_brelse(prev_bh); | ||
| 755 | affs_fix_checksum(sb, bh); | ||
| 756 | mark_buffer_dirty_inode(bh, inode); | ||
| 757 | written += tmp; | ||
| 758 | from += tmp; | ||
| 759 | bidx++; | ||
| 760 | } | ||
| 761 | SetPageUptodate(page); | ||
| 762 | |||
| 763 | done: | ||
| 764 | affs_brelse(bh); | ||
| 765 | tmp = (page->index << PAGE_CACHE_SHIFT) + from; | ||
| 766 | if (tmp > inode->i_size) | ||
| 767 | inode->i_size = AFFS_I(inode)->mmu_private = tmp; | ||
| 768 | |||
| 769 | return written; | ||
| 770 | |||
| 771 | out: | ||
| 772 | bh = prev_bh; | ||
| 773 | if (!written) | ||
| 774 | written = PTR_ERR(bh); | ||
| 775 | goto done; | ||
| 776 | } | ||
| 777 | |||
| 778 | struct address_space_operations affs_aops_ofs = { | ||
| 779 | .readpage = affs_readpage_ofs, | ||
| 780 | //.writepage = affs_writepage_ofs, | ||
| 781 | //.sync_page = affs_sync_page_ofs, | ||
| 782 | .prepare_write = affs_prepare_write_ofs, | ||
| 783 | .commit_write = affs_commit_write_ofs | ||
| 784 | }; | ||
| 785 | |||
| 786 | /* Free any preallocated blocks. */ | ||
| 787 | |||
| 788 | void | ||
| 789 | affs_free_prealloc(struct inode *inode) | ||
| 790 | { | ||
| 791 | struct super_block *sb = inode->i_sb; | ||
| 792 | |||
| 793 | pr_debug("AFFS: free_prealloc(ino=%lu)\n", inode->i_ino); | ||
| 794 | |||
| 795 | while (AFFS_I(inode)->i_pa_cnt) { | ||
| 796 | AFFS_I(inode)->i_pa_cnt--; | ||
| 797 | affs_free_block(sb, ++AFFS_I(inode)->i_lastalloc); | ||
| 798 | } | ||
| 799 | } | ||
| 800 | |||
| 801 | /* Truncate (or enlarge) a file to the requested size. */ | ||
| 802 | |||
| 803 | void | ||
| 804 | affs_truncate(struct inode *inode) | ||
| 805 | { | ||
| 806 | struct super_block *sb = inode->i_sb; | ||
| 807 | u32 ext, ext_key; | ||
| 808 | u32 last_blk, blkcnt, blk; | ||
| 809 | u32 size; | ||
| 810 | struct buffer_head *ext_bh; | ||
| 811 | int i; | ||
| 812 | |||
| 813 | pr_debug("AFFS: truncate(inode=%d, oldsize=%u, newsize=%u)\n", | ||
| 814 | (u32)inode->i_ino, (u32)AFFS_I(inode)->mmu_private, (u32)inode->i_size); | ||
| 815 | |||
| 816 | last_blk = 0; | ||
| 817 | ext = 0; | ||
| 818 | if (inode->i_size) { | ||
| 819 | last_blk = ((u32)inode->i_size - 1) / AFFS_SB(sb)->s_data_blksize; | ||
| 820 | ext = last_blk / AFFS_SB(sb)->s_hashsize; | ||
| 821 | } | ||
| 822 | |||
| 823 | if (inode->i_size > AFFS_I(inode)->mmu_private) { | ||
| 824 | struct address_space *mapping = inode->i_mapping; | ||
| 825 | struct page *page; | ||
| 826 | u32 size = inode->i_size - 1; | ||
| 827 | int res; | ||
| 828 | |||
| 829 | page = grab_cache_page(mapping, size >> PAGE_CACHE_SHIFT); | ||
| 830 | if (!page) | ||
| 831 | return; | ||
| 832 | size = (size & (PAGE_CACHE_SIZE - 1)) + 1; | ||
| 833 | res = mapping->a_ops->prepare_write(NULL, page, size, size); | ||
| 834 | if (!res) | ||
| 835 | res = mapping->a_ops->commit_write(NULL, page, size, size); | ||
| 836 | unlock_page(page); | ||
| 837 | page_cache_release(page); | ||
| 838 | mark_inode_dirty(inode); | ||
| 839 | return; | ||
| 840 | } else if (inode->i_size == AFFS_I(inode)->mmu_private) | ||
| 841 | return; | ||
| 842 | |||
| 843 | // lock cache | ||
| 844 | ext_bh = affs_get_extblock(inode, ext); | ||
| 845 | if (IS_ERR(ext_bh)) { | ||
| 846 | affs_warning(sb, "truncate", "unexpected read error for ext block %u (%d)", | ||
| 847 | ext, PTR_ERR(ext_bh)); | ||
| 848 | return; | ||
| 849 | } | ||
| 850 | if (AFFS_I(inode)->i_lc) { | ||
| 851 | /* clear linear cache */ | ||
| 852 | i = (ext + 1) >> AFFS_I(inode)->i_lc_shift; | ||
| 853 | if (AFFS_I(inode)->i_lc_size > i) { | ||
| 854 | AFFS_I(inode)->i_lc_size = i; | ||
| 855 | for (; i < AFFS_LC_SIZE; i++) | ||
| 856 | AFFS_I(inode)->i_lc[i] = 0; | ||
| 857 | } | ||
| 858 | /* clear associative cache */ | ||
| 859 | for (i = 0; i < AFFS_AC_SIZE; i++) | ||
| 860 | if (AFFS_I(inode)->i_ac[i].ext >= ext) | ||
| 861 | AFFS_I(inode)->i_ac[i].ext = 0; | ||
| 862 | } | ||
| 863 | ext_key = be32_to_cpu(AFFS_TAIL(sb, ext_bh)->extension); | ||
| 864 | |||
| 865 | blkcnt = AFFS_I(inode)->i_blkcnt; | ||
| 866 | i = 0; | ||
| 867 | blk = last_blk; | ||
| 868 | if (inode->i_size) { | ||
| 869 | i = last_blk % AFFS_SB(sb)->s_hashsize + 1; | ||
| 870 | blk++; | ||
| 871 | } else | ||
| 872 | AFFS_HEAD(ext_bh)->first_data = 0; | ||
| 873 | size = AFFS_SB(sb)->s_hashsize; | ||
| 874 | if (size > blkcnt - blk + i) | ||
| 875 | size = blkcnt - blk + i; | ||
| 876 | for (; i < size; i++, blk++) { | ||
| 877 | affs_free_block(sb, be32_to_cpu(AFFS_BLOCK(sb, ext_bh, i))); | ||
| 878 | AFFS_BLOCK(sb, ext_bh, i) = 0; | ||
| 879 | } | ||
| 880 | AFFS_TAIL(sb, ext_bh)->extension = 0; | ||
| 881 | affs_fix_checksum(sb, ext_bh); | ||
| 882 | mark_buffer_dirty_inode(ext_bh, inode); | ||
| 883 | affs_brelse(ext_bh); | ||
| 884 | |||
| 885 | if (inode->i_size) { | ||
| 886 | AFFS_I(inode)->i_blkcnt = last_blk + 1; | ||
| 887 | AFFS_I(inode)->i_extcnt = ext + 1; | ||
| 888 | if (AFFS_SB(sb)->s_flags & SF_OFS) { | ||
| 889 | struct buffer_head *bh = affs_bread_ino(inode, last_blk, 0); | ||
| 890 | u32 tmp; | ||
| 891 | if (IS_ERR(ext_bh)) { | ||
| 892 | affs_warning(sb, "truncate", "unexpected read error for last block %u (%d)", | ||
| 893 | ext, PTR_ERR(ext_bh)); | ||
| 894 | return; | ||
| 895 | } | ||
| 896 | tmp = be32_to_cpu(AFFS_DATA_HEAD(bh)->next); | ||
| 897 | AFFS_DATA_HEAD(bh)->next = 0; | ||
| 898 | affs_adjust_checksum(bh, -tmp); | ||
| 899 | affs_brelse(bh); | ||
| 900 | } | ||
| 901 | } else { | ||
| 902 | AFFS_I(inode)->i_blkcnt = 0; | ||
| 903 | AFFS_I(inode)->i_extcnt = 1; | ||
| 904 | } | ||
| 905 | AFFS_I(inode)->mmu_private = inode->i_size; | ||
| 906 | // unlock cache | ||
| 907 | |||
| 908 | while (ext_key) { | ||
| 909 | ext_bh = affs_bread(sb, ext_key); | ||
| 910 | size = AFFS_SB(sb)->s_hashsize; | ||
| 911 | if (size > blkcnt - blk) | ||
| 912 | size = blkcnt - blk; | ||
| 913 | for (i = 0; i < size; i++, blk++) | ||
| 914 | affs_free_block(sb, be32_to_cpu(AFFS_BLOCK(sb, ext_bh, i))); | ||
| 915 | affs_free_block(sb, ext_key); | ||
| 916 | ext_key = be32_to_cpu(AFFS_TAIL(sb, ext_bh)->extension); | ||
| 917 | affs_brelse(ext_bh); | ||
| 918 | } | ||
| 919 | affs_free_prealloc(inode); | ||
| 920 | } | ||
diff --git a/fs/affs/inode.c b/fs/affs/inode.c new file mode 100644 index 000000000000..7aa6f2004536 --- /dev/null +++ b/fs/affs/inode.c | |||
| @@ -0,0 +1,411 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/affs/inode.c | ||
| 3 | * | ||
| 4 | * (c) 1996 Hans-Joachim Widmaier - Rewritten | ||
| 5 | * | ||
| 6 | * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem. | ||
| 7 | * | ||
| 8 | * (C) 1992 Eric Youngdale Modified for ISO9660 filesystem. | ||
| 9 | * | ||
| 10 | * (C) 1991 Linus Torvalds - minix filesystem | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include "affs.h" | ||
| 14 | |||
| 15 | extern struct inode_operations affs_symlink_inode_operations; | ||
| 16 | extern struct timezone sys_tz; | ||
| 17 | |||
| 18 | void | ||
| 19 | affs_read_inode(struct inode *inode) | ||
| 20 | { | ||
| 21 | struct super_block *sb = inode->i_sb; | ||
| 22 | struct affs_sb_info *sbi = AFFS_SB(sb); | ||
| 23 | struct buffer_head *bh; | ||
| 24 | struct affs_head *head; | ||
| 25 | struct affs_tail *tail; | ||
| 26 | u32 block; | ||
| 27 | u32 size; | ||
| 28 | u32 prot; | ||
| 29 | u16 id; | ||
| 30 | |||
| 31 | pr_debug("AFFS: read_inode(%lu)\n",inode->i_ino); | ||
| 32 | |||
| 33 | block = inode->i_ino; | ||
| 34 | bh = affs_bread(sb, block); | ||
| 35 | if (!bh) { | ||
| 36 | affs_warning(sb, "read_inode", "Cannot read block %d", block); | ||
| 37 | goto bad_inode; | ||
| 38 | } | ||
| 39 | if (affs_checksum_block(sb, bh) || be32_to_cpu(AFFS_HEAD(bh)->ptype) != T_SHORT) { | ||
| 40 | affs_warning(sb,"read_inode", | ||
| 41 | "Checksum or type (ptype=%d) error on inode %d", | ||
| 42 | AFFS_HEAD(bh)->ptype, block); | ||
| 43 | goto bad_inode; | ||
| 44 | } | ||
| 45 | |||
| 46 | head = AFFS_HEAD(bh); | ||
| 47 | tail = AFFS_TAIL(sb, bh); | ||
| 48 | prot = be32_to_cpu(tail->protect); | ||
| 49 | |||
| 50 | inode->i_size = 0; | ||
| 51 | inode->i_nlink = 1; | ||
| 52 | inode->i_mode = 0; | ||
| 53 | AFFS_I(inode)->i_extcnt = 1; | ||
| 54 | AFFS_I(inode)->i_ext_last = ~1; | ||
| 55 | AFFS_I(inode)->i_protect = prot; | ||
| 56 | AFFS_I(inode)->i_opencnt = 0; | ||
| 57 | AFFS_I(inode)->i_blkcnt = 0; | ||
| 58 | AFFS_I(inode)->i_lc = NULL; | ||
| 59 | AFFS_I(inode)->i_lc_size = 0; | ||
| 60 | AFFS_I(inode)->i_lc_shift = 0; | ||
| 61 | AFFS_I(inode)->i_lc_mask = 0; | ||
| 62 | AFFS_I(inode)->i_ac = NULL; | ||
| 63 | AFFS_I(inode)->i_ext_bh = NULL; | ||
| 64 | AFFS_I(inode)->mmu_private = 0; | ||
| 65 | AFFS_I(inode)->i_lastalloc = 0; | ||
| 66 | AFFS_I(inode)->i_pa_cnt = 0; | ||
| 67 | |||
| 68 | if (sbi->s_flags & SF_SETMODE) | ||
| 69 | inode->i_mode = sbi->s_mode; | ||
| 70 | else | ||
| 71 | inode->i_mode = prot_to_mode(prot); | ||
| 72 | |||
| 73 | id = be16_to_cpu(tail->uid); | ||
| 74 | if (id == 0 || sbi->s_flags & SF_SETUID) | ||
| 75 | inode->i_uid = sbi->s_uid; | ||
| 76 | else if (id == 0xFFFF && sbi->s_flags & SF_MUFS) | ||
| 77 | inode->i_uid = 0; | ||
| 78 | else | ||
| 79 | inode->i_uid = id; | ||
| 80 | |||
| 81 | id = be16_to_cpu(tail->gid); | ||
| 82 | if (id == 0 || sbi->s_flags & SF_SETGID) | ||
| 83 | inode->i_gid = sbi->s_gid; | ||
| 84 | else if (id == 0xFFFF && sbi->s_flags & SF_MUFS) | ||
| 85 | inode->i_gid = 0; | ||
| 86 | else | ||
| 87 | inode->i_gid = id; | ||
| 88 | |||
| 89 | switch (be32_to_cpu(tail->stype)) { | ||
| 90 | case ST_ROOT: | ||
| 91 | inode->i_uid = sbi->s_uid; | ||
| 92 | inode->i_gid = sbi->s_gid; | ||
| 93 | /* fall through */ | ||
| 94 | case ST_USERDIR: | ||
| 95 | if (be32_to_cpu(tail->stype) == ST_USERDIR || | ||
| 96 | sbi->s_flags & SF_SETMODE) { | ||
| 97 | if (inode->i_mode & S_IRUSR) | ||
| 98 | inode->i_mode |= S_IXUSR; | ||
| 99 | if (inode->i_mode & S_IRGRP) | ||
| 100 | inode->i_mode |= S_IXGRP; | ||
| 101 | if (inode->i_mode & S_IROTH) | ||
| 102 | inode->i_mode |= S_IXOTH; | ||
| 103 | inode->i_mode |= S_IFDIR; | ||
| 104 | } else | ||
| 105 | inode->i_mode = S_IRUGO | S_IXUGO | S_IWUSR | S_IFDIR; | ||
| 106 | if (tail->link_chain) | ||
| 107 | inode->i_nlink = 2; | ||
| 108 | /* Maybe it should be controlled by mount parameter? */ | ||
| 109 | //inode->i_mode |= S_ISVTX; | ||
| 110 | inode->i_op = &affs_dir_inode_operations; | ||
| 111 | inode->i_fop = &affs_dir_operations; | ||
| 112 | break; | ||
| 113 | case ST_LINKDIR: | ||
| 114 | #if 0 | ||
| 115 | affs_warning(sb, "read_inode", "inode is LINKDIR"); | ||
| 116 | goto bad_inode; | ||
| 117 | #else | ||
| 118 | inode->i_mode |= S_IFDIR; | ||
| 119 | inode->i_op = NULL; | ||
| 120 | inode->i_fop = NULL; | ||
| 121 | break; | ||
| 122 | #endif | ||
| 123 | case ST_LINKFILE: | ||
| 124 | affs_warning(sb, "read_inode", "inode is LINKFILE"); | ||
| 125 | goto bad_inode; | ||
| 126 | case ST_FILE: | ||
| 127 | size = be32_to_cpu(tail->size); | ||
| 128 | inode->i_mode |= S_IFREG; | ||
| 129 | AFFS_I(inode)->mmu_private = inode->i_size = size; | ||
| 130 | if (inode->i_size) { | ||
| 131 | AFFS_I(inode)->i_blkcnt = (size - 1) / | ||
| 132 | sbi->s_data_blksize + 1; | ||
| 133 | AFFS_I(inode)->i_extcnt = (AFFS_I(inode)->i_blkcnt - 1) / | ||
| 134 | sbi->s_hashsize + 1; | ||
| 135 | } | ||
| 136 | if (tail->link_chain) | ||
| 137 | inode->i_nlink = 2; | ||
| 138 | inode->i_mapping->a_ops = (sbi->s_flags & SF_OFS) ? &affs_aops_ofs : &affs_aops; | ||
| 139 | inode->i_op = &affs_file_inode_operations; | ||
| 140 | inode->i_fop = &affs_file_operations; | ||
| 141 | break; | ||
| 142 | case ST_SOFTLINK: | ||
| 143 | inode->i_mode |= S_IFLNK; | ||
| 144 | inode->i_op = &affs_symlink_inode_operations; | ||
| 145 | inode->i_data.a_ops = &affs_symlink_aops; | ||
| 146 | break; | ||
| 147 | } | ||
| 148 | |||
| 149 | inode->i_mtime.tv_sec = inode->i_atime.tv_sec = inode->i_ctime.tv_sec | ||
| 150 | = (be32_to_cpu(tail->change.days) * (24 * 60 * 60) + | ||
| 151 | be32_to_cpu(tail->change.mins) * 60 + | ||
| 152 | be32_to_cpu(tail->change.ticks) / 50 + | ||
| 153 | ((8 * 365 + 2) * 24 * 60 * 60)) + | ||
| 154 | sys_tz.tz_minuteswest * 60; | ||
| 155 | inode->i_mtime.tv_nsec = inode->i_ctime.tv_nsec = inode->i_atime.tv_nsec = 0; | ||
| 156 | affs_brelse(bh); | ||
| 157 | return; | ||
| 158 | |||
| 159 | bad_inode: | ||
| 160 | make_bad_inode(inode); | ||
| 161 | affs_brelse(bh); | ||
| 162 | return; | ||
| 163 | } | ||
| 164 | |||
| 165 | int | ||
| 166 | affs_write_inode(struct inode *inode, int unused) | ||
| 167 | { | ||
| 168 | struct super_block *sb = inode->i_sb; | ||
| 169 | struct buffer_head *bh; | ||
| 170 | struct affs_tail *tail; | ||
| 171 | uid_t uid; | ||
| 172 | gid_t gid; | ||
| 173 | |||
| 174 | pr_debug("AFFS: write_inode(%lu)\n",inode->i_ino); | ||
| 175 | |||
| 176 | if (!inode->i_nlink) | ||
| 177 | // possibly free block | ||
| 178 | return 0; | ||
| 179 | bh = affs_bread(sb, inode->i_ino); | ||
| 180 | if (!bh) { | ||
| 181 | affs_error(sb,"write_inode","Cannot read block %lu",inode->i_ino); | ||
| 182 | return -EIO; | ||
| 183 | } | ||
| 184 | tail = AFFS_TAIL(sb, bh); | ||
| 185 | if (tail->stype == cpu_to_be32(ST_ROOT)) { | ||
| 186 | secs_to_datestamp(inode->i_mtime.tv_sec,&AFFS_ROOT_TAIL(sb, bh)->root_change); | ||
| 187 | } else { | ||
| 188 | tail->protect = cpu_to_be32(AFFS_I(inode)->i_protect); | ||
| 189 | tail->size = cpu_to_be32(inode->i_size); | ||
| 190 | secs_to_datestamp(inode->i_mtime.tv_sec,&tail->change); | ||
| 191 | if (!(inode->i_ino == AFFS_SB(sb)->s_root_block)) { | ||
| 192 | uid = inode->i_uid; | ||
| 193 | gid = inode->i_gid; | ||
| 194 | if (AFFS_SB(sb)->s_flags & SF_MUFS) { | ||
| 195 | if (inode->i_uid == 0 || inode->i_uid == 0xFFFF) | ||
| 196 | uid = inode->i_uid ^ ~0; | ||
| 197 | if (inode->i_gid == 0 || inode->i_gid == 0xFFFF) | ||
| 198 | gid = inode->i_gid ^ ~0; | ||
| 199 | } | ||
| 200 | if (!(AFFS_SB(sb)->s_flags & SF_SETUID)) | ||
| 201 | tail->uid = cpu_to_be16(uid); | ||
| 202 | if (!(AFFS_SB(sb)->s_flags & SF_SETGID)) | ||
| 203 | tail->gid = cpu_to_be16(gid); | ||
| 204 | } | ||
| 205 | } | ||
| 206 | affs_fix_checksum(sb, bh); | ||
| 207 | mark_buffer_dirty_inode(bh, inode); | ||
| 208 | affs_brelse(bh); | ||
| 209 | affs_free_prealloc(inode); | ||
| 210 | return 0; | ||
| 211 | } | ||
| 212 | |||
| 213 | int | ||
| 214 | affs_notify_change(struct dentry *dentry, struct iattr *attr) | ||
| 215 | { | ||
| 216 | struct inode *inode = dentry->d_inode; | ||
| 217 | int error; | ||
| 218 | |||
| 219 | pr_debug("AFFS: notify_change(%lu,0x%x)\n",inode->i_ino,attr->ia_valid); | ||
| 220 | |||
| 221 | error = inode_change_ok(inode,attr); | ||
| 222 | if (error) | ||
| 223 | goto out; | ||
| 224 | |||
| 225 | if (((attr->ia_valid & ATTR_UID) && (AFFS_SB(inode->i_sb)->s_flags & SF_SETUID)) || | ||
| 226 | ((attr->ia_valid & ATTR_GID) && (AFFS_SB(inode->i_sb)->s_flags & SF_SETGID)) || | ||
| 227 | ((attr->ia_valid & ATTR_MODE) && | ||
| 228 | (AFFS_SB(inode->i_sb)->s_flags & (SF_SETMODE | SF_IMMUTABLE)))) { | ||
| 229 | if (!(AFFS_SB(inode->i_sb)->s_flags & SF_QUIET)) | ||
| 230 | error = -EPERM; | ||
| 231 | goto out; | ||
| 232 | } | ||
| 233 | |||
| 234 | error = inode_setattr(inode, attr); | ||
| 235 | if (!error && (attr->ia_valid & ATTR_MODE)) | ||
| 236 | mode_to_prot(inode); | ||
| 237 | out: | ||
| 238 | return error; | ||
| 239 | } | ||
| 240 | |||
| 241 | void | ||
| 242 | affs_put_inode(struct inode *inode) | ||
| 243 | { | ||
| 244 | pr_debug("AFFS: put_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink); | ||
| 245 | affs_free_prealloc(inode); | ||
| 246 | if (atomic_read(&inode->i_count) == 1) { | ||
| 247 | down(&inode->i_sem); | ||
| 248 | if (inode->i_size != AFFS_I(inode)->mmu_private) | ||
| 249 | affs_truncate(inode); | ||
| 250 | up(&inode->i_sem); | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | void | ||
| 255 | affs_delete_inode(struct inode *inode) | ||
| 256 | { | ||
| 257 | pr_debug("AFFS: delete_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink); | ||
| 258 | inode->i_size = 0; | ||
| 259 | if (S_ISREG(inode->i_mode)) | ||
| 260 | affs_truncate(inode); | ||
| 261 | clear_inode(inode); | ||
| 262 | affs_free_block(inode->i_sb, inode->i_ino); | ||
| 263 | } | ||
| 264 | |||
| 265 | void | ||
| 266 | affs_clear_inode(struct inode *inode) | ||
| 267 | { | ||
| 268 | unsigned long cache_page = (unsigned long) AFFS_I(inode)->i_lc; | ||
| 269 | |||
| 270 | pr_debug("AFFS: clear_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink); | ||
| 271 | if (cache_page) { | ||
| 272 | pr_debug("AFFS: freeing ext cache\n"); | ||
| 273 | AFFS_I(inode)->i_lc = NULL; | ||
| 274 | AFFS_I(inode)->i_ac = NULL; | ||
| 275 | free_page(cache_page); | ||
| 276 | } | ||
| 277 | affs_brelse(AFFS_I(inode)->i_ext_bh); | ||
| 278 | AFFS_I(inode)->i_ext_last = ~1; | ||
| 279 | AFFS_I(inode)->i_ext_bh = NULL; | ||
| 280 | } | ||
| 281 | |||
| 282 | struct inode * | ||
| 283 | affs_new_inode(struct inode *dir) | ||
| 284 | { | ||
| 285 | struct super_block *sb = dir->i_sb; | ||
| 286 | struct inode *inode; | ||
| 287 | u32 block; | ||
| 288 | struct buffer_head *bh; | ||
| 289 | |||
| 290 | if (!(inode = new_inode(sb))) | ||
| 291 | goto err_inode; | ||
| 292 | |||
| 293 | if (!(block = affs_alloc_block(dir, dir->i_ino))) | ||
| 294 | goto err_block; | ||
| 295 | |||
| 296 | bh = affs_getzeroblk(sb, block); | ||
| 297 | if (!bh) | ||
| 298 | goto err_bh; | ||
| 299 | mark_buffer_dirty_inode(bh, inode); | ||
| 300 | affs_brelse(bh); | ||
| 301 | |||
| 302 | inode->i_uid = current->fsuid; | ||
| 303 | inode->i_gid = current->fsgid; | ||
| 304 | inode->i_ino = block; | ||
| 305 | inode->i_nlink = 1; | ||
| 306 | inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC; | ||
| 307 | AFFS_I(inode)->i_opencnt = 0; | ||
| 308 | AFFS_I(inode)->i_blkcnt = 0; | ||
| 309 | AFFS_I(inode)->i_lc = NULL; | ||
| 310 | AFFS_I(inode)->i_lc_size = 0; | ||
| 311 | AFFS_I(inode)->i_lc_shift = 0; | ||
| 312 | AFFS_I(inode)->i_lc_mask = 0; | ||
| 313 | AFFS_I(inode)->i_ac = NULL; | ||
| 314 | AFFS_I(inode)->i_ext_bh = NULL; | ||
| 315 | AFFS_I(inode)->mmu_private = 0; | ||
| 316 | AFFS_I(inode)->i_protect = 0; | ||
| 317 | AFFS_I(inode)->i_lastalloc = 0; | ||
| 318 | AFFS_I(inode)->i_pa_cnt = 0; | ||
| 319 | AFFS_I(inode)->i_extcnt = 1; | ||
| 320 | AFFS_I(inode)->i_ext_last = ~1; | ||
| 321 | |||
| 322 | insert_inode_hash(inode); | ||
| 323 | |||
| 324 | return inode; | ||
| 325 | |||
| 326 | err_bh: | ||
| 327 | affs_free_block(sb, block); | ||
| 328 | err_block: | ||
| 329 | iput(inode); | ||
| 330 | err_inode: | ||
| 331 | return NULL; | ||
| 332 | } | ||
| 333 | |||
| 334 | /* | ||
| 335 | * Add an entry to a directory. Create the header block | ||
| 336 | * and insert it into the hash table. | ||
| 337 | */ | ||
| 338 | |||
| 339 | int | ||
| 340 | affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s32 type) | ||
| 341 | { | ||
| 342 | struct super_block *sb = dir->i_sb; | ||
| 343 | struct buffer_head *inode_bh = NULL; | ||
| 344 | struct buffer_head *bh = NULL; | ||
| 345 | u32 block = 0; | ||
| 346 | int retval; | ||
| 347 | |||
| 348 | pr_debug("AFFS: add_entry(dir=%u, inode=%u, \"%*s\", type=%d)\n", (u32)dir->i_ino, | ||
| 349 | (u32)inode->i_ino, (int)dentry->d_name.len, dentry->d_name.name, type); | ||
| 350 | |||
| 351 | retval = -EIO; | ||
| 352 | bh = affs_bread(sb, inode->i_ino); | ||
| 353 | if (!bh) | ||
| 354 | goto done; | ||
| 355 | |||
| 356 | affs_lock_link(inode); | ||
| 357 | switch (type) { | ||
| 358 | case ST_LINKFILE: | ||
| 359 | case ST_LINKDIR: | ||
| 360 | inode_bh = bh; | ||
| 361 | retval = -ENOSPC; | ||
| 362 | block = affs_alloc_block(dir, dir->i_ino); | ||
| 363 | if (!block) | ||
| 364 | goto err; | ||
| 365 | retval = -EIO; | ||
| 366 | bh = affs_getzeroblk(sb, block); | ||
| 367 | if (!bh) | ||
| 368 | goto err; | ||
| 369 | break; | ||
| 370 | default: | ||
| 371 | break; | ||
| 372 | } | ||
| 373 | |||
| 374 | AFFS_HEAD(bh)->ptype = cpu_to_be32(T_SHORT); | ||
| 375 | AFFS_HEAD(bh)->key = cpu_to_be32(bh->b_blocknr); | ||
| 376 | affs_copy_name(AFFS_TAIL(sb, bh)->name, dentry); | ||
| 377 | AFFS_TAIL(sb, bh)->stype = cpu_to_be32(type); | ||
| 378 | AFFS_TAIL(sb, bh)->parent = cpu_to_be32(dir->i_ino); | ||
| 379 | |||
| 380 | if (inode_bh) { | ||
| 381 | __be32 chain; | ||
| 382 | chain = AFFS_TAIL(sb, inode_bh)->link_chain; | ||
| 383 | AFFS_TAIL(sb, bh)->original = cpu_to_be32(inode->i_ino); | ||
| 384 | AFFS_TAIL(sb, bh)->link_chain = chain; | ||
| 385 | AFFS_TAIL(sb, inode_bh)->link_chain = cpu_to_be32(block); | ||
| 386 | affs_adjust_checksum(inode_bh, block - be32_to_cpu(chain)); | ||
| 387 | mark_buffer_dirty_inode(inode_bh, inode); | ||
| 388 | inode->i_nlink = 2; | ||
| 389 | atomic_inc(&inode->i_count); | ||
| 390 | } | ||
| 391 | affs_fix_checksum(sb, bh); | ||
| 392 | mark_buffer_dirty_inode(bh, inode); | ||
| 393 | dentry->d_fsdata = (void *)(long)bh->b_blocknr; | ||
| 394 | |||
| 395 | affs_lock_dir(dir); | ||
| 396 | retval = affs_insert_hash(dir, bh); | ||
| 397 | mark_buffer_dirty_inode(bh, inode); | ||
| 398 | affs_unlock_dir(dir); | ||
| 399 | affs_unlock_link(inode); | ||
| 400 | |||
| 401 | d_instantiate(dentry, inode); | ||
| 402 | done: | ||
| 403 | affs_brelse(inode_bh); | ||
| 404 | affs_brelse(bh); | ||
| 405 | return retval; | ||
| 406 | err: | ||
| 407 | if (block) | ||
| 408 | affs_free_block(sb, block); | ||
| 409 | affs_unlock_link(inode); | ||
| 410 | goto done; | ||
| 411 | } | ||
diff --git a/fs/affs/namei.c b/fs/affs/namei.c new file mode 100644 index 000000000000..d4c2d636c479 --- /dev/null +++ b/fs/affs/namei.c | |||
| @@ -0,0 +1,443 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/affs/namei.c | ||
| 3 | * | ||
| 4 | * (c) 1996 Hans-Joachim Widmaier - Rewritten | ||
| 5 | * | ||
| 6 | * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem. | ||
| 7 | * | ||
| 8 | * (C) 1991 Linus Torvalds - minix filesystem | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include "affs.h" | ||
| 12 | |||
| 13 | typedef int (*toupper_t)(int); | ||
| 14 | |||
| 15 | static int affs_toupper(int ch); | ||
| 16 | static int affs_hash_dentry(struct dentry *, struct qstr *); | ||
| 17 | static int affs_compare_dentry(struct dentry *, struct qstr *, struct qstr *); | ||
| 18 | static int affs_intl_toupper(int ch); | ||
| 19 | static int affs_intl_hash_dentry(struct dentry *, struct qstr *); | ||
| 20 | static int affs_intl_compare_dentry(struct dentry *, struct qstr *, struct qstr *); | ||
| 21 | |||
| 22 | struct dentry_operations affs_dentry_operations = { | ||
| 23 | .d_hash = affs_hash_dentry, | ||
| 24 | .d_compare = affs_compare_dentry, | ||
| 25 | }; | ||
| 26 | |||
| 27 | static struct dentry_operations affs_intl_dentry_operations = { | ||
| 28 | .d_hash = affs_intl_hash_dentry, | ||
| 29 | .d_compare = affs_intl_compare_dentry, | ||
| 30 | }; | ||
| 31 | |||
| 32 | |||
| 33 | /* Simple toupper() for DOS\1 */ | ||
| 34 | |||
| 35 | static int | ||
| 36 | affs_toupper(int ch) | ||
| 37 | { | ||
| 38 | return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch; | ||
| 39 | } | ||
| 40 | |||
| 41 | /* International toupper() for DOS\3 ("international") */ | ||
| 42 | |||
| 43 | static int | ||
| 44 | affs_intl_toupper(int ch) | ||
| 45 | { | ||
| 46 | return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0 | ||
| 47 | && ch <= 0xFE && ch != 0xF7) ? | ||
| 48 | ch - ('a' - 'A') : ch; | ||
| 49 | } | ||
| 50 | |||
| 51 | static inline toupper_t | ||
| 52 | affs_get_toupper(struct super_block *sb) | ||
| 53 | { | ||
| 54 | return AFFS_SB(sb)->s_flags & SF_INTL ? affs_intl_toupper : affs_toupper; | ||
| 55 | } | ||
| 56 | |||
| 57 | /* | ||
| 58 | * Note: the dentry argument is the parent dentry. | ||
| 59 | */ | ||
| 60 | static inline int | ||
| 61 | __affs_hash_dentry(struct dentry *dentry, struct qstr *qstr, toupper_t toupper) | ||
| 62 | { | ||
| 63 | const u8 *name = qstr->name; | ||
| 64 | unsigned long hash; | ||
| 65 | int i; | ||
| 66 | |||
| 67 | i = affs_check_name(qstr->name,qstr->len); | ||
| 68 | if (i) | ||
| 69 | return i; | ||
| 70 | |||
| 71 | hash = init_name_hash(); | ||
| 72 | i = min(qstr->len, 30u); | ||
| 73 | for (; i > 0; name++, i--) | ||
| 74 | hash = partial_name_hash(toupper(*name), hash); | ||
| 75 | qstr->hash = end_name_hash(hash); | ||
| 76 | |||
| 77 | return 0; | ||
| 78 | } | ||
| 79 | |||
| 80 | static int | ||
| 81 | affs_hash_dentry(struct dentry *dentry, struct qstr *qstr) | ||
| 82 | { | ||
| 83 | return __affs_hash_dentry(dentry, qstr, affs_toupper); | ||
| 84 | } | ||
| 85 | static int | ||
| 86 | affs_intl_hash_dentry(struct dentry *dentry, struct qstr *qstr) | ||
| 87 | { | ||
| 88 | return __affs_hash_dentry(dentry, qstr, affs_intl_toupper); | ||
| 89 | } | ||
| 90 | |||
| 91 | static inline int | ||
| 92 | __affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b, toupper_t toupper) | ||
| 93 | { | ||
| 94 | const u8 *aname = a->name; | ||
| 95 | const u8 *bname = b->name; | ||
| 96 | int len; | ||
| 97 | |||
| 98 | /* 'a' is the qstr of an already existing dentry, so the name | ||
| 99 | * must be valid. 'b' must be validated first. | ||
| 100 | */ | ||
| 101 | |||
| 102 | if (affs_check_name(b->name,b->len)) | ||
| 103 | return 1; | ||
| 104 | |||
| 105 | /* If the names are longer than the allowed 30 chars, | ||
| 106 | * the excess is ignored, so their length may differ. | ||
| 107 | */ | ||
| 108 | len = a->len; | ||
| 109 | if (len >= 30) { | ||
| 110 | if (b->len < 30) | ||
| 111 | return 1; | ||
| 112 | len = 30; | ||
| 113 | } else if (len != b->len) | ||
| 114 | return 1; | ||
| 115 | |||
| 116 | for (; len > 0; len--) | ||
| 117 | if (toupper(*aname++) != toupper(*bname++)) | ||
| 118 | return 1; | ||
| 119 | |||
| 120 | return 0; | ||
| 121 | } | ||
| 122 | |||
| 123 | static int | ||
| 124 | affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b) | ||
| 125 | { | ||
| 126 | return __affs_compare_dentry(dentry, a, b, affs_toupper); | ||
| 127 | } | ||
| 128 | static int | ||
| 129 | affs_intl_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b) | ||
| 130 | { | ||
| 131 | return __affs_compare_dentry(dentry, a, b, affs_intl_toupper); | ||
| 132 | } | ||
| 133 | |||
| 134 | /* | ||
| 135 | * NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure. | ||
| 136 | */ | ||
| 137 | |||
| 138 | static inline int | ||
| 139 | affs_match(struct dentry *dentry, const u8 *name2, toupper_t toupper) | ||
| 140 | { | ||
| 141 | const u8 *name = dentry->d_name.name; | ||
| 142 | int len = dentry->d_name.len; | ||
| 143 | |||
| 144 | if (len >= 30) { | ||
| 145 | if (*name2 < 30) | ||
| 146 | return 0; | ||
| 147 | len = 30; | ||
| 148 | } else if (len != *name2) | ||
| 149 | return 0; | ||
| 150 | |||
| 151 | for (name2++; len > 0; len--) | ||
| 152 | if (toupper(*name++) != toupper(*name2++)) | ||
| 153 | return 0; | ||
| 154 | return 1; | ||
| 155 | } | ||
| 156 | |||
| 157 | int | ||
| 158 | affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len) | ||
| 159 | { | ||
| 160 | toupper_t toupper = affs_get_toupper(sb); | ||
| 161 | int hash; | ||
| 162 | |||
| 163 | hash = len = min(len, 30u); | ||
| 164 | for (; len > 0; len--) | ||
| 165 | hash = (hash * 13 + toupper(*name++)) & 0x7ff; | ||
| 166 | |||
| 167 | return hash % AFFS_SB(sb)->s_hashsize; | ||
| 168 | } | ||
| 169 | |||
| 170 | static struct buffer_head * | ||
| 171 | affs_find_entry(struct inode *dir, struct dentry *dentry) | ||
| 172 | { | ||
| 173 | struct super_block *sb = dir->i_sb; | ||
| 174 | struct buffer_head *bh; | ||
| 175 | toupper_t toupper = affs_get_toupper(sb); | ||
| 176 | u32 key; | ||
| 177 | |||
| 178 | pr_debug("AFFS: find_entry(\"%.*s\")\n", (int)dentry->d_name.len, dentry->d_name.name); | ||
| 179 | |||
| 180 | bh = affs_bread(sb, dir->i_ino); | ||
| 181 | if (!bh) | ||
| 182 | return ERR_PTR(-EIO); | ||
| 183 | |||
| 184 | key = be32_to_cpu(AFFS_HEAD(bh)->table[affs_hash_name(sb, dentry->d_name.name, dentry->d_name.len)]); | ||
| 185 | |||
| 186 | for (;;) { | ||
| 187 | affs_brelse(bh); | ||
| 188 | if (key == 0) | ||
| 189 | return NULL; | ||
| 190 | bh = affs_bread(sb, key); | ||
| 191 | if (!bh) | ||
| 192 | return ERR_PTR(-EIO); | ||
| 193 | if (affs_match(dentry, AFFS_TAIL(sb, bh)->name, toupper)) | ||
| 194 | return bh; | ||
| 195 | key = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain); | ||
| 196 | } | ||
| 197 | } | ||
| 198 | |||
| 199 | struct dentry * | ||
| 200 | affs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) | ||
| 201 | { | ||
| 202 | struct super_block *sb = dir->i_sb; | ||
| 203 | struct buffer_head *bh; | ||
| 204 | struct inode *inode = NULL; | ||
| 205 | |||
| 206 | pr_debug("AFFS: lookup(\"%.*s\")\n",(int)dentry->d_name.len,dentry->d_name.name); | ||
| 207 | |||
| 208 | affs_lock_dir(dir); | ||
| 209 | bh = affs_find_entry(dir, dentry); | ||
| 210 | affs_unlock_dir(dir); | ||
| 211 | if (IS_ERR(bh)) { | ||
| 212 | return ERR_PTR(PTR_ERR(bh)); | ||
| 213 | } | ||
| 214 | if (bh) { | ||
| 215 | u32 ino = bh->b_blocknr; | ||
| 216 | |||
| 217 | /* store the real header ino in d_fsdata for faster lookups */ | ||
| 218 | dentry->d_fsdata = (void *)(long)ino; | ||
| 219 | switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) { | ||
| 220 | //link to dirs disabled | ||
| 221 | //case ST_LINKDIR: | ||
| 222 | case ST_LINKFILE: | ||
| 223 | ino = be32_to_cpu(AFFS_TAIL(sb, bh)->original); | ||
| 224 | } | ||
| 225 | affs_brelse(bh); | ||
| 226 | inode = iget(sb, ino); | ||
| 227 | if (!inode) { | ||
| 228 | return ERR_PTR(-EACCES); | ||
| 229 | } | ||
| 230 | } | ||
| 231 | dentry->d_op = AFFS_SB(sb)->s_flags & SF_INTL ? &affs_intl_dentry_operations : &affs_dentry_operations; | ||
| 232 | d_add(dentry, inode); | ||
| 233 | return NULL; | ||
| 234 | } | ||
| 235 | |||
| 236 | int | ||
| 237 | affs_unlink(struct inode *dir, struct dentry *dentry) | ||
| 238 | { | ||
| 239 | pr_debug("AFFS: unlink(dir=%d, \"%.*s\")\n", (u32)dir->i_ino, | ||
| 240 | (int)dentry->d_name.len, dentry->d_name.name); | ||
| 241 | |||
| 242 | return affs_remove_header(dentry); | ||
| 243 | } | ||
| 244 | |||
| 245 | int | ||
| 246 | affs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) | ||
| 247 | { | ||
| 248 | struct super_block *sb = dir->i_sb; | ||
| 249 | struct inode *inode; | ||
| 250 | int error; | ||
| 251 | |||
| 252 | pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,(int)dentry->d_name.len, | ||
| 253 | dentry->d_name.name,mode); | ||
| 254 | |||
| 255 | inode = affs_new_inode(dir); | ||
| 256 | if (!inode) | ||
| 257 | return -ENOSPC; | ||
| 258 | |||
| 259 | inode->i_mode = mode; | ||
| 260 | mode_to_prot(inode); | ||
| 261 | mark_inode_dirty(inode); | ||
| 262 | |||
| 263 | inode->i_op = &affs_file_inode_operations; | ||
| 264 | inode->i_fop = &affs_file_operations; | ||
| 265 | inode->i_mapping->a_ops = (AFFS_SB(sb)->s_flags & SF_OFS) ? &affs_aops_ofs : &affs_aops; | ||
| 266 | error = affs_add_entry(dir, inode, dentry, ST_FILE); | ||
| 267 | if (error) { | ||
| 268 | inode->i_nlink = 0; | ||
| 269 | iput(inode); | ||
| 270 | return error; | ||
| 271 | } | ||
| 272 | return 0; | ||
| 273 | } | ||
| 274 | |||
| 275 | int | ||
| 276 | affs_mkdir(struct inode *dir, struct dentry *dentry, int mode) | ||
| 277 | { | ||
| 278 | struct inode *inode; | ||
| 279 | int error; | ||
| 280 | |||
| 281 | pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino, | ||
| 282 | (int)dentry->d_name.len,dentry->d_name.name,mode); | ||
| 283 | |||
| 284 | inode = affs_new_inode(dir); | ||
| 285 | if (!inode) | ||
| 286 | return -ENOSPC; | ||
| 287 | |||
| 288 | inode->i_mode = S_IFDIR | mode; | ||
| 289 | mode_to_prot(inode); | ||
| 290 | |||
| 291 | inode->i_op = &affs_dir_inode_operations; | ||
| 292 | inode->i_fop = &affs_dir_operations; | ||
| 293 | |||
| 294 | error = affs_add_entry(dir, inode, dentry, ST_USERDIR); | ||
| 295 | if (error) { | ||
| 296 | inode->i_nlink = 0; | ||
| 297 | mark_inode_dirty(inode); | ||
| 298 | iput(inode); | ||
| 299 | return error; | ||
| 300 | } | ||
| 301 | return 0; | ||
| 302 | } | ||
| 303 | |||
| 304 | int | ||
| 305 | affs_rmdir(struct inode *dir, struct dentry *dentry) | ||
| 306 | { | ||
| 307 | pr_debug("AFFS: rmdir(dir=%u, \"%.*s\")\n", (u32)dir->i_ino, | ||
| 308 | (int)dentry->d_name.len, dentry->d_name.name); | ||
| 309 | |||
| 310 | return affs_remove_header(dentry); | ||
| 311 | } | ||
| 312 | |||
| 313 | int | ||
| 314 | affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) | ||
| 315 | { | ||
| 316 | struct super_block *sb = dir->i_sb; | ||
| 317 | struct buffer_head *bh; | ||
| 318 | struct inode *inode; | ||
| 319 | char *p; | ||
| 320 | int i, maxlen, error; | ||
| 321 | char c, lc; | ||
| 322 | |||
| 323 | pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino, | ||
| 324 | (int)dentry->d_name.len,dentry->d_name.name,symname); | ||
| 325 | |||
| 326 | maxlen = AFFS_SB(sb)->s_hashsize * sizeof(u32) - 1; | ||
| 327 | inode = affs_new_inode(dir); | ||
| 328 | if (!inode) | ||
| 329 | return -ENOSPC; | ||
| 330 | |||
| 331 | inode->i_op = &affs_symlink_inode_operations; | ||
| 332 | inode->i_data.a_ops = &affs_symlink_aops; | ||
| 333 | inode->i_mode = S_IFLNK | 0777; | ||
| 334 | mode_to_prot(inode); | ||
| 335 | |||
| 336 | error = -EIO; | ||
| 337 | bh = affs_bread(sb, inode->i_ino); | ||
| 338 | if (!bh) | ||
| 339 | goto err; | ||
| 340 | i = 0; | ||
| 341 | p = (char *)AFFS_HEAD(bh)->table; | ||
| 342 | lc = '/'; | ||
| 343 | if (*symname == '/') { | ||
| 344 | while (*symname == '/') | ||
| 345 | symname++; | ||
| 346 | while (AFFS_SB(sb)->s_volume[i]) /* Cannot overflow */ | ||
| 347 | *p++ = AFFS_SB(sb)->s_volume[i++]; | ||
| 348 | } | ||
| 349 | while (i < maxlen && (c = *symname++)) { | ||
| 350 | if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') { | ||
| 351 | *p++ = '/'; | ||
| 352 | i++; | ||
| 353 | symname += 2; | ||
| 354 | lc = '/'; | ||
| 355 | } else if (c == '.' && lc == '/' && *symname == '/') { | ||
| 356 | symname++; | ||
| 357 | lc = '/'; | ||
| 358 | } else { | ||
| 359 | *p++ = c; | ||
| 360 | lc = c; | ||
| 361 | i++; | ||
| 362 | } | ||
| 363 | if (lc == '/') | ||
| 364 | while (*symname == '/') | ||
| 365 | symname++; | ||
| 366 | } | ||
| 367 | *p = 0; | ||
| 368 | mark_buffer_dirty_inode(bh, inode); | ||
| 369 | affs_brelse(bh); | ||
| 370 | mark_inode_dirty(inode); | ||
| 371 | |||
| 372 | error = affs_add_entry(dir, inode, dentry, ST_SOFTLINK); | ||
| 373 | if (error) | ||
| 374 | goto err; | ||
| 375 | |||
| 376 | return 0; | ||
| 377 | |||
| 378 | err: | ||
| 379 | inode->i_nlink = 0; | ||
| 380 | mark_inode_dirty(inode); | ||
| 381 | iput(inode); | ||
| 382 | return error; | ||
| 383 | } | ||
| 384 | |||
| 385 | int | ||
| 386 | affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) | ||
| 387 | { | ||
| 388 | struct inode *inode = old_dentry->d_inode; | ||
| 389 | |||
| 390 | pr_debug("AFFS: link(%u, %u, \"%.*s\")\n", (u32)inode->i_ino, (u32)dir->i_ino, | ||
| 391 | (int)dentry->d_name.len,dentry->d_name.name); | ||
| 392 | |||
| 393 | return affs_add_entry(dir, inode, dentry, ST_LINKFILE); | ||
| 394 | } | ||
| 395 | |||
| 396 | int | ||
| 397 | affs_rename(struct inode *old_dir, struct dentry *old_dentry, | ||
| 398 | struct inode *new_dir, struct dentry *new_dentry) | ||
| 399 | { | ||
| 400 | struct super_block *sb = old_dir->i_sb; | ||
| 401 | struct buffer_head *bh = NULL; | ||
| 402 | int retval; | ||
| 403 | |||
| 404 | pr_debug("AFFS: rename(old=%u,\"%*s\" to new=%u,\"%*s\")\n", | ||
| 405 | (u32)old_dir->i_ino, (int)old_dentry->d_name.len, old_dentry->d_name.name, | ||
| 406 | (u32)new_dir->i_ino, (int)new_dentry->d_name.len, new_dentry->d_name.name); | ||
| 407 | |||
| 408 | retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len); | ||
| 409 | if (retval) | ||
| 410 | return retval; | ||
| 411 | |||
| 412 | /* Unlink destination if it already exists */ | ||
| 413 | if (new_dentry->d_inode) { | ||
| 414 | retval = affs_remove_header(new_dentry); | ||
| 415 | if (retval) | ||
| 416 | return retval; | ||
| 417 | } | ||
| 418 | |||
| 419 | retval = -EIO; | ||
| 420 | bh = affs_bread(sb, old_dentry->d_inode->i_ino); | ||
| 421 | if (!bh) | ||
| 422 | goto done; | ||
| 423 | |||
| 424 | /* Remove header from its parent directory. */ | ||
| 425 | affs_lock_dir(old_dir); | ||
| 426 | retval = affs_remove_hash(old_dir, bh); | ||
| 427 | affs_unlock_dir(old_dir); | ||
| 428 | if (retval) | ||
| 429 | goto done; | ||
| 430 | |||
| 431 | /* And insert it into the new directory with the new name. */ | ||
| 432 | affs_copy_name(AFFS_TAIL(sb, bh)->name, new_dentry); | ||
| 433 | affs_fix_checksum(sb, bh); | ||
| 434 | affs_lock_dir(new_dir); | ||
| 435 | retval = affs_insert_hash(new_dir, bh); | ||
| 436 | affs_unlock_dir(new_dir); | ||
| 437 | /* TODO: move it back to old_dir, if error? */ | ||
| 438 | |||
| 439 | done: | ||
| 440 | mark_buffer_dirty_inode(bh, retval ? old_dir : new_dir); | ||
| 441 | affs_brelse(bh); | ||
| 442 | return retval; | ||
| 443 | } | ||
diff --git a/fs/affs/super.c b/fs/affs/super.c new file mode 100644 index 000000000000..9c3080716c92 --- /dev/null +++ b/fs/affs/super.c | |||
| @@ -0,0 +1,569 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/affs/inode.c | ||
| 3 | * | ||
| 4 | * (c) 1996 Hans-Joachim Widmaier - Rewritten | ||
| 5 | * | ||
| 6 | * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem. | ||
| 7 | * | ||
| 8 | * (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem. | ||
| 9 | * | ||
| 10 | * (C) 1991 Linus Torvalds - minix filesystem | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include <linux/module.h> | ||
| 14 | #include <linux/init.h> | ||
| 15 | #include <linux/statfs.h> | ||
| 16 | #include <linux/parser.h> | ||
| 17 | #include "affs.h" | ||
| 18 | |||
| 19 | extern struct timezone sys_tz; | ||
| 20 | |||
| 21 | static int affs_statfs(struct super_block *sb, struct kstatfs *buf); | ||
| 22 | static int affs_remount (struct super_block *sb, int *flags, char *data); | ||
| 23 | |||
| 24 | static void | ||
| 25 | affs_put_super(struct super_block *sb) | ||
| 26 | { | ||
| 27 | struct affs_sb_info *sbi = AFFS_SB(sb); | ||
| 28 | pr_debug("AFFS: put_super()\n"); | ||
| 29 | |||
| 30 | if (!(sb->s_flags & MS_RDONLY)) { | ||
| 31 | AFFS_ROOT_TAIL(sb, sbi->s_root_bh)->bm_flag = cpu_to_be32(1); | ||
| 32 | secs_to_datestamp(get_seconds(), | ||
| 33 | &AFFS_ROOT_TAIL(sb, sbi->s_root_bh)->disk_change); | ||
| 34 | affs_fix_checksum(sb, sbi->s_root_bh); | ||
| 35 | mark_buffer_dirty(sbi->s_root_bh); | ||
| 36 | } | ||
| 37 | |||
| 38 | if (sbi->s_prefix) | ||
| 39 | kfree(sbi->s_prefix); | ||
| 40 | affs_free_bitmap(sb); | ||
| 41 | affs_brelse(sbi->s_root_bh); | ||
| 42 | kfree(sbi); | ||
| 43 | sb->s_fs_info = NULL; | ||
| 44 | return; | ||
| 45 | } | ||
| 46 | |||
| 47 | static void | ||
| 48 | affs_write_super(struct super_block *sb) | ||
| 49 | { | ||
| 50 | int clean = 2; | ||
| 51 | struct affs_sb_info *sbi = AFFS_SB(sb); | ||
| 52 | |||
| 53 | if (!(sb->s_flags & MS_RDONLY)) { | ||
| 54 | // if (sbi->s_bitmap[i].bm_bh) { | ||
| 55 | // if (buffer_dirty(sbi->s_bitmap[i].bm_bh)) { | ||
| 56 | // clean = 0; | ||
| 57 | AFFS_ROOT_TAIL(sb, sbi->s_root_bh)->bm_flag = cpu_to_be32(clean); | ||
| 58 | secs_to_datestamp(get_seconds(), | ||
| 59 | &AFFS_ROOT_TAIL(sb, sbi->s_root_bh)->disk_change); | ||
| 60 | affs_fix_checksum(sb, sbi->s_root_bh); | ||
| 61 | mark_buffer_dirty(sbi->s_root_bh); | ||
| 62 | sb->s_dirt = !clean; /* redo until bitmap synced */ | ||
| 63 | } else | ||
| 64 | sb->s_dirt = 0; | ||
| 65 | |||
| 66 | pr_debug("AFFS: write_super() at %lu, clean=%d\n", get_seconds(), clean); | ||
| 67 | } | ||
| 68 | |||
| 69 | static kmem_cache_t * affs_inode_cachep; | ||
| 70 | |||
| 71 | static struct inode *affs_alloc_inode(struct super_block *sb) | ||
| 72 | { | ||
| 73 | struct affs_inode_info *ei; | ||
| 74 | ei = (struct affs_inode_info *)kmem_cache_alloc(affs_inode_cachep, SLAB_KERNEL); | ||
| 75 | if (!ei) | ||
| 76 | return NULL; | ||
| 77 | ei->vfs_inode.i_version = 1; | ||
| 78 | return &ei->vfs_inode; | ||
| 79 | } | ||
| 80 | |||
| 81 | static void affs_destroy_inode(struct inode *inode) | ||
| 82 | { | ||
| 83 | kmem_cache_free(affs_inode_cachep, AFFS_I(inode)); | ||
| 84 | } | ||
| 85 | |||
| 86 | static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) | ||
| 87 | { | ||
| 88 | struct affs_inode_info *ei = (struct affs_inode_info *) foo; | ||
| 89 | |||
| 90 | if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == | ||
| 91 | SLAB_CTOR_CONSTRUCTOR) { | ||
| 92 | init_MUTEX(&ei->i_link_lock); | ||
| 93 | init_MUTEX(&ei->i_ext_lock); | ||
| 94 | inode_init_once(&ei->vfs_inode); | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | static int init_inodecache(void) | ||
| 99 | { | ||
| 100 | affs_inode_cachep = kmem_cache_create("affs_inode_cache", | ||
| 101 | sizeof(struct affs_inode_info), | ||
| 102 | 0, SLAB_RECLAIM_ACCOUNT, | ||
| 103 | init_once, NULL); | ||
| 104 | if (affs_inode_cachep == NULL) | ||
| 105 | return -ENOMEM; | ||
| 106 | return 0; | ||
| 107 | } | ||
| 108 | |||
| 109 | static void destroy_inodecache(void) | ||
| 110 | { | ||
| 111 | if (kmem_cache_destroy(affs_inode_cachep)) | ||
| 112 | printk(KERN_INFO "affs_inode_cache: not all structures were freed\n"); | ||
| 113 | } | ||
| 114 | |||
| 115 | static struct super_operations affs_sops = { | ||
| 116 | .alloc_inode = affs_alloc_inode, | ||
| 117 | .destroy_inode = affs_destroy_inode, | ||
| 118 | .read_inode = affs_read_inode, | ||
| 119 | .write_inode = affs_write_inode, | ||
| 120 | .put_inode = affs_put_inode, | ||
| 121 | .delete_inode = affs_delete_inode, | ||
| 122 | .clear_inode = affs_clear_inode, | ||
| 123 | .put_super = affs_put_super, | ||
| 124 | .write_super = affs_write_super, | ||
| 125 | .statfs = affs_statfs, | ||
| 126 | .remount_fs = affs_remount, | ||
| 127 | }; | ||
| 128 | |||
| 129 | enum { | ||
| 130 | Opt_bs, Opt_mode, Opt_mufs, Opt_prefix, Opt_protect, | ||
| 131 | Opt_reserved, Opt_root, Opt_setgid, Opt_setuid, | ||
| 132 | Opt_verbose, Opt_volume, Opt_ignore, Opt_err, | ||
| 133 | }; | ||
| 134 | |||
| 135 | static match_table_t tokens = { | ||
| 136 | {Opt_bs, "bs=%u"}, | ||
| 137 | {Opt_mode, "mode=%o"}, | ||
| 138 | {Opt_mufs, "mufs"}, | ||
| 139 | {Opt_prefix, "prefix=%s"}, | ||
| 140 | {Opt_protect, "protect"}, | ||
| 141 | {Opt_reserved, "reserved=%u"}, | ||
| 142 | {Opt_root, "root=%u"}, | ||
| 143 | {Opt_setgid, "setgid=%u"}, | ||
| 144 | {Opt_setuid, "setuid=%u"}, | ||
| 145 | {Opt_verbose, "verbose"}, | ||
| 146 | {Opt_volume, "volume=%s"}, | ||
| 147 | {Opt_ignore, "grpquota"}, | ||
| 148 | {Opt_ignore, "noquota"}, | ||
| 149 | {Opt_ignore, "quota"}, | ||
| 150 | {Opt_ignore, "usrquota"}, | ||
| 151 | {Opt_err, NULL}, | ||
| 152 | }; | ||
| 153 | |||
| 154 | static int | ||
| 155 | parse_options(char *options, uid_t *uid, gid_t *gid, int *mode, int *reserved, s32 *root, | ||
| 156 | int *blocksize, char **prefix, char *volume, unsigned long *mount_opts) | ||
| 157 | { | ||
| 158 | char *p; | ||
| 159 | substring_t args[MAX_OPT_ARGS]; | ||
| 160 | |||
| 161 | /* Fill in defaults */ | ||
| 162 | |||
| 163 | *uid = current->uid; | ||
| 164 | *gid = current->gid; | ||
| 165 | *reserved = 2; | ||
| 166 | *root = -1; | ||
| 167 | *blocksize = -1; | ||
| 168 | volume[0] = ':'; | ||
| 169 | volume[1] = 0; | ||
| 170 | *mount_opts = 0; | ||
| 171 | if (!options) | ||
| 172 | return 1; | ||
| 173 | |||
| 174 | while ((p = strsep(&options, ",")) != NULL) { | ||
| 175 | int token, n, option; | ||
| 176 | if (!*p) | ||
| 177 | continue; | ||
| 178 | |||
| 179 | token = match_token(p, tokens, args); | ||
| 180 | switch (token) { | ||
| 181 | case Opt_bs: | ||
| 182 | if (match_int(&args[0], &n)) | ||
| 183 | return -EINVAL; | ||
| 184 | if (n != 512 && n != 1024 && n != 2048 | ||
| 185 | && n != 4096) { | ||
| 186 | printk ("AFFS: Invalid blocksize (512, 1024, 2048, 4096 allowed)\n"); | ||
| 187 | return 0; | ||
| 188 | } | ||
| 189 | *blocksize = n; | ||
| 190 | break; | ||
| 191 | case Opt_mode: | ||
| 192 | if (match_octal(&args[0], &option)) | ||
| 193 | return 1; | ||
| 194 | *mode = option & 0777; | ||
| 195 | *mount_opts |= SF_SETMODE; | ||
| 196 | break; | ||
| 197 | case Opt_mufs: | ||
| 198 | *mount_opts |= SF_MUFS; | ||
| 199 | break; | ||
| 200 | case Opt_prefix: | ||
| 201 | if (*prefix) { /* Free any previous prefix */ | ||
| 202 | kfree(*prefix); | ||
| 203 | *prefix = NULL; | ||
| 204 | } | ||
| 205 | *prefix = match_strdup(&args[0]); | ||
| 206 | if (!*prefix) | ||
| 207 | return 0; | ||
| 208 | *mount_opts |= SF_PREFIX; | ||
| 209 | break; | ||
| 210 | case Opt_protect: | ||
| 211 | *mount_opts |= SF_IMMUTABLE; | ||
| 212 | break; | ||
| 213 | case Opt_reserved: | ||
| 214 | if (match_int(&args[0], reserved)) | ||
| 215 | return 1; | ||
| 216 | break; | ||
| 217 | case Opt_root: | ||
| 218 | if (match_int(&args[0], root)) | ||
| 219 | return 1; | ||
| 220 | break; | ||
| 221 | case Opt_setgid: | ||
| 222 | if (match_int(&args[0], &option)) | ||
| 223 | return 1; | ||
| 224 | *gid = option; | ||
| 225 | *mount_opts |= SF_SETGID; | ||
| 226 | break; | ||
| 227 | case Opt_setuid: | ||
| 228 | if (match_int(&args[0], &option)) | ||
| 229 | return -EINVAL; | ||
| 230 | *uid = option; | ||
| 231 | *mount_opts |= SF_SETUID; | ||
| 232 | break; | ||
| 233 | case Opt_verbose: | ||
| 234 | *mount_opts |= SF_VERBOSE; | ||
| 235 | break; | ||
| 236 | case Opt_volume: { | ||
| 237 | char *vol = match_strdup(&args[0]); | ||
| 238 | strlcpy(volume, vol, 32); | ||
| 239 | kfree(vol); | ||
| 240 | break; | ||
| 241 | } | ||
| 242 | case Opt_ignore: | ||
| 243 | /* Silently ignore the quota options */ | ||
| 244 | break; | ||
| 245 | default: | ||
| 246 | printk("AFFS: Unrecognized mount option \"%s\" " | ||
| 247 | "or missing value\n", p); | ||
| 248 | return 0; | ||
| 249 | } | ||
| 250 | } | ||
| 251 | return 1; | ||
| 252 | } | ||
| 253 | |||
| 254 | /* This function definitely needs to be split up. Some fine day I'll | ||
| 255 | * hopefully have the guts to do so. Until then: sorry for the mess. | ||
| 256 | */ | ||
| 257 | |||
| 258 | static int affs_fill_super(struct super_block *sb, void *data, int silent) | ||
| 259 | { | ||
| 260 | struct affs_sb_info *sbi; | ||
| 261 | struct buffer_head *root_bh = NULL; | ||
| 262 | struct buffer_head *boot_bh; | ||
| 263 | struct inode *root_inode = NULL; | ||
| 264 | s32 root_block; | ||
| 265 | int size, blocksize; | ||
| 266 | u32 chksum; | ||
| 267 | int num_bm; | ||
| 268 | int i, j; | ||
| 269 | s32 key; | ||
| 270 | uid_t uid; | ||
| 271 | gid_t gid; | ||
| 272 | int reserved; | ||
| 273 | unsigned long mount_flags; | ||
| 274 | int tmp_flags; /* fix remount prototype... */ | ||
| 275 | |||
| 276 | pr_debug("AFFS: read_super(%s)\n",data ? (const char *)data : "no options"); | ||
| 277 | |||
| 278 | sb->s_magic = AFFS_SUPER_MAGIC; | ||
| 279 | sb->s_op = &affs_sops; | ||
| 280 | sb->s_flags |= MS_NODIRATIME; | ||
| 281 | |||
| 282 | sbi = kmalloc(sizeof(struct affs_sb_info), GFP_KERNEL); | ||
| 283 | if (!sbi) | ||
| 284 | return -ENOMEM; | ||
| 285 | sb->s_fs_info = sbi; | ||
| 286 | memset(sbi, 0, sizeof(*sbi)); | ||
| 287 | init_MUTEX(&sbi->s_bmlock); | ||
| 288 | |||
| 289 | if (!parse_options(data,&uid,&gid,&i,&reserved,&root_block, | ||
| 290 | &blocksize,&sbi->s_prefix, | ||
| 291 | sbi->s_volume, &mount_flags)) { | ||
| 292 | printk(KERN_ERR "AFFS: Error parsing options\n"); | ||
| 293 | return -EINVAL; | ||
| 294 | } | ||
| 295 | /* N.B. after this point s_prefix must be released */ | ||
| 296 | |||
| 297 | sbi->s_flags = mount_flags; | ||
| 298 | sbi->s_mode = i; | ||
| 299 | sbi->s_uid = uid; | ||
| 300 | sbi->s_gid = gid; | ||
| 301 | sbi->s_reserved= reserved; | ||
| 302 | |||
| 303 | /* Get the size of the device in 512-byte blocks. | ||
| 304 | * If we later see that the partition uses bigger | ||
| 305 | * blocks, we will have to change it. | ||
| 306 | */ | ||
| 307 | |||
| 308 | size = sb->s_bdev->bd_inode->i_size >> 9; | ||
| 309 | pr_debug("AFFS: initial blocksize=%d, #blocks=%d\n", 512, size); | ||
| 310 | |||
| 311 | affs_set_blocksize(sb, PAGE_SIZE); | ||
| 312 | /* Try to find root block. Its location depends on the block size. */ | ||
| 313 | |||
| 314 | i = 512; | ||
| 315 | j = 4096; | ||
| 316 | if (blocksize > 0) { | ||
| 317 | i = j = blocksize; | ||
| 318 | size = size / (blocksize / 512); | ||
| 319 | } | ||
| 320 | for (blocksize = i, key = 0; blocksize <= j; blocksize <<= 1, size >>= 1) { | ||
| 321 | sbi->s_root_block = root_block; | ||
| 322 | if (root_block < 0) | ||
| 323 | sbi->s_root_block = (reserved + size - 1) / 2; | ||
| 324 | pr_debug("AFFS: setting blocksize to %d\n", blocksize); | ||
| 325 | affs_set_blocksize(sb, blocksize); | ||
| 326 | sbi->s_partition_size = size; | ||
| 327 | |||
| 328 | /* The root block location that was calculated above is not | ||
| 329 | * correct if the partition size is an odd number of 512- | ||
| 330 | * byte blocks, which will be rounded down to a number of | ||
| 331 | * 1024-byte blocks, and if there were an even number of | ||
| 332 | * reserved blocks. Ideally, all partition checkers should | ||
| 333 | * report the real number of blocks of the real blocksize, | ||
| 334 | * but since this just cannot be done, we have to try to | ||
| 335 | * find the root block anyways. In the above case, it is one | ||
| 336 | * block behind the calculated one. So we check this one, too. | ||
| 337 | */ | ||
| 338 | for (num_bm = 0; num_bm < 2; num_bm++) { | ||
| 339 | pr_debug("AFFS: Dev %s, trying root=%u, bs=%d, " | ||
| 340 | "size=%d, reserved=%d\n", | ||
| 341 | sb->s_id, | ||
| 342 | sbi->s_root_block + num_bm, | ||
| 343 | blocksize, size, reserved); | ||
| 344 | root_bh = affs_bread(sb, sbi->s_root_block + num_bm); | ||
| 345 | if (!root_bh) | ||
| 346 | continue; | ||
| 347 | if (!affs_checksum_block(sb, root_bh) && | ||
| 348 | be32_to_cpu(AFFS_ROOT_HEAD(root_bh)->ptype) == T_SHORT && | ||
| 349 | be32_to_cpu(AFFS_ROOT_TAIL(sb, root_bh)->stype) == ST_ROOT) { | ||
| 350 | sbi->s_hashsize = blocksize / 4 - 56; | ||
| 351 | sbi->s_root_block += num_bm; | ||
| 352 | key = 1; | ||
| 353 | goto got_root; | ||
| 354 | } | ||
| 355 | affs_brelse(root_bh); | ||
| 356 | root_bh = NULL; | ||
| 357 | } | ||
| 358 | } | ||
| 359 | if (!silent) | ||
| 360 | printk(KERN_ERR "AFFS: No valid root block on device %s\n", | ||
| 361 | sb->s_id); | ||
| 362 | goto out_error; | ||
| 363 | |||
| 364 | /* N.B. after this point bh must be released */ | ||
| 365 | got_root: | ||
| 366 | root_block = sbi->s_root_block; | ||
| 367 | |||
| 368 | /* Find out which kind of FS we have */ | ||
| 369 | boot_bh = sb_bread(sb, 0); | ||
| 370 | if (!boot_bh) { | ||
| 371 | printk(KERN_ERR "AFFS: Cannot read boot block\n"); | ||
| 372 | goto out_error; | ||
| 373 | } | ||
| 374 | chksum = be32_to_cpu(*(__be32 *)boot_bh->b_data); | ||
| 375 | brelse(boot_bh); | ||
| 376 | |||
| 377 | /* Dircache filesystems are compatible with non-dircache ones | ||
| 378 | * when reading. As long as they aren't supported, writing is | ||
| 379 | * not recommended. | ||
| 380 | */ | ||
| 381 | if ((chksum == FS_DCFFS || chksum == MUFS_DCFFS || chksum == FS_DCOFS | ||
| 382 | || chksum == MUFS_DCOFS) && !(sb->s_flags & MS_RDONLY)) { | ||
| 383 | printk(KERN_NOTICE "AFFS: Dircache FS - mounting %s read only\n", | ||
| 384 | sb->s_id); | ||
| 385 | sb->s_flags |= MS_RDONLY; | ||
| 386 | } | ||
| 387 | switch (chksum) { | ||
| 388 | case MUFS_FS: | ||
| 389 | case MUFS_INTLFFS: | ||
| 390 | case MUFS_DCFFS: | ||
| 391 | sbi->s_flags |= SF_MUFS; | ||
| 392 | /* fall thru */ | ||
| 393 | case FS_INTLFFS: | ||
| 394 | case FS_DCFFS: | ||
| 395 | sbi->s_flags |= SF_INTL; | ||
| 396 | break; | ||
| 397 | case MUFS_FFS: | ||
| 398 | sbi->s_flags |= SF_MUFS; | ||
| 399 | break; | ||
| 400 | case FS_FFS: | ||
| 401 | break; | ||
| 402 | case MUFS_OFS: | ||
| 403 | sbi->s_flags |= SF_MUFS; | ||
| 404 | /* fall thru */ | ||
| 405 | case FS_OFS: | ||
| 406 | sbi->s_flags |= SF_OFS; | ||
| 407 | sb->s_flags |= MS_NOEXEC; | ||
| 408 | break; | ||
| 409 | case MUFS_DCOFS: | ||
| 410 | case MUFS_INTLOFS: | ||
| 411 | sbi->s_flags |= SF_MUFS; | ||
| 412 | case FS_DCOFS: | ||
| 413 | case FS_INTLOFS: | ||
| 414 | sbi->s_flags |= SF_INTL | SF_OFS; | ||
| 415 | sb->s_flags |= MS_NOEXEC; | ||
| 416 | break; | ||
| 417 | default: | ||
| 418 | printk(KERN_ERR "AFFS: Unknown filesystem on device %s: %08X\n", | ||
| 419 | sb->s_id, chksum); | ||
| 420 | goto out_error; | ||
| 421 | } | ||
| 422 | |||
| 423 | if (mount_flags & SF_VERBOSE) { | ||
| 424 | chksum = cpu_to_be32(chksum); | ||
| 425 | printk(KERN_NOTICE "AFFS: Mounting volume \"%*s\": Type=%.3s\\%c, Blocksize=%d\n", | ||
| 426 | AFFS_ROOT_TAIL(sb, root_bh)->disk_name[0], | ||
| 427 | AFFS_ROOT_TAIL(sb, root_bh)->disk_name + 1, | ||
| 428 | (char *)&chksum,((char *)&chksum)[3] + '0',blocksize); | ||
| 429 | } | ||
| 430 | |||
| 431 | sb->s_flags |= MS_NODEV | MS_NOSUID; | ||
| 432 | |||
| 433 | sbi->s_data_blksize = sb->s_blocksize; | ||
| 434 | if (sbi->s_flags & SF_OFS) | ||
| 435 | sbi->s_data_blksize -= 24; | ||
| 436 | |||
| 437 | /* Keep super block in cache */ | ||
| 438 | sbi->s_root_bh = root_bh; | ||
| 439 | /* N.B. after this point s_root_bh must be released */ | ||
| 440 | |||
| 441 | tmp_flags = sb->s_flags; | ||
| 442 | if (affs_init_bitmap(sb, &tmp_flags)) | ||
| 443 | goto out_error; | ||
| 444 | sb->s_flags = tmp_flags; | ||
| 445 | |||
| 446 | /* set up enough so that it can read an inode */ | ||
| 447 | |||
| 448 | root_inode = iget(sb, root_block); | ||
| 449 | sb->s_root = d_alloc_root(root_inode); | ||
| 450 | if (!sb->s_root) { | ||
| 451 | printk(KERN_ERR "AFFS: Get root inode failed\n"); | ||
| 452 | goto out_error; | ||
| 453 | } | ||
| 454 | sb->s_root->d_op = &affs_dentry_operations; | ||
| 455 | |||
| 456 | pr_debug("AFFS: s_flags=%lX\n",sb->s_flags); | ||
| 457 | return 0; | ||
| 458 | |||
| 459 | /* | ||
| 460 | * Begin the cascaded cleanup ... | ||
| 461 | */ | ||
| 462 | out_error: | ||
| 463 | if (root_inode) | ||
| 464 | iput(root_inode); | ||
| 465 | if (sbi->s_bitmap) | ||
| 466 | kfree(sbi->s_bitmap); | ||
| 467 | affs_brelse(root_bh); | ||
| 468 | if (sbi->s_prefix) | ||
| 469 | kfree(sbi->s_prefix); | ||
| 470 | kfree(sbi); | ||
| 471 | sb->s_fs_info = NULL; | ||
| 472 | return -EINVAL; | ||
| 473 | } | ||
| 474 | |||
| 475 | static int | ||
| 476 | affs_remount(struct super_block *sb, int *flags, char *data) | ||
| 477 | { | ||
| 478 | struct affs_sb_info *sbi = AFFS_SB(sb); | ||
| 479 | int blocksize; | ||
| 480 | uid_t uid; | ||
| 481 | gid_t gid; | ||
| 482 | int mode; | ||
| 483 | int reserved; | ||
| 484 | int root_block; | ||
| 485 | unsigned long mount_flags; | ||
| 486 | int res = 0; | ||
| 487 | |||
| 488 | pr_debug("AFFS: remount(flags=0x%x,opts=\"%s\")\n",*flags,data); | ||
| 489 | |||
| 490 | *flags |= MS_NODIRATIME; | ||
| 491 | |||
| 492 | if (!parse_options(data,&uid,&gid,&mode,&reserved,&root_block, | ||
| 493 | &blocksize,&sbi->s_prefix,sbi->s_volume,&mount_flags)) | ||
| 494 | return -EINVAL; | ||
| 495 | sbi->s_flags = mount_flags; | ||
| 496 | sbi->s_mode = mode; | ||
| 497 | sbi->s_uid = uid; | ||
| 498 | sbi->s_gid = gid; | ||
| 499 | |||
| 500 | if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) | ||
| 501 | return 0; | ||
| 502 | if (*flags & MS_RDONLY) { | ||
| 503 | sb->s_dirt = 1; | ||
| 504 | while (sb->s_dirt) | ||
| 505 | affs_write_super(sb); | ||
| 506 | affs_free_bitmap(sb); | ||
| 507 | } else | ||
| 508 | res = affs_init_bitmap(sb, flags); | ||
| 509 | |||
| 510 | return res; | ||
| 511 | } | ||
| 512 | |||
| 513 | static int | ||
| 514 | affs_statfs(struct super_block *sb, struct kstatfs *buf) | ||
| 515 | { | ||
| 516 | int free; | ||
| 517 | |||
| 518 | pr_debug("AFFS: statfs() partsize=%d, reserved=%d\n",AFFS_SB(sb)->s_partition_size, | ||
| 519 | AFFS_SB(sb)->s_reserved); | ||
| 520 | |||
| 521 | free = affs_count_free_blocks(sb); | ||
| 522 | buf->f_type = AFFS_SUPER_MAGIC; | ||
| 523 | buf->f_bsize = sb->s_blocksize; | ||
| 524 | buf->f_blocks = AFFS_SB(sb)->s_partition_size - AFFS_SB(sb)->s_reserved; | ||
| 525 | buf->f_bfree = free; | ||
| 526 | buf->f_bavail = free; | ||
| 527 | return 0; | ||
| 528 | } | ||
| 529 | |||
| 530 | static struct super_block *affs_get_sb(struct file_system_type *fs_type, | ||
| 531 | int flags, const char *dev_name, void *data) | ||
| 532 | { | ||
| 533 | return get_sb_bdev(fs_type, flags, dev_name, data, affs_fill_super); | ||
| 534 | } | ||
| 535 | |||
| 536 | static struct file_system_type affs_fs_type = { | ||
| 537 | .owner = THIS_MODULE, | ||
| 538 | .name = "affs", | ||
| 539 | .get_sb = affs_get_sb, | ||
| 540 | .kill_sb = kill_block_super, | ||
| 541 | .fs_flags = FS_REQUIRES_DEV, | ||
| 542 | }; | ||
| 543 | |||
| 544 | static int __init init_affs_fs(void) | ||
| 545 | { | ||
| 546 | int err = init_inodecache(); | ||
| 547 | if (err) | ||
| 548 | goto out1; | ||
| 549 | err = register_filesystem(&affs_fs_type); | ||
| 550 | if (err) | ||
| 551 | goto out; | ||
| 552 | return 0; | ||
| 553 | out: | ||
| 554 | destroy_inodecache(); | ||
| 555 | out1: | ||
| 556 | return err; | ||
| 557 | } | ||
| 558 | |||
| 559 | static void __exit exit_affs_fs(void) | ||
| 560 | { | ||
| 561 | unregister_filesystem(&affs_fs_type); | ||
| 562 | destroy_inodecache(); | ||
| 563 | } | ||
| 564 | |||
| 565 | MODULE_DESCRIPTION("Amiga filesystem support for Linux"); | ||
| 566 | MODULE_LICENSE("GPL"); | ||
| 567 | |||
| 568 | module_init(init_affs_fs) | ||
| 569 | module_exit(exit_affs_fs) | ||
diff --git a/fs/affs/symlink.c b/fs/affs/symlink.c new file mode 100644 index 000000000000..426f0f094f23 --- /dev/null +++ b/fs/affs/symlink.c | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/affs/symlink.c | ||
| 3 | * | ||
| 4 | * 1995 Hans-Joachim Widmaier - Modified for affs. | ||
| 5 | * | ||
| 6 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
| 7 | * | ||
| 8 | * affs symlink handling code | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include "affs.h" | ||
| 12 | |||
| 13 | static int affs_symlink_readpage(struct file *file, struct page *page) | ||
| 14 | { | ||
| 15 | struct buffer_head *bh; | ||
| 16 | struct inode *inode = page->mapping->host; | ||
| 17 | char *link = kmap(page); | ||
| 18 | struct slink_front *lf; | ||
| 19 | int err; | ||
| 20 | int i, j; | ||
| 21 | char c; | ||
| 22 | char lc; | ||
| 23 | char *pf; | ||
| 24 | |||
| 25 | pr_debug("AFFS: follow_link(ino=%lu)\n",inode->i_ino); | ||
| 26 | |||
| 27 | err = -EIO; | ||
| 28 | bh = affs_bread(inode->i_sb, inode->i_ino); | ||
| 29 | if (!bh) | ||
| 30 | goto fail; | ||
| 31 | i = 0; | ||
| 32 | j = 0; | ||
| 33 | lf = (struct slink_front *)bh->b_data; | ||
| 34 | lc = 0; | ||
| 35 | pf = AFFS_SB(inode->i_sb)->s_prefix ? AFFS_SB(inode->i_sb)->s_prefix : "/"; | ||
| 36 | |||
| 37 | if (strchr(lf->symname,':')) { /* Handle assign or volume name */ | ||
| 38 | while (i < 1023 && (c = pf[i])) | ||
| 39 | link[i++] = c; | ||
| 40 | while (i < 1023 && lf->symname[j] != ':') | ||
| 41 | link[i++] = lf->symname[j++]; | ||
| 42 | if (i < 1023) | ||
| 43 | link[i++] = '/'; | ||
| 44 | j++; | ||
| 45 | lc = '/'; | ||
| 46 | } | ||
| 47 | while (i < 1023 && (c = lf->symname[j])) { | ||
| 48 | if (c == '/' && lc == '/' && i < 1020) { /* parent dir */ | ||
| 49 | link[i++] = '.'; | ||
| 50 | link[i++] = '.'; | ||
| 51 | } | ||
| 52 | link[i++] = c; | ||
| 53 | lc = c; | ||
| 54 | j++; | ||
| 55 | } | ||
| 56 | link[i] = '\0'; | ||
| 57 | affs_brelse(bh); | ||
| 58 | SetPageUptodate(page); | ||
| 59 | kunmap(page); | ||
| 60 | unlock_page(page); | ||
| 61 | return 0; | ||
| 62 | fail: | ||
| 63 | SetPageError(page); | ||
| 64 | kunmap(page); | ||
| 65 | unlock_page(page); | ||
| 66 | return err; | ||
| 67 | } | ||
| 68 | |||
| 69 | struct address_space_operations affs_symlink_aops = { | ||
| 70 | .readpage = affs_symlink_readpage, | ||
| 71 | }; | ||
| 72 | |||
| 73 | struct inode_operations affs_symlink_inode_operations = { | ||
| 74 | .readlink = generic_readlink, | ||
| 75 | .follow_link = page_follow_link_light, | ||
| 76 | .put_link = page_put_link, | ||
| 77 | .setattr = affs_notify_change, | ||
| 78 | }; | ||
