diff options
Diffstat (limited to 'fs/ramfs')
| -rw-r--r-- | fs/ramfs/Makefile | 4 | ||||
| -rw-r--r-- | fs/ramfs/file-mmu.c | 57 | ||||
| -rw-r--r-- | fs/ramfs/file-nommu.c | 292 | ||||
| -rw-r--r-- | fs/ramfs/inode.c | 22 | ||||
| -rw-r--r-- | fs/ramfs/internal.h | 15 |
5 files changed, 368 insertions, 22 deletions
diff --git a/fs/ramfs/Makefile b/fs/ramfs/Makefile index f096f3007091..5a0236e02ee1 100644 --- a/fs/ramfs/Makefile +++ b/fs/ramfs/Makefile | |||
| @@ -4,4 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | obj-$(CONFIG_RAMFS) += ramfs.o | 5 | obj-$(CONFIG_RAMFS) += ramfs.o |
| 6 | 6 | ||
| 7 | ramfs-objs := inode.o | 7 | file-mmu-y := file-nommu.o |
| 8 | file-mmu-$(CONFIG_MMU) := file-mmu.o | ||
| 9 | ramfs-objs += inode.o $(file-mmu-y) | ||
diff --git a/fs/ramfs/file-mmu.c b/fs/ramfs/file-mmu.c new file mode 100644 index 000000000000..2115383dcc8d --- /dev/null +++ b/fs/ramfs/file-mmu.c | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | /* file-mmu.c: ramfs MMU-based file operations | ||
| 2 | * | ||
| 3 | * Resizable simple ram filesystem for Linux. | ||
| 4 | * | ||
| 5 | * Copyright (C) 2000 Linus Torvalds. | ||
| 6 | * 2000 Transmeta Corp. | ||
| 7 | * | ||
| 8 | * Usage limits added by David Gibson, Linuxcare Australia. | ||
| 9 | * This file is released under the GPL. | ||
| 10 | */ | ||
| 11 | |||
| 12 | /* | ||
| 13 | * NOTE! This filesystem is probably most useful | ||
| 14 | * not as a real filesystem, but as an example of | ||
| 15 | * how virtual filesystems can be written. | ||
| 16 | * | ||
| 17 | * It doesn't get much simpler than this. Consider | ||
| 18 | * that this file implements the full semantics of | ||
| 19 | * a POSIX-compliant read-write filesystem. | ||
| 20 | * | ||
| 21 | * Note in particular how the filesystem does not | ||
| 22 | * need to implement any data structures of its own | ||
| 23 | * to keep track of the virtual data: using the VFS | ||
| 24 | * caches is sufficient. | ||
| 25 | */ | ||
| 26 | |||
| 27 | #include <linux/module.h> | ||
| 28 | #include <linux/fs.h> | ||
| 29 | #include <linux/pagemap.h> | ||
| 30 | #include <linux/highmem.h> | ||
| 31 | #include <linux/init.h> | ||
| 32 | #include <linux/string.h> | ||
| 33 | #include <linux/smp_lock.h> | ||
| 34 | #include <linux/backing-dev.h> | ||
| 35 | #include <linux/ramfs.h> | ||
| 36 | |||
| 37 | #include <asm/uaccess.h> | ||
| 38 | #include "internal.h" | ||
| 39 | |||
| 40 | struct address_space_operations ramfs_aops = { | ||
| 41 | .readpage = simple_readpage, | ||
| 42 | .prepare_write = simple_prepare_write, | ||
| 43 | .commit_write = simple_commit_write | ||
| 44 | }; | ||
| 45 | |||
| 46 | struct file_operations ramfs_file_operations = { | ||
| 47 | .read = generic_file_read, | ||
| 48 | .write = generic_file_write, | ||
| 49 | .mmap = generic_file_mmap, | ||
| 50 | .fsync = simple_sync_file, | ||
| 51 | .sendfile = generic_file_sendfile, | ||
| 52 | .llseek = generic_file_llseek, | ||
| 53 | }; | ||
| 54 | |||
| 55 | struct inode_operations ramfs_file_inode_operations = { | ||
| 56 | .getattr = simple_getattr, | ||
| 57 | }; | ||
diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c new file mode 100644 index 000000000000..3f810acd0bfa --- /dev/null +++ b/fs/ramfs/file-nommu.c | |||
| @@ -0,0 +1,292 @@ | |||
| 1 | /* file-nommu.c: no-MMU version of ramfs | ||
| 2 | * | ||
| 3 | * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved. | ||
| 4 | * Written by David Howells (dhowells@redhat.com) | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public License | ||
| 8 | * as published by the Free Software Foundation; either version | ||
| 9 | * 2 of the License, or (at your option) any later version. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/fs.h> | ||
| 14 | #include <linux/pagemap.h> | ||
| 15 | #include <linux/highmem.h> | ||
| 16 | #include <linux/init.h> | ||
| 17 | #include <linux/string.h> | ||
| 18 | #include <linux/smp_lock.h> | ||
| 19 | #include <linux/backing-dev.h> | ||
| 20 | #include <linux/ramfs.h> | ||
| 21 | #include <linux/quotaops.h> | ||
| 22 | #include <linux/pagevec.h> | ||
| 23 | #include <linux/mman.h> | ||
| 24 | |||
| 25 | #include <asm/uaccess.h> | ||
| 26 | #include "internal.h" | ||
| 27 | |||
| 28 | static int ramfs_nommu_setattr(struct dentry *, struct iattr *); | ||
| 29 | |||
| 30 | struct address_space_operations ramfs_aops = { | ||
| 31 | .readpage = simple_readpage, | ||
| 32 | .prepare_write = simple_prepare_write, | ||
| 33 | .commit_write = simple_commit_write | ||
| 34 | }; | ||
| 35 | |||
| 36 | struct file_operations ramfs_file_operations = { | ||
| 37 | .mmap = ramfs_nommu_mmap, | ||
| 38 | .get_unmapped_area = ramfs_nommu_get_unmapped_area, | ||
| 39 | .read = generic_file_read, | ||
| 40 | .write = generic_file_write, | ||
| 41 | .fsync = simple_sync_file, | ||
| 42 | .sendfile = generic_file_sendfile, | ||
| 43 | .llseek = generic_file_llseek, | ||
| 44 | }; | ||
| 45 | |||
| 46 | struct inode_operations ramfs_file_inode_operations = { | ||
| 47 | .setattr = ramfs_nommu_setattr, | ||
| 48 | .getattr = simple_getattr, | ||
| 49 | }; | ||
| 50 | |||
| 51 | /*****************************************************************************/ | ||
| 52 | /* | ||
| 53 | * add a contiguous set of pages into a ramfs inode when it's truncated from | ||
| 54 | * size 0 on the assumption that it's going to be used for an mmap of shared | ||
| 55 | * memory | ||
| 56 | */ | ||
| 57 | static int ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize) | ||
| 58 | { | ||
| 59 | struct pagevec lru_pvec; | ||
| 60 | unsigned long npages, xpages, loop, limit; | ||
| 61 | struct page *pages; | ||
| 62 | unsigned order; | ||
| 63 | void *data; | ||
| 64 | int ret; | ||
| 65 | |||
| 66 | /* make various checks */ | ||
| 67 | order = get_order(newsize); | ||
| 68 | if (unlikely(order >= MAX_ORDER)) | ||
| 69 | goto too_big; | ||
| 70 | |||
| 71 | limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur; | ||
| 72 | if (limit != RLIM_INFINITY && newsize > limit) | ||
| 73 | goto fsize_exceeded; | ||
| 74 | |||
| 75 | if (newsize > inode->i_sb->s_maxbytes) | ||
| 76 | goto too_big; | ||
| 77 | |||
| 78 | i_size_write(inode, newsize); | ||
| 79 | |||
| 80 | /* allocate enough contiguous pages to be able to satisfy the | ||
| 81 | * request */ | ||
| 82 | pages = alloc_pages(mapping_gfp_mask(inode->i_mapping), order); | ||
| 83 | if (!pages) | ||
| 84 | return -ENOMEM; | ||
| 85 | |||
| 86 | /* split the high-order page into an array of single pages */ | ||
| 87 | xpages = 1UL << order; | ||
| 88 | npages = (newsize + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
| 89 | |||
| 90 | for (loop = 0; loop < npages; loop++) | ||
| 91 | set_page_count(pages + loop, 1); | ||
| 92 | |||
| 93 | /* trim off any pages we don't actually require */ | ||
| 94 | for (loop = npages; loop < xpages; loop++) | ||
| 95 | __free_page(pages + loop); | ||
| 96 | |||
| 97 | /* clear the memory we allocated */ | ||
| 98 | newsize = PAGE_SIZE * npages; | ||
| 99 | data = page_address(pages); | ||
| 100 | memset(data, 0, newsize); | ||
| 101 | |||
| 102 | /* attach all the pages to the inode's address space */ | ||
| 103 | pagevec_init(&lru_pvec, 0); | ||
| 104 | for (loop = 0; loop < npages; loop++) { | ||
| 105 | struct page *page = pages + loop; | ||
| 106 | |||
| 107 | ret = add_to_page_cache(page, inode->i_mapping, loop, GFP_KERNEL); | ||
| 108 | if (ret < 0) | ||
| 109 | goto add_error; | ||
| 110 | |||
| 111 | if (!pagevec_add(&lru_pvec, page)) | ||
| 112 | __pagevec_lru_add(&lru_pvec); | ||
| 113 | |||
| 114 | unlock_page(page); | ||
| 115 | } | ||
| 116 | |||
| 117 | pagevec_lru_add(&lru_pvec); | ||
| 118 | return 0; | ||
| 119 | |||
| 120 | fsize_exceeded: | ||
| 121 | send_sig(SIGXFSZ, current, 0); | ||
| 122 | too_big: | ||
| 123 | return -EFBIG; | ||
| 124 | |||
| 125 | add_error: | ||
| 126 | page_cache_release(pages + loop); | ||
| 127 | for (loop++; loop < npages; loop++) | ||
| 128 | __free_page(pages + loop); | ||
| 129 | return ret; | ||
| 130 | } | ||
| 131 | |||
| 132 | /*****************************************************************************/ | ||
| 133 | /* | ||
| 134 | * check that file shrinkage doesn't leave any VMAs dangling in midair | ||
| 135 | */ | ||
| 136 | static int ramfs_nommu_check_mappings(struct inode *inode, | ||
| 137 | size_t newsize, size_t size) | ||
| 138 | { | ||
| 139 | struct vm_area_struct *vma; | ||
| 140 | struct prio_tree_iter iter; | ||
| 141 | |||
| 142 | /* search for VMAs that fall within the dead zone */ | ||
| 143 | vma_prio_tree_foreach(vma, &iter, &inode->i_mapping->i_mmap, | ||
| 144 | newsize >> PAGE_SHIFT, | ||
| 145 | (size + PAGE_SIZE - 1) >> PAGE_SHIFT | ||
| 146 | ) { | ||
| 147 | /* found one - only interested if it's shared out of the page | ||
| 148 | * cache */ | ||
| 149 | if (vma->vm_flags & VM_SHARED) | ||
| 150 | return -ETXTBSY; /* not quite true, but near enough */ | ||
| 151 | } | ||
| 152 | |||
| 153 | return 0; | ||
| 154 | } | ||
| 155 | |||
| 156 | /*****************************************************************************/ | ||
| 157 | /* | ||
| 158 | * | ||
| 159 | */ | ||
| 160 | static int ramfs_nommu_resize(struct inode *inode, loff_t newsize, loff_t size) | ||
| 161 | { | ||
| 162 | int ret; | ||
| 163 | |||
| 164 | /* assume a truncate from zero size is going to be for the purposes of | ||
| 165 | * shared mmap */ | ||
| 166 | if (size == 0) { | ||
| 167 | if (unlikely(newsize >> 32)) | ||
| 168 | return -EFBIG; | ||
| 169 | |||
| 170 | return ramfs_nommu_expand_for_mapping(inode, newsize); | ||
| 171 | } | ||
| 172 | |||
| 173 | /* check that a decrease in size doesn't cut off any shared mappings */ | ||
| 174 | if (newsize < size) { | ||
| 175 | ret = ramfs_nommu_check_mappings(inode, newsize, size); | ||
| 176 | if (ret < 0) | ||
| 177 | return ret; | ||
| 178 | } | ||
| 179 | |||
| 180 | ret = vmtruncate(inode, size); | ||
| 181 | |||
| 182 | return ret; | ||
| 183 | } | ||
| 184 | |||
| 185 | /*****************************************************************************/ | ||
| 186 | /* | ||
| 187 | * handle a change of attributes | ||
| 188 | * - we're specifically interested in a change of size | ||
| 189 | */ | ||
| 190 | static int ramfs_nommu_setattr(struct dentry *dentry, struct iattr *ia) | ||
| 191 | { | ||
| 192 | struct inode *inode = dentry->d_inode; | ||
| 193 | unsigned int old_ia_valid = ia->ia_valid; | ||
| 194 | int ret = 0; | ||
| 195 | |||
| 196 | /* by providing our own setattr() method, we skip this quotaism */ | ||
| 197 | if ((old_ia_valid & ATTR_UID && ia->ia_uid != inode->i_uid) || | ||
| 198 | (old_ia_valid & ATTR_GID && ia->ia_gid != inode->i_gid)) | ||
| 199 | ret = DQUOT_TRANSFER(inode, ia) ? -EDQUOT : 0; | ||
| 200 | |||
| 201 | /* pick out size-changing events */ | ||
| 202 | if (ia->ia_valid & ATTR_SIZE) { | ||
| 203 | loff_t size = i_size_read(inode); | ||
| 204 | if (ia->ia_size != size) { | ||
| 205 | ret = ramfs_nommu_resize(inode, ia->ia_size, size); | ||
| 206 | if (ret < 0 || ia->ia_valid == ATTR_SIZE) | ||
| 207 | goto out; | ||
| 208 | } else { | ||
| 209 | /* we skipped the truncate but must still update | ||
| 210 | * timestamps | ||
| 211 | */ | ||
| 212 | ia->ia_valid |= ATTR_MTIME|ATTR_CTIME; | ||
| 213 | } | ||
| 214 | } | ||
| 215 | |||
| 216 | ret = inode_setattr(inode, ia); | ||
| 217 | out: | ||
| 218 | ia->ia_valid = old_ia_valid; | ||
| 219 | return ret; | ||
| 220 | } | ||
| 221 | |||
| 222 | /*****************************************************************************/ | ||
| 223 | /* | ||
| 224 | * try to determine where a shared mapping can be made | ||
| 225 | * - we require that: | ||
| 226 | * - the pages to be mapped must exist | ||
| 227 | * - the pages be physically contiguous in sequence | ||
| 228 | */ | ||
| 229 | unsigned long ramfs_nommu_get_unmapped_area(struct file *file, | ||
| 230 | unsigned long addr, unsigned long len, | ||
| 231 | unsigned long pgoff, unsigned long flags) | ||
| 232 | { | ||
| 233 | unsigned long maxpages, lpages, nr, loop, ret; | ||
| 234 | struct inode *inode = file->f_dentry->d_inode; | ||
| 235 | struct page **pages = NULL, **ptr, *page; | ||
| 236 | loff_t isize; | ||
| 237 | |||
| 238 | if (!(flags & MAP_SHARED)) | ||
| 239 | return addr; | ||
| 240 | |||
| 241 | /* the mapping mustn't extend beyond the EOF */ | ||
| 242 | lpages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
| 243 | isize = i_size_read(inode); | ||
| 244 | |||
| 245 | ret = -EINVAL; | ||
| 246 | maxpages = (isize + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
| 247 | if (pgoff >= maxpages) | ||
| 248 | goto out; | ||
| 249 | |||
| 250 | if (maxpages - pgoff < lpages) | ||
| 251 | goto out; | ||
| 252 | |||
| 253 | /* gang-find the pages */ | ||
| 254 | ret = -ENOMEM; | ||
| 255 | pages = kzalloc(lpages * sizeof(struct page *), GFP_KERNEL); | ||
| 256 | if (!pages) | ||
| 257 | goto out; | ||
| 258 | |||
| 259 | nr = find_get_pages(inode->i_mapping, pgoff, lpages, pages); | ||
| 260 | if (nr != lpages) | ||
| 261 | goto out; /* leave if some pages were missing */ | ||
| 262 | |||
| 263 | /* check the pages for physical adjacency */ | ||
| 264 | ptr = pages; | ||
| 265 | page = *ptr++; | ||
| 266 | page++; | ||
| 267 | for (loop = lpages; loop > 1; loop--) | ||
| 268 | if (*ptr++ != page++) | ||
| 269 | goto out; | ||
| 270 | |||
| 271 | /* okay - all conditions fulfilled */ | ||
| 272 | ret = (unsigned long) page_address(pages[0]); | ||
| 273 | |||
| 274 | out: | ||
| 275 | if (pages) { | ||
| 276 | ptr = pages; | ||
| 277 | for (loop = lpages; loop > 0; loop--) | ||
| 278 | put_page(*ptr++); | ||
| 279 | kfree(pages); | ||
| 280 | } | ||
| 281 | |||
| 282 | return ret; | ||
| 283 | } | ||
| 284 | |||
| 285 | /*****************************************************************************/ | ||
| 286 | /* | ||
| 287 | * set up a mapping | ||
| 288 | */ | ||
| 289 | int ramfs_nommu_mmap(struct file *file, struct vm_area_struct *vma) | ||
| 290 | { | ||
| 291 | return 0; | ||
| 292 | } | ||
diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c index 0a88917605ae..c66bd5e4c05c 100644 --- a/fs/ramfs/inode.c +++ b/fs/ramfs/inode.c | |||
| @@ -34,13 +34,12 @@ | |||
| 34 | #include <linux/ramfs.h> | 34 | #include <linux/ramfs.h> |
| 35 | 35 | ||
| 36 | #include <asm/uaccess.h> | 36 | #include <asm/uaccess.h> |
| 37 | #include "internal.h" | ||
| 37 | 38 | ||
| 38 | /* some random number */ | 39 | /* some random number */ |
| 39 | #define RAMFS_MAGIC 0x858458f6 | 40 | #define RAMFS_MAGIC 0x858458f6 |
| 40 | 41 | ||
| 41 | static struct super_operations ramfs_ops; | 42 | static struct super_operations ramfs_ops; |
| 42 | static struct address_space_operations ramfs_aops; | ||
| 43 | static struct inode_operations ramfs_file_inode_operations; | ||
| 44 | static struct inode_operations ramfs_dir_inode_operations; | 43 | static struct inode_operations ramfs_dir_inode_operations; |
| 45 | 44 | ||
| 46 | static struct backing_dev_info ramfs_backing_dev_info = { | 45 | static struct backing_dev_info ramfs_backing_dev_info = { |
| @@ -142,25 +141,6 @@ static int ramfs_symlink(struct inode * dir, struct dentry *dentry, const char * | |||
| 142 | return error; | 141 | return error; |
| 143 | } | 142 | } |
| 144 | 143 | ||
| 145 | static struct address_space_operations ramfs_aops = { | ||
| 146 | .readpage = simple_readpage, | ||
| 147 | .prepare_write = simple_prepare_write, | ||
| 148 | .commit_write = simple_commit_write | ||
| 149 | }; | ||
| 150 | |||
| 151 | struct file_operations ramfs_file_operations = { | ||
| 152 | .read = generic_file_read, | ||
| 153 | .write = generic_file_write, | ||
| 154 | .mmap = generic_file_mmap, | ||
| 155 | .fsync = simple_sync_file, | ||
| 156 | .sendfile = generic_file_sendfile, | ||
| 157 | .llseek = generic_file_llseek, | ||
| 158 | }; | ||
| 159 | |||
| 160 | static struct inode_operations ramfs_file_inode_operations = { | ||
| 161 | .getattr = simple_getattr, | ||
| 162 | }; | ||
| 163 | |||
| 164 | static struct inode_operations ramfs_dir_inode_operations = { | 144 | static struct inode_operations ramfs_dir_inode_operations = { |
| 165 | .create = ramfs_create, | 145 | .create = ramfs_create, |
| 166 | .lookup = simple_lookup, | 146 | .lookup = simple_lookup, |
diff --git a/fs/ramfs/internal.h b/fs/ramfs/internal.h new file mode 100644 index 000000000000..272c8a7120b0 --- /dev/null +++ b/fs/ramfs/internal.h | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | /* internal.h: ramfs internal definitions | ||
| 2 | * | ||
| 3 | * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved. | ||
| 4 | * Written by David Howells (dhowells@redhat.com) | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public License | ||
| 8 | * as published by the Free Software Foundation; either version | ||
| 9 | * 2 of the License, or (at your option) any later version. | ||
| 10 | */ | ||
| 11 | |||
| 12 | |||
| 13 | extern struct address_space_operations ramfs_aops; | ||
| 14 | extern struct file_operations ramfs_file_operations; | ||
| 15 | extern struct inode_operations ramfs_file_inode_operations; | ||
