diff options
Diffstat (limited to 'fs/read_write.c')
-rw-r--r-- | fs/read_write.c | 74 |
1 files changed, 45 insertions, 29 deletions
diff --git a/fs/read_write.c b/fs/read_write.c index 179f1c33ea57..dfd125798791 100644 --- a/fs/read_write.c +++ b/fs/read_write.c | |||
@@ -35,23 +35,45 @@ static inline int unsigned_offsets(struct file *file) | |||
35 | return file->f_mode & FMODE_UNSIGNED_OFFSET; | 35 | return file->f_mode & FMODE_UNSIGNED_OFFSET; |
36 | } | 36 | } |
37 | 37 | ||
38 | static loff_t lseek_execute(struct file *file, struct inode *inode, | ||
39 | loff_t offset, loff_t maxsize) | ||
40 | { | ||
41 | if (offset < 0 && !unsigned_offsets(file)) | ||
42 | return -EINVAL; | ||
43 | if (offset > maxsize) | ||
44 | return -EINVAL; | ||
45 | |||
46 | if (offset != file->f_pos) { | ||
47 | file->f_pos = offset; | ||
48 | file->f_version = 0; | ||
49 | } | ||
50 | return offset; | ||
51 | } | ||
52 | |||
38 | /** | 53 | /** |
39 | * generic_file_llseek_unlocked - lockless generic llseek implementation | 54 | * generic_file_llseek_size - generic llseek implementation for regular files |
40 | * @file: file structure to seek on | 55 | * @file: file structure to seek on |
41 | * @offset: file offset to seek to | 56 | * @offset: file offset to seek to |
42 | * @origin: type of seek | 57 | * @origin: type of seek |
58 | * @size: max size of file system | ||
59 | * | ||
60 | * This is a variant of generic_file_llseek that allows passing in a custom | ||
61 | * file size. | ||
43 | * | 62 | * |
44 | * Updates the file offset to the value specified by @offset and @origin. | 63 | * Synchronization: |
45 | * Locking must be provided by the caller. | 64 | * SEEK_SET and SEEK_END are unsynchronized (but atomic on 64bit platforms) |
65 | * SEEK_CUR is synchronized against other SEEK_CURs, but not read/writes. | ||
66 | * read/writes behave like SEEK_SET against seeks. | ||
46 | */ | 67 | */ |
47 | loff_t | 68 | loff_t |
48 | generic_file_llseek_unlocked(struct file *file, loff_t offset, int origin) | 69 | generic_file_llseek_size(struct file *file, loff_t offset, int origin, |
70 | loff_t maxsize) | ||
49 | { | 71 | { |
50 | struct inode *inode = file->f_mapping->host; | 72 | struct inode *inode = file->f_mapping->host; |
51 | 73 | ||
52 | switch (origin) { | 74 | switch (origin) { |
53 | case SEEK_END: | 75 | case SEEK_END: |
54 | offset += inode->i_size; | 76 | offset += i_size_read(inode); |
55 | break; | 77 | break; |
56 | case SEEK_CUR: | 78 | case SEEK_CUR: |
57 | /* | 79 | /* |
@@ -62,14 +84,22 @@ generic_file_llseek_unlocked(struct file *file, loff_t offset, int origin) | |||
62 | */ | 84 | */ |
63 | if (offset == 0) | 85 | if (offset == 0) |
64 | return file->f_pos; | 86 | return file->f_pos; |
65 | offset += file->f_pos; | 87 | /* |
66 | break; | 88 | * f_lock protects against read/modify/write race with other |
89 | * SEEK_CURs. Note that parallel writes and reads behave | ||
90 | * like SEEK_SET. | ||
91 | */ | ||
92 | spin_lock(&file->f_lock); | ||
93 | offset = lseek_execute(file, inode, file->f_pos + offset, | ||
94 | maxsize); | ||
95 | spin_unlock(&file->f_lock); | ||
96 | return offset; | ||
67 | case SEEK_DATA: | 97 | case SEEK_DATA: |
68 | /* | 98 | /* |
69 | * In the generic case the entire file is data, so as long as | 99 | * In the generic case the entire file is data, so as long as |
70 | * offset isn't at the end of the file then the offset is data. | 100 | * offset isn't at the end of the file then the offset is data. |
71 | */ | 101 | */ |
72 | if (offset >= inode->i_size) | 102 | if (offset >= i_size_read(inode)) |
73 | return -ENXIO; | 103 | return -ENXIO; |
74 | break; | 104 | break; |
75 | case SEEK_HOLE: | 105 | case SEEK_HOLE: |
@@ -77,26 +107,15 @@ generic_file_llseek_unlocked(struct file *file, loff_t offset, int origin) | |||
77 | * There is a virtual hole at the end of the file, so as long as | 107 | * There is a virtual hole at the end of the file, so as long as |
78 | * offset isn't i_size or larger, return i_size. | 108 | * offset isn't i_size or larger, return i_size. |
79 | */ | 109 | */ |
80 | if (offset >= inode->i_size) | 110 | if (offset >= i_size_read(inode)) |
81 | return -ENXIO; | 111 | return -ENXIO; |
82 | offset = inode->i_size; | 112 | offset = i_size_read(inode); |
83 | break; | 113 | break; |
84 | } | 114 | } |
85 | 115 | ||
86 | if (offset < 0 && !unsigned_offsets(file)) | 116 | return lseek_execute(file, inode, offset, maxsize); |
87 | return -EINVAL; | ||
88 | if (offset > inode->i_sb->s_maxbytes) | ||
89 | return -EINVAL; | ||
90 | |||
91 | /* Special lock needed here? */ | ||
92 | if (offset != file->f_pos) { | ||
93 | file->f_pos = offset; | ||
94 | file->f_version = 0; | ||
95 | } | ||
96 | |||
97 | return offset; | ||
98 | } | 117 | } |
99 | EXPORT_SYMBOL(generic_file_llseek_unlocked); | 118 | EXPORT_SYMBOL(generic_file_llseek_size); |
100 | 119 | ||
101 | /** | 120 | /** |
102 | * generic_file_llseek - generic llseek implementation for regular files | 121 | * generic_file_llseek - generic llseek implementation for regular files |
@@ -110,13 +129,10 @@ EXPORT_SYMBOL(generic_file_llseek_unlocked); | |||
110 | */ | 129 | */ |
111 | loff_t generic_file_llseek(struct file *file, loff_t offset, int origin) | 130 | loff_t generic_file_llseek(struct file *file, loff_t offset, int origin) |
112 | { | 131 | { |
113 | loff_t rval; | 132 | struct inode *inode = file->f_mapping->host; |
114 | |||
115 | mutex_lock(&file->f_dentry->d_inode->i_mutex); | ||
116 | rval = generic_file_llseek_unlocked(file, offset, origin); | ||
117 | mutex_unlock(&file->f_dentry->d_inode->i_mutex); | ||
118 | 133 | ||
119 | return rval; | 134 | return generic_file_llseek_size(file, offset, origin, |
135 | inode->i_sb->s_maxbytes); | ||
120 | } | 136 | } |
121 | EXPORT_SYMBOL(generic_file_llseek); | 137 | EXPORT_SYMBOL(generic_file_llseek); |
122 | 138 | ||