diff options
Diffstat (limited to 'fs/read_write.c')
-rw-r--r-- | fs/read_write.c | 85 |
1 files changed, 41 insertions, 44 deletions
diff --git a/fs/read_write.c b/fs/read_write.c index 179f1c33ea57..672b187def62 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 - 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 |
43 | * | 58 | * |
44 | * Updates the file offset to the value specified by @offset and @origin. | 59 | * This is a generic implemenation of ->llseek usable for all normal local |
45 | * Locking must be provided by the caller. | 60 | * filesystems. It just updates the file offset to the value specified by |
61 | * @offset and @origin under i_mutex. | ||
62 | * | ||
63 | * Synchronization: | ||
64 | * SEEK_SET is 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. | ||
67 | * SEEK_END | ||
46 | */ | 68 | */ |
47 | loff_t | 69 | loff_t |
48 | generic_file_llseek_unlocked(struct file *file, loff_t offset, int origin) | 70 | generic_file_llseek(struct file *file, loff_t offset, int origin) |
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 | inode->i_sb->s_maxbytes); | ||
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,46 +107,13 @@ 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, inode->i_sb->s_maxbytes); |
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 | } | ||
99 | EXPORT_SYMBOL(generic_file_llseek_unlocked); | ||
100 | |||
101 | /** | ||
102 | * generic_file_llseek - generic llseek implementation for regular files | ||
103 | * @file: file structure to seek on | ||
104 | * @offset: file offset to seek to | ||
105 | * @origin: type of seek | ||
106 | * | ||
107 | * This is a generic implemenation of ->llseek useable for all normal local | ||
108 | * filesystems. It just updates the file offset to the value specified by | ||
109 | * @offset and @origin under i_mutex. | ||
110 | */ | ||
111 | loff_t generic_file_llseek(struct file *file, loff_t offset, int origin) | ||
112 | { | ||
113 | loff_t rval; | ||
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 | |||
119 | return rval; | ||
120 | } | 117 | } |
121 | EXPORT_SYMBOL(generic_file_llseek); | 118 | EXPORT_SYMBOL(generic_file_llseek); |
122 | 119 | ||