File: vnode_raw.cpp

package info (click to toggle)
afflib 3.7.22-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,584 kB
  • sloc: cpp: 20,911; ansic: 15,912; makefile: 520; sh: 436; python: 192
file content (335 lines) | stat: -rw-r--r-- 8,747 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
#include "affconfig.h"
#include "afflib.h"
#include "afflib_i.h"
#include "vnode_raw.h"

/*
 * Distributed under the Berkeley 4-part license
 */

/* the RAW_PAGESIZE is visible outside the module, but it's kind of irrevellant */
#define RAW_PAGESIZE 16*1024*1024

/* raw file implementation */
struct raw_private {
    /* For Raw files */
    FILE *raw;				// if it is a raw file
    int raw_popen;			// opened with popen
};

#define RAW_PRIVATE(af) ((struct raw_private *)(af->vnodeprivate))

/* Return 1 if a file is a raw file... */
static int raw_identify_file(const char *filename,int /*exists*/)
{
    return af_ext_is(filename, "raw") || af_ext_is(filename, "iso");
}


/* Return the size of the raw file */
static int64_t raw_filesize(AFFILE *af)
{
    struct raw_private *rp = RAW_PRIVATE(af);

#ifdef _WIN32
    struct _stat64 sb;
    if (_fstat64(fileno(rp->raw), &sb) ==0) {
#else
    struct stat sb;
    if(fstat(fileno(rp->raw),&sb)==0){
#endif
	if(sb.st_mode & S_IFREG){	// only do this for regular files
	    return sb.st_size;
	}

	/* See if this is a device that we can figure */
	struct af_figure_media_buf afb;
	if(af_figure_media(fileno(rp->raw),&afb)==0){
	    if(afb.total_sectors>0 && afb.sector_size>0){
		return afb.total_sectors * afb.sector_size;
	    }
	}
    }
    return 0;				// no clue
}

static int raw_open(AFFILE *af)
{
    /* Raw is the passthrough system.
     */
    int fd = open(af->fname, af->openflags | O_BINARY, af->openmode);
    if(fd < 0)
        return -1;

    int accmode = (af->openflags & O_ACCMODE);
    FILE *file = fdopen(fd, (accmode == O_RDWR || accmode == O_WRONLY) ? "r+b" : "rb");
    if(!file)
    {
        close(fd);
        return -1;
    }

    af->vnodeprivate = (void *)calloc(1,sizeof(struct raw_private));
    struct raw_private *rp = RAW_PRIVATE(af);
    rp->raw = file;
    af->image_size	= raw_filesize(af);
    af->image_pagesize	= RAW_PAGESIZE;
    af->cur_page	= 0;
    return 0;
}

int raw_freopen(AFFILE *af,FILE *file)
{
    af->fname = 0;
    af->vnodeprivate = (void *)calloc(1,sizeof(struct raw_private));
    struct raw_private *rp = RAW_PRIVATE(af);
    rp->raw = file;
    af->image_size = raw_filesize(af);
    af->image_pagesize = RAW_PAGESIZE;
    af->cur_page = 0;
    return 0;
}


int raw_popen(AFFILE *af,const char *command,const char *type)
{
#ifdef HAVE_POPEN
    if(strcmp(type,"r")!=0){
	(*af->error_reporter)("af_popen: only type 'r' supported");
	return -1;
    }
    /* If shell metacharacters exist in command, don't open it */
    if(af_hasmeta(command)){
	(*af->error_reporter)("raw_popen: invalid shell metacharacters in command '%s'",
			      command);
	return -1;
    }
    af->fname = 0;
    af->vnodeprivate = (void *)calloc(1,sizeof(struct raw_private));
    struct raw_private *rp = RAW_PRIVATE(af);
    rp->raw = popen(command,"r");
    rp->raw_popen = 1;
    return 0;
#else
    (*af->error_reporter)("af_popen: popen not supported on this platform.");
    return -1;
#endif
}


static int raw_close(AFFILE *af)
{
    struct raw_private *rp = RAW_PRIVATE(af);

    if(rp->raw_popen){
#ifdef HAVE_POPEN
	pclose(rp->raw);
#endif
    }
    else {
	fclose(rp->raw);
    }
    memset(rp,0,sizeof(*rp));		// clean object reuse
    free(rp);				// won't need it again
    return 0;
}

static int raw_get_seg(AFFILE *af,const char *name,
		       uint32_t *arg,unsigned char *data,size_t *datalen)
{
    struct raw_private *rp = RAW_PRIVATE(af);

    int64_t segnum = af_segname_page_number(name);
    if(segnum<0){
	/* See if PAGESIZE or IMAGESIZE is being requested; we can fake those */
	if(strcmp(name,AF_PAGESIZE)==0){
	    if(arg) *arg = af->image_pagesize;
	    if(datalen) *datalen = 0;
	    return 0;
	}
	if(strcmp(name,AF_IMAGESIZE)==0){
	    struct aff_quad q;
	    if(data && *datalen>=8){
		q.low = htonl((uint32_t)(af->image_size & 0xffffffff));
		q.high = htonl((uint32_t)(af->image_size >> 32));
		memcpy(data,&q,8);
		*datalen = 8;
	    }
	    return 0;
	}
	if(strcmp(name,AF_SECTORSIZE)==0){
	    if(arg) *arg = af->image_sectorsize;
	    if(datalen) *datalen = 0;
	    return 0;
	}
	if(strcmp(name,AF_DEVICE_SECTORS)==0){
	    int64_t devicesectors = af->image_size / af->image_sectorsize;
	    struct aff_quad q;
	    if(data && *datalen>=8){
		q.low = htonl((uint32_t)(devicesectors & 0xffffffff));
		q.high = htonl((uint32_t)(devicesectors >> 32));
		memcpy(data,&q,8);
		*datalen = 8;
	    }
	    return 0;
	}

	return -1;		// don't know how to fake this
    }

    fflush(rp->raw);			// make sure that any buffers are flushed

    int64_t pos = (int64_t)segnum * af->image_pagesize; // where we are to start reading
    int64_t bytes_left = af->image_size - pos;	// how many bytes left in the file

    if(bytes_left<0) bytes_left = 0;

    int bytes_to_read = af->image_pagesize; // copy this many bytes, unless
    if(bytes_to_read > bytes_left) bytes_to_read = bytes_left; // only this much is left

    if(arg) *arg = 0;			// arg is always 0
    if(datalen){
	if(data==0){ // asked for 0 bytes, so give the actual size
	    *datalen = bytes_to_read;
	    return 0;
	}
	if(*datalen < (unsigned)bytes_to_read){
	    *datalen = bytes_to_read;
	    return AF_ERROR_DATASMALL;
	}
    }
    if(data){
	fseeko(rp->raw,pos,SEEK_SET);
	int bytes_read = fread(data,1,bytes_to_read,rp->raw);
	if(bytes_read==bytes_to_read){
	    if(datalen) *datalen = bytes_read;
	    return 0;
	}
	return -1;			// some kind of EOF?
    }
    return 0;				// no problems!
}


int raw_update_seg(AFFILE *af, const char *name,
                   uint32_t /*arg*/,const u_char *value,uint32_t vallen)
{
    struct raw_private *rp = RAW_PRIVATE(af);

    /* Simple implementation; only updates data segments */
    int64_t pagenum = af_segname_page_number(name);
    if(pagenum<0){
	errno = ENOTSUP;
	return -1;			// not a segment number
    }
    int64_t pos = pagenum * af->image_pagesize; // where we are to start reading
    fseeko(rp->raw,pos,SEEK_SET);

    if(fwrite(value,vallen,1,rp->raw)==1){
	return 0;
    }
    return -1;				// some kind of error...
}


static int raw_vstat(AFFILE *af,struct af_vnode_info *vni)
{
    struct raw_private *rp = RAW_PRIVATE(af);

    vni->imagesize            = -1;
    vni->pagesize	      = RAW_PAGESIZE;	// decent page size
    vni->supports_metadata    = 0;
    vni->is_raw               = 1;
    vni->changable_pagesize   = 1;	// change it at any time
    vni->changable_sectorsize = 1;	// change it at any time

    /* If we can stat the file, use that. */
    fflush(rp->raw);
    vni->imagesize = raw_filesize(af);
    vni->supports_compression = 0;
    vni->has_pages = 1;

    if(rp->raw_popen){
	/* popen files require special handling */
	vni->has_pages = 0;
	vni->use_eof   = 1;
	vni->at_eof    = feof(rp->raw);	// are we there yet?
    }
    return 0;
}

static int raw_rewind_seg(AFFILE *af)
{
    af->cur_page = 0;
    return 0;
}


static int raw_get_next_seg(AFFILE *af,char *segname,size_t segname_len,uint32_t *arg,
			unsigned char *data,size_t *datalen)
{

    /* See if we are at the end of the "virtual" segment list */
    if((uint64_t)af->cur_page * af->image_pagesize >= af->image_size) return -1;

    /* Make the segment name */
    char pagename[AF_MAX_NAME_LEN];		//
    memset(pagename,0,sizeof(pagename));
    snprintf(pagename,sizeof(pagename),AF_PAGE,af->cur_page++);

    /* Get the segment, if we can */
    int r = raw_get_seg(af,pagename,arg,data,datalen);

    /* If r==0 and there is room for copying in the segment name, return it */
    if(r==0){
	if(strlen(pagename)+1 < segname_len){
	    strcpy(segname,pagename);
	    return 0;
	}
	/* segname wasn't big enough */
	return -2;
    }
    return r;				// some other error
}

static int raw_read(AFFILE *af, unsigned char *buf, uint64_t pos,size_t count)
{
    struct raw_private *rp = RAW_PRIVATE(af);
    if(fseeko(rp->raw, pos, SEEK_SET) < 0)
        return -1;

    errno = 0;
    count = fread(buf, 1, count, rp->raw);
    return (!count && errno) ? -1 : count;
}

static int raw_write(AFFILE *af, unsigned char *buf, uint64_t pos,size_t count)
{
    struct raw_private *rp = RAW_PRIVATE(af);
    if(fseeko(rp->raw, pos, SEEK_SET) < 0)
        return -1;

    errno = 0;
    count = fwrite(buf, 1, count, rp->raw);
    return (!count && errno) ? -1 : count;
}



struct af_vnode vnode_raw = {
    AF_IDENTIFY_RAW,
    AF_VNODE_TYPE_PRIMITIVE|AF_VNODE_TYPE_RELIABLE|AF_VNODE_NO_SIGNING|AF_VNODE_NO_SEALING,
    "Raw",
    raw_identify_file,
    raw_open,
    raw_close,
    raw_vstat,
    raw_get_seg,			// get seg
    raw_get_next_seg,			// get_next_seg
    raw_rewind_seg,			// rewind_seg
    raw_update_seg,			// update_seg
    0,					// del_seg
    raw_read,				// read
    raw_write				// write
};