import source from 3.0.10
[external/dosfstools.git] / src / io.c
1 /* io.c - Virtual disk input/output
2
3    Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
4    Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
5
6    This program is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation, either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19    On Debian systems, the complete text of the GNU General Public License
20    can be found in /usr/share/common-licenses/GPL-3 file.
21 */
22
23 /*
24  * Thu Feb 26 01:15:36 CET 1998: Martin Schulze <joey@infodrom.north.de>
25  *      Fixed nasty bug that caused every file with a name like
26  *      xxxxxxxx.xxx to be treated as bad name that needed to be fixed.
27  */
28
29 /* FAT32, VFAT, Atari format support, and various fixes additions May 1998
30  * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
31
32 #define _LARGEFILE64_SOURCE
33 #include <sys/types.h>
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <sys/stat.h>
39 #include <sys/ioctl.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <linux/fd.h>
43
44 #include "dosfsck.h"
45 #include "common.h"
46 #include "io.h"
47
48
49 typedef struct _change {
50     void *data;
51     loff_t pos;
52     int size;
53     struct _change *next;
54 } CHANGE;
55
56
57 static CHANGE *changes,*last;
58 static int fd,did_change = 0;
59
60 unsigned device_no;
61
62
63 #ifdef __DJGPP__
64 #include "volume.h"     /* DOS lowlevel disk access functions */
65 loff_t llseek(int fd, loff_t offset, int whence)
66 {
67     if ((whence != SEEK_SET) || (fd == 4711)) return -1; /* only those supported */
68     return VolumeSeek(offset);
69 }
70 #define open OpenVolume
71 #define close CloseVolume
72 #define read(a,b,c) ReadVolume(b,c)
73 #define write(a,b,c) WriteVolume(b,c)
74 #else
75 loff_t llseek(int fd, loff_t offset, int whence)
76 {
77     return (loff_t) lseek64(fd, (off64_t)offset, whence);
78 }
79 #endif
80
81 void fs_open(char *path,int rw)
82 {
83     struct stat stbuf;
84
85     if ((fd = open(path,rw ? O_RDWR : O_RDONLY)) < 0) {
86        perror("open");
87        exit(6);
88     }
89     changes = last = NULL;
90     did_change = 0;
91
92 #ifndef _DJGPP_
93     if (fstat(fd,&stbuf) < 0)
94         pdie("fstat %s",path);
95     device_no = S_ISBLK(stbuf.st_mode) ? (stbuf.st_rdev >> 8) & 0xff : 0;
96 #else
97     if (IsWorkingOnImageFile()) {
98         if (fstat(GetVolumeHandle(),&stbuf) < 0)
99             pdie("fstat image %s",path);
100         device_no = 0;
101     }
102     else {
103         /* return 2 for floppy, 1 for ramdisk, 7 for loopback  */
104         /* used by boot.c in Atari mode: floppy always FAT12,  */
105         /* loopback / ramdisk only FAT12 if usual floppy size, */
106         /* harddisk always FAT16 on Atari... */
107         device_no = (GetVolumeHandle() < 2) ? 2 : 1;
108         /* telling "floppy" for A:/B:, "ramdisk" for the rest */
109     }
110 #endif
111 }
112
113
114 /**
115  * Read data from the partition, accounting for any pending updates that are
116  * queued for writing.
117  *
118  * @param[in]   pos     Byte offset, relative to the beginning of the partition,
119  *                      at which to read
120  * @param[in]   size    Number of bytes to read
121  * @param[out]  data    Where to put the data read
122  */
123 void fs_read(loff_t pos,int size,void *data)
124 {
125     CHANGE *walk;
126     int got;
127
128     if (llseek(fd,pos,0) != pos) pdie("Seek to %lld",pos);
129     if ((got = read(fd,data,size)) < 0) pdie("Read %d bytes at %lld",size,pos);
130     if (got != size) die("Got %d bytes instead of %d at %lld",got,size,pos);
131     for (walk = changes; walk; walk = walk->next) {
132         if (walk->pos < pos+size && walk->pos+walk->size > pos) {
133             if (walk->pos < pos)
134                 memcpy(data,(char *) walk->data+pos-walk->pos,min(size,
135                   walk->size-pos+walk->pos));
136             else memcpy((char *) data+walk->pos-pos,walk->data,min(walk->size,
137                   size+pos-walk->pos));
138         }
139     }
140 }
141
142
143 int fs_test(loff_t pos,int size)
144 {
145     void *scratch;
146     int okay;
147
148     if (llseek(fd,pos,0) != pos) pdie("Seek to %lld",pos);
149     scratch = alloc(size);
150     okay = read(fd,scratch,size) == size;
151     free(scratch);
152     return okay;
153 }
154
155
156 void fs_write(loff_t pos,int size,void *data)
157 {
158     CHANGE *new;
159     int did;
160
161     if (write_immed) {
162         did_change = 1;
163         if (llseek(fd,pos,0) != pos) pdie("Seek to %lld",pos);
164         if ((did = write(fd,data,size)) == size) return;
165         if (did < 0) pdie("Write %d bytes at %lld",size,pos);
166         die("Wrote %d bytes instead of %d at %lld",did,size,pos);
167     }
168     new = alloc(sizeof(CHANGE));
169     new->pos = pos;
170     memcpy(new->data = alloc(new->size = size),data,size);
171     new->next = NULL;
172     if (last) last->next = new;
173     else changes = new;
174     last = new;
175 }
176
177
178 static void fs_flush(void)
179 {
180     CHANGE *this;
181     int size;
182
183     while (changes) {
184         this = changes;
185         changes = changes->next;
186         if (llseek(fd,this->pos,0) != this->pos)
187             fprintf(stderr,"Seek to %lld failed: %s\n  Did not write %d bytes.\n",
188               (long long)this->pos,strerror(errno),this->size);
189         else if ((size = write(fd,this->data,this->size)) < 0)
190                 fprintf(stderr,"Writing %d bytes at %lld failed: %s\n",this->size,
191                   (long long)this->pos,strerror(errno));
192             else if (size != this->size)
193                     fprintf(stderr,"Wrote %d bytes instead of %d bytes at %lld."
194                       "\n",size,this->size,(long long)this->pos);
195         free(this->data);
196         free(this);
197     }
198 }
199
200
201 int fs_close(int write)
202 {
203     CHANGE *next;
204     int changed;
205
206     changed = !!changes;
207     if (write) fs_flush();
208     else while (changes) {
209             next = changes->next;
210             free(changes->data);
211             free(changes);
212             changes = next;
213         }
214     if (close(fd) < 0) pdie("closing file system");
215     return changed || did_change;
216 }
217
218
219 int fs_changed(void)
220 {
221     return !!changes || did_change;
222 }