Start work on partition support.
[pintos-anon] / src / filesys / file.c
1 #include "filesys/file.h"
2 #include <debug.h>
3 #include <string.h>
4 #include "filesys/directory.h"
5 #include "filesys/inode.h"
6 #include "filesys/filesys.h"
7 #include "devices/partition.h"
8 #include "threads/malloc.h"
9
10 /* An open file. */
11 struct file 
12   {
13     struct inode *inode;        /* File's inode. */
14     uint8_t *bounce;            /* Bounce buffer for reads and writes. */
15     off_t pos;                  /* Current position. */
16   };
17
18 /* Opens and returns the file whose inode is in sector
19    INODE_SECTOR.  Returns a null pointer if unsuccessful. */
20 struct file *
21 file_open (disk_sector_t inode_sector) 
22 {
23   struct file *file = calloc (1, sizeof *file);
24   if (file == NULL)
25     return NULL;
26   
27   file->inode = inode_open (inode_sector);
28   file->bounce = malloc (DISK_SECTOR_SIZE);
29   file->pos = 0;
30   if (file->inode == NULL || file->bounce == NULL)
31     {
32       inode_close (file->inode);
33       free (file->bounce);
34       return NULL;
35     }
36
37   return file;
38 }
39
40 /* Closes FILE. */
41 void
42 file_close (struct file *file) 
43 {
44   if (file == NULL)
45     return;
46   
47   inode_close (file->inode);
48   free (file->bounce);
49 }
50
51 /* Reads SIZE bytes from FILE into BUFFER,
52    starting at the file's current position,
53    and advances the current position.
54    Returns the number of bytes actually read,
55    which may be less than SIZE if end of file is reached. */
56 off_t
57 file_read (struct file *file, void *buffer, off_t size) 
58 {
59   off_t bytes_read = file_read_at (file, buffer, size, file->pos);
60   file->pos += bytes_read;
61   return bytes_read;
62 }
63
64 /* Reads SIZE bytes from FILE into BUFFER,
65    starting at offset FILE_OFS in the file.
66    The file's current position is unaffected
67    Returns the number of bytes actually read,
68    which may be less than SIZE if end of file is reached. */
69 off_t
70 file_read_at (struct file *file, void *buffer_, off_t size,
71               off_t file_ofs) 
72 {
73   uint8_t *buffer = buffer_;
74   off_t bytes_read = 0;
75
76   while (size > 0) 
77     {
78       /* Disk sector to read, starting byte offset within sector. */
79       disk_sector_t sector_idx;
80       int sector_ofs = file_ofs % DISK_SECTOR_SIZE;
81
82       /* Bytes left in file, bytes left in sector, lesser of the two. */
83       off_t file_left = inode_length (file->inode) - file_ofs;
84       int sector_left = DISK_SECTOR_SIZE - sector_ofs;
85       int min_left = file_left < sector_left ? file_left : sector_left;
86
87       /* Number of bytes to actually copy out of this sector. */
88       int chunk_size = size < min_left ? size : min_left;
89       if (chunk_size <= 0)
90         break;
91
92       /* Read sector into bounce buffer, then copy into caller's
93          buffer. */
94       sector_idx = inode_byte_to_sector (file->inode, file_ofs);
95       partition_read (filesys_partition, sector_idx, file->bounce);
96       memcpy (buffer + bytes_read, file->bounce + sector_ofs, chunk_size);
97
98       /* Advance. */
99       size -= chunk_size;
100       file_ofs += chunk_size;
101       bytes_read += chunk_size;
102     }
103
104   return bytes_read;
105 }
106
107 /* Writes SIZE bytes from BUFFER into FILE,
108    starting at the file's current position,
109    and advances the current position.
110    Returns the number of bytes actually written,
111    which may be less than SIZE if end of file is reached.
112    (Normally we'd grow the file in that case, but file growth is
113    not yet implemented.) */
114 off_t
115 file_write (struct file *file, const void *buffer, off_t size) 
116 {
117   off_t bytes_written = file_write_at (file, buffer, size, file->pos);
118   file->pos += bytes_written;
119   return bytes_written;
120 }
121
122 /* Writes SIZE bytes from BUFFER into FILE,
123    starting at offset FILE_OFS in the file.
124    The file's current position is unaffected
125    Returns the number of bytes actually written,
126    which may be less than SIZE if end of file is reached.
127    (Normally we'd grow the file in that case, but file growth is
128    not yet implemented.) */
129 off_t
130 file_write_at (struct file *file, const void *buffer_, off_t size,
131                off_t file_ofs) 
132 {
133   const uint8_t *buffer = buffer_;
134   off_t bytes_written = 0;
135
136   while (size > 0) 
137     {
138       /* Sector to write, starting byte offset within sector. */
139       off_t sector_idx;
140       int sector_ofs = file_ofs % DISK_SECTOR_SIZE;
141
142       /* Bytes left in file, bytes left in sector, lesser of the two. */
143       off_t file_left = inode_length (file->inode) - file_ofs;
144       int sector_left = DISK_SECTOR_SIZE - sector_ofs;
145       int min_left = file_left < sector_left ? file_left : sector_left;
146
147       /* Number of bytes to actually write into this sector. */
148       int chunk_size = size < min_left ? size : min_left;
149       if (chunk_size <= 0)
150         break;
151
152       /* If the sector contains data before or after the chunk
153          we're writing, then we need to read in the sector
154          first.  Otherwise we start with a sector of all zeros. */
155       sector_idx = inode_byte_to_sector (file->inode, file_ofs);
156       if (sector_ofs > 0 || chunk_size < sector_left)
157         partition_read (filesys_partition, sector_idx, file->bounce);
158       else
159         memset (file->bounce, 0, DISK_SECTOR_SIZE);
160       memcpy (file->bounce + sector_ofs, buffer + bytes_written, chunk_size);
161       partition_write (filesys_partition, sector_idx, file->bounce);
162
163       /* Advance. */
164       size -= chunk_size;
165       file_ofs += chunk_size;
166       bytes_written += chunk_size;
167     }
168
169   return bytes_written;
170 }
171
172 /* Returns the size of FILE in bytes. */
173 off_t
174 file_length (struct file *file) 
175 {
176   ASSERT (file != NULL);
177   return inode_length (file->inode);
178 }
179
180 /* Sets the current position in FILE to an offset of FILE_OFS
181    bytes from the start of the file. */
182 void
183 file_seek (struct file *file, off_t file_ofs) 
184 {
185   ASSERT (file != NULL);
186   ASSERT (file_ofs >= 0);
187   file->pos = file_ofs;
188 }
189
190 /* Returns the current position in FILE as a byte offset from the
191    start of the file. */
192 off_t
193 file_tell (struct file *file) 
194 {
195   ASSERT (file != NULL);
196   return file->pos;
197 }