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