cd5fe3ee05ace31b9981805862fb72260a89f0d8
[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 }
49
50 /* Reads SIZE bytes from FILE into BUFFER,
51    starting at the file's current position,
52    and advances the current position.
53    Returns the number of bytes actually read,
54    which may be less than SIZE if end of file is reached. */
55 off_t
56 file_read (struct file *file, void *buffer, off_t size) 
57 {
58   off_t bytes_read = file_read_at (file, buffer, size, file->pos);
59   file->pos += bytes_read;
60   return bytes_read;
61 }
62
63 /* Reads SIZE bytes from FILE into BUFFER,
64    starting at offset FILE_OFS in the file.
65    The file's current position is unaffected
66    Returns the number of bytes actually read,
67    which may be less than SIZE if end of file is reached. */
68 off_t
69 file_read_at (struct file *file, void *buffer_, off_t size,
70               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       off_t sector_idx = inode_byte_to_sector (file->inode, file_ofs);
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       disk_read (filesys_disk, sector_idx, file->bounce);
94       memcpy (buffer + bytes_read, file->bounce + sector_ofs, chunk_size);
95
96       /* Advance. */
97       size -= chunk_size;
98       file_ofs += chunk_size;
99       bytes_read += chunk_size;
100     }
101
102   return bytes_read;
103 }
104
105 /* Writes SIZE bytes from BUFFER into FILE,
106    starting at the file's current position,
107    and advances the current position.
108    Returns the number of bytes actually written,
109    which may be less than SIZE if end of file is reached.
110    (Normally we'd grow the file in that case, but file growth is
111    not yet implemented.) */
112 off_t
113 file_write (struct file *file, const void *buffer, off_t size) 
114 {
115   off_t bytes_written = file_write_at (file, buffer, size, file->pos);
116   file->pos += bytes_written;
117   return bytes_written;
118 }
119
120 /* Writes SIZE bytes from BUFFER into FILE,
121    starting at offset FILE_OFS in the file.
122    The file's current position is unaffected
123    Returns the number of bytes actually written,
124    which may be less than SIZE if end of file is reached.
125    (Normally we'd grow the file in that case, but file growth is
126    not yet implemented.) */
127 off_t
128 file_write_at (struct file *file, const void *buffer_, off_t size,
129                off_t file_ofs) 
130 {
131   const uint8_t *buffer = buffer_;
132   off_t bytes_written = 0;
133
134   while (size > 0) 
135     {
136       /* Starting byte offset within sector to read. */
137       off_t sector_idx = inode_byte_to_sector (file->inode, file_ofs);
138       int sector_ofs = file_ofs % DISK_SECTOR_SIZE;
139
140       /* Bytes left in file, bytes left in sector, lesser of the two. */
141       off_t file_left = inode_length (file->inode) - file_ofs;
142       int sector_left = DISK_SECTOR_SIZE - sector_ofs;
143       int min_left = file_left < sector_left ? file_left : sector_left;
144
145       /* Number of bytes to actually write into this sector. */
146       int chunk_size = size < min_left ? size : min_left;
147       if (chunk_size <= 0)
148         break;
149
150       /* If the sector contains data before or after the chunk
151          we're writing, then we need to read in the sector
152          first.  Otherwise we start with a sector of all zeros. */
153       if (sector_ofs > 0 || chunk_size < sector_left)
154         disk_read (filesys_disk, sector_idx, file->bounce);
155       else
156         memset (file->bounce, 0, DISK_SECTOR_SIZE);
157       memcpy (file->bounce + sector_ofs, buffer + bytes_written, chunk_size);
158       disk_write (filesys_disk, sector_idx, file->bounce);
159
160       /* Advance. */
161       size -= chunk_size;
162       file_ofs += chunk_size;
163       bytes_written += chunk_size;
164     }
165
166   return bytes_written;
167 }
168
169 /* Returns the size of FILE in bytes. */
170 off_t
171 file_length (struct file *file) 
172 {
173   ASSERT (file != NULL);
174   return inode_length (file->inode);
175 }
176
177 /* Sets the current position in FILE to an offset of FILE_OFS
178    bytes from the start of the file. */
179 void
180 file_seek (struct file *file, off_t file_ofs) 
181 {
182   ASSERT (file != NULL);
183   ASSERT (file_ofs >= 0);
184   file->pos = file_ofs;
185 }
186
187 /* Returns the current position in FILE as a byte offset from the
188    start of the file. */
189 off_t
190 file_tell (struct file *file) 
191 {
192   ASSERT (file != NULL);
193   return file->pos;
194 }