Whamcloud - gitweb
add .cvsignore
[fs/lustre-release.git] / lustre / snapfs / file.c
1 /*
2  * file.c
3  */
4
5 #define DEBUG_SUBSYSTEM S_SNAP
6
7 #include <linux/module.h>
8 #include <linux/kernel.h>
9 #include <linux/string.h>
10 #include <linux/slab.h>
11 #include <linux/stat.h>
12 #include <linux/unistd.h>
13 #include <linux/jbd.h>
14 #include <linux/ext3_fs.h>
15 #include <linux/snap.h>
16
17 #include "snapfs_internal.h" 
18
19 /* instantiate a file handle to the cache file */
20 static void currentfs_prepare_snapfile(struct inode *inode,
21                                      struct file *clone_file, 
22                                      struct inode *cache_inode,
23                                      struct file *cache_file,
24                                      struct dentry *cache_dentry)
25 {
26         cache_file->f_pos = clone_file->f_pos;
27         cache_file->f_mode = clone_file->f_mode;
28         cache_file->f_flags = clone_file->f_flags;
29         cache_file->f_count  = clone_file->f_count;
30         cache_file->f_owner  = clone_file->f_owner;
31         cache_file->f_dentry = cache_dentry;
32         cache_file->f_dentry->d_inode = cache_inode;
33 }
34
35 /* update the currentfs file struct after IO in cache file */
36 static void currentfs_restore_snapfile(struct inode *cache_inode,
37                                    struct file *cache_file, 
38                                    struct inode *clone_inode,
39                                    struct file *clone_file)
40 {
41         cache_file->f_pos = clone_file->f_pos;
42 }
43
44
45 static ssize_t currentfs_write (struct file *filp, const char *buf, 
46                                 size_t count, loff_t *ppos)
47 {
48         struct snap_cache *cache;
49         struct inode *inode = filp->f_dentry->d_inode;
50         ssize_t rc;
51         struct file_operations *fops;
52         loff_t pos;
53         long block[2]={-1,-1}, mask, i;
54         struct snap_table *table;
55         int slot = 0;
56         int index = 0;
57         struct address_space_operations *aops;
58         struct inode *cache_inode = NULL;
59         struct snapshot_operations *snapops;
60   
61         ENTRY;
62
63         if (currentfs_is_under_dotsnap(filp->f_dentry)) 
64                 RETURN(-ENOSPC);
65
66         cache = snap_find_cache(inode->i_dev);
67         if ( !cache ) 
68                 RETURN(-EINVAL);
69
70         if ( snap_needs_cow(inode) != -1 ) {
71                 CDEBUG(D_SNAP, "snap_needs_cow for ino %lu \n",inode->i_ino);
72                 snap_do_cow(inode, filp->f_dentry->d_parent->d_inode->i_ino, 0);
73         }
74
75         fops = filter_c2cffops(cache->cache_filter); 
76         if (!fops || !fops->write) 
77                 RETURN(-EINVAL);
78
79         if (filp->f_flags & O_APPEND)
80                 pos = inode->i_size;
81         else {
82                 pos = *ppos;
83                 if (pos != *ppos)
84                         RETURN(-EINVAL);
85         }
86
87         /*
88          * we only need to copy back the first and last blocks
89          */
90         mask = inode->i_sb->s_blocksize-1;
91         if( pos & mask )
92                 block[0] = pos >> inode->i_sb->s_blocksize_bits;
93         pos += count - 1;
94         if( (pos+1) &  mask )
95                 block[1] = pos >> inode->i_sb->s_blocksize_bits;
96         if( block[0] == block[1] )
97                 block[1] = -1;
98         
99         aops = filter_c2cfaops(cache->cache_filter);
100         snapops = filter_c2csnapops(cache->cache_filter);
101
102         for( i=0; i<2; i++ ){
103                 if(block[i]!=-1 && aops->bmap(inode->i_mapping, block[i])) {
104                         table = &snap_tables[cache->cache_snap_tableno];
105                         for (slot = table->tbl_count - 1; slot >= 1; slot--) {
106                                 struct address_space_operations *c_aops = 
107                                         cache_inode->i_mapping->a_ops;
108                                 cache_inode = NULL;
109                                 index = table->snap_items[slot].index;
110                                 cache_inode = snap_get_indirect(inode, NULL, index);
111
112                                 if ( !cache_inode )  continue;
113
114                                 if (c_aops->bmap(cache_inode->i_mapping, block[i])) {
115                                         CDEBUG(D_SNAP, "find cache_ino %lu\n",
116                                                 cache_inode->i_ino);
117                                         if( snapops && snapops->copy_block) {
118                                                 snapops->copy_block(inode, 
119                                                                 cache_inode, block[i]);
120                                         }
121                                         iput(cache_inode);
122                                         break;
123                                 }
124                                 iput(cache_inode);
125                         }
126                 }
127         }
128         rc = fops->write(filp, buf, count, ppos);
129         RETURN(rc);
130 }
131
132 static int currentfs_readpage(struct file *file, struct page *page)
133 {
134         int result = 0;
135         struct inode *inode = file->f_dentry->d_inode;
136         unsigned long ind_ino = inode->i_ino;
137         struct inode *pri_inode = NULL;
138         struct inode *cache_inode = NULL;
139         struct file open_file;
140         struct dentry open_dentry ;
141         struct address_space_operations *c_aops;
142         struct snap_cache *cache;
143         long block;
144         struct snap_table *table;
145         int slot = 0;
146         int index = 0;
147         int search_older = 0;
148
149         ENTRY;
150
151         cache = snap_find_cache(inode->i_dev);
152         if ( !cache ) { 
153                 RETURN(-EINVAL);
154         }
155         
156         c_aops = filter_c2cfaops(cache->cache_filter);
157
158         block = page->index >> inode->i_sb->s_blocksize_bits;
159
160         /* if there is a block in the cache, return the cache readpage */
161         if( inode->i_blocks && c_aops->bmap(inode->i_mapping, block) ) {
162                 CDEBUG(D_SNAP, "block %lu in cache, ino %lu\n", 
163                                 block, inode->i_ino);
164                 result = c_aops->readpage(file, page);
165                 RETURN(result);
166         }
167
168         /*
169          * clonefs_readpage will fill this with primary ino number
170          * we need it to follow the cloned chain of primary inode
171          */
172         if( file->f_dentry->d_fsdata ){
173                 pri_inode = iget(inode->i_sb, (unsigned long)file->f_dentry->d_fsdata);
174                 if( !pri_inode )
175                         RETURN(-EINVAL);
176                 inode = pri_inode;
177                 search_older = 1;
178         }
179
180         table = &snap_tables[cache->cache_snap_tableno];
181
182         for (slot = table->tbl_count - 1; slot >= 1; slot--)
183         {
184                 struct address_space_operations *c_aops = 
185                                         cache_inode->i_mapping->a_ops;
186                 cache_inode = NULL;
187                 index = table->snap_items[slot].index;
188                 cache_inode = snap_get_indirect(inode, NULL, index);
189
190                 if (!cache_inode )  continue;
191
192                 /* we only want slots between cache_inode to the oldest one */
193                 if(search_older && cache_inode->i_ino == ind_ino )
194                         search_older = 0;
195
196                 if (!search_older && c_aops->bmap(cache_inode->i_mapping, block)) 
197                         break;
198                 iput(cache_inode);
199         }
200         if( pri_inode )
201                 iput(pri_inode);
202
203         if ( !cache_inode )  
204                 RETURN(-EINVAL);
205
206         currentfs_prepare_snapfile(inode, file, cache_inode, &open_file,
207                               &open_dentry);
208
209         down(&cache_inode->i_sem);
210
211         if( c_aops->readpage ) {
212                 CDEBUG(D_SNAP, "block %lu NOT in cache, use redirected ino %lu\n", 
213                        block, cache_inode->i_ino );
214                 result = c_aops->readpage(&open_file, page);
215         }else {
216                 CDEBUG(D_SNAP, "cache ino %lu, readpage is NULL\n", 
217                        cache_inode->i_ino);
218         }
219         up(&cache_inode->i_sem);
220         currentfs_restore_snapfile(inode, file, cache_inode, &open_file);
221         iput(cache_inode);
222         RETURN(result);
223 }
224 struct address_space_operations currentfs_file_aops = {
225         readpage:       currentfs_readpage,
226 };
227                                                                                                                                                                                                      
228 struct file_operations currentfs_file_fops = {
229         write:          currentfs_write,
230 };
231                                                                                                                                                                                                      
232 struct inode_operations currentfs_file_iops = {
233         revalidate:     NULL,
234 };
235