File: cdw_graftpoints.c

package info (click to toggle)
cdw 0.7.1-1
  • links: PTS
  • area: main
  • in suites: wheezy
  • size: 3,952 kB
  • sloc: ansic: 31,667; sh: 1,496; makefile: 274
file content (411 lines) | stat: -rw-r--r-- 12,759 bytes parent folder | download
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
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
/* cdw
 * Copyright (C) 2002 Varkonyi Balazs
 * Copyright (C) 2007 - 2012 Kamil Ignacak
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */


/**
   \file cdw_graftpoints.c
   \brief Implements access to graftpoints file

   File implements function that are wrappers around graftpoints file
   descriptor: creating (with random file name), writing and closing.

   Graftpoints file is used to tell mkisofs which files to use to
   create ISO image, and to create desired directory structure in ISO file.
*/

#define _BSD_SOURCE /* fsync() */

#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h> /* current time helps creating seed for srand() */
#include <errno.h>

/* open() */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "cdw_graftpoints.h"
#include "cdw_string.h"
#include "cdw_file.h"
#include "cdw_fs.h"
#include "cdw_debug.h"
#include "cdw_file_manager.h"



static cdw_rv_t cdw_graftpoints_open_file(void);
static cdw_rv_t cdw_graftpoints_close_file(void);
static cdw_rv_t cdw_graftpoints_write_to_file(const char *left, const char *right);
static void set_string_random(char *buffer);

/* value assigned to graftpoints_file when file is closed (it either wasn't
   open yet or was closed by cdw graftpoints functions) */
#define CDW_FILE_CLOSED -2

/** \brief File descriptor of current graftpoints file */
static int graftpoints_file = CDW_FILE_CLOSED;

/** \brief Full path to current file name of graftpoints file */
static char *graftpoints_fullpath = (char *) NULL;

/** \brief Length of part of graftpoints file name that is randomly generated */
#define RANDOM_STRING_LEN 10 /* length does not include trailing '\0' */


/**
   \brief Put content of list of files to temporary file

   Put content of list of files (that are going to be written to iso image)
   to temporary file that will be used by program creating iso image.

   Graft points (see 'man mkisofs') are created here: this function simply
   takes name of last item in full path and puts the name on left side of
   '=' sign. On the right side of the sign a fullpath is printed.

   Use this function only when needed, that is: only when you want to do
   sth with selected files (before writing them to CD or to image).
   Make sure that there are any selected files before calling the function.

   \return CDW_ERROR when some error occurs
   \return CDW_OK on success
*/
cdw_rv_t cdw_graftpoints_create_file(void)
{
	cdw_dll_item_t *f = cdw_file_manager_get_list_of_selected_files(); /* head */
	cdw_assert (f != (cdw_dll_item_t *) NULL, "ERROR: called the function when list of selected files is empty\n")

	cdw_rv_t crv = cdw_graftpoints_open_file();
	if (crv != CDW_OK) {
		/* no need to call cdw_graftpoints_delete_file(),
		   open_file() does not leave mess in case of errors */
		cdw_vdm ("ERROR: failed to open graftpoints file\n");
		return CDW_ERROR;
	}

	size_t i = 0;
	for (; f; f = f->next) {
		cdw_file_t *file = (cdw_file_t *) f->data;
		cdw_assert (file->fullpath != (char *) NULL, "ERROR: file has no fullpath\n");
		cdw_assert (strlen(file->fullpath) != 0, "ERROR: file full path length == 0\n");
		cdw_assert (file->name_start > 0, "ERROR: file has no name\n");
		cdw_assert (strlen(file->fullpath + file->name_start) != 0, "ERROR: file name length == 0\n");

		if (!strcmp(file->fullpath, "/")) {
			crv = cdw_graftpoints_write_to_file("", file->fullpath);
		} else {
			crv = cdw_graftpoints_write_to_file(file->fullpath + file->name_start, file->fullpath);
		}

		i++;

		if (crv == CDW_ERROR) {
			cdw_vdm ("ERROR: failed to properly write line to graftpoints file\n");
			cdw_graftpoints_delete_file();
			return CDW_ERROR;
		}

	}

	cdw_assert (i > 0, "ERROR: there were 0 files on list of selected files\n");

	/* calls fsync, so all written lines are in file for sure */
	crv = cdw_graftpoints_close_file();
	if (crv != CDW_OK) {
		cdw_vdm ("ERROR: failed to properly close graftpoints file\n");
		cdw_graftpoints_delete_file();
		return CDW_ERROR;
	} else {
		return CDW_OK;
	}
}





/**
   \brief Delete graftpoints file correctly created with last call to cdw_graftpoints_create_file()

   Delete graftpoints file that was used for last writing session.
   File should be closed with cdw_graftpoints_close_file() before calling
   this function.

   The function can also be called when you need to clean up resources in
   case of some errors with graftpoints file.

   The function does not return any error values, even if there are some errors.
*/
void cdw_graftpoints_delete_file(void)
{
	if (graftpoints_file != CDW_FILE_CLOSED && graftpoints_file != -1) {
		cdw_vdm ("ERROR: called the function before closing file\n");

		close(graftpoints_file);
		graftpoints_file = CDW_FILE_CLOSED;
	}

	if (graftpoints_fullpath != (char *) NULL) {
		int a = access(graftpoints_fullpath, F_OK);
		if (a != 0) {
			cdw_vdm ("ERROR: access() failed for graftpoints fullpath \"%s\": %s\n", graftpoints_fullpath, strerror(errno));
		} else {
			a = unlink(graftpoints_fullpath);
			if (a != 0) {
				cdw_vdm ("ERROR: unlink() failed for graftpoints fullpath \"%s\": %s\n", graftpoints_fullpath, strerror(errno));
			}
		}
		free(graftpoints_fullpath);
		graftpoints_fullpath = (char *) NULL;
	} else {
		cdw_vdm ("ERROR: graftpoints fullpath is NULL\n");
	}

	return;
}





/**
   \brief A getter for variable with full path to graftpoints file

   \returns full path to graftpoints file (the pointer may be (char *) NULL)
*/
const char *cdw_graftpoints_get_fullpath(void)
{
	cdw_assert (graftpoints_fullpath != (char *) NULL, "graftpoints fullpath is NULL\n");
	int r = access(graftpoints_fullpath, F_OK);
	cdw_assert (r == 0, "graftpoints file does not exist, you don't want a path at this moment\n");
	cdw_assert (graftpoints_file == CDW_FILE_CLOSED, "file should be closed at this point\n");

	return graftpoints_fullpath;
}




/* private functions */




/**
   \brief Create new graftpoints file

   Function opens in tmp directory new file with random name.
   The file can be used as graftpoint file for mkisofs.

   Variable char *graftpoints_fullpath (global in file) is set to
   full path to graftpoint file. It can also be set to (char *) NULL if
   file cannot be open.

   File descriptor of open graftpoints file should be unique - there cannot
   be two open graftpoints at the same time. Therefore this function
   returns CDW_ERROR if some graftpoints file is already open. If such
   situation occurs, then this is a sign of programming error.

   In case of errors graftpoints_fullpath is set to (char *) NULL, and
   graftpoints_file is set to CDW_FILE_CLOSED.

   \return CDW_ERROR on errors
   \return CDW_OK on success
*/
cdw_rv_t cdw_graftpoints_open_file(void)
{
	cdw_assert (graftpoints_file == CDW_FILE_CLOSED, "called the function to open second file\n");
	cdw_assert (graftpoints_fullpath == (char *) NULL,
		    "you forgot to clean up when closing previous file\n");

	const char *tmp_dir = cdw_fs_get_tmp_dir_fullpath();
	if (tmp_dir == (char *) NULL) {
		cdw_vdm ("ERROR: failed to get path to tmp dir\n");
		return CDW_ERROR;
	}

	/* random part of file name, 10 digits + '\0' */
	/* address of the table will be also used as pseudo random string;
	   I know that this is a poor choice, but it will be used only
	   when time() fails, so not too often */
	char random_string[RANDOM_STRING_LEN + 1];

	set_string_random(random_string);
	/* TODO: file name is (more or less) random, now make it safe (think
	   race conditions etc.) */

	graftpoints_fullpath = cdw_string_concat(tmp_dir, "cdw_tmp_graft_", random_string, (char * ) NULL);
	if (graftpoints_fullpath == (char *) NULL) {
		cdw_vdm ("ERROR: failed to create path to graftpoint file\n");
		return CDW_ERROR;
	}

	graftpoints_file = open(graftpoints_fullpath, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
	if (graftpoints_file == -1) {
		cdw_vdm ("ERROR: failed to open graftpoints file\n");

		free(graftpoints_fullpath);
		graftpoints_fullpath = (char *) NULL;
		graftpoints_file = CDW_FILE_CLOSED;

		return CDW_ERROR;
	} else {
		return CDW_OK;
	}
}





/**
   \brief Close current graftpoints file

   Close file descriptor of unique graftpoints file.
   File name buffer is not affected. On successful closing of file flag
   is set to indicate that file is closed.

   \return CDW_OK on success
   \return CDW_ERROR if fsync() or close() fail
*/
cdw_rv_t cdw_graftpoints_close_file(void)
{
	int rv1 = fsync(graftpoints_file);
	if (rv1 != 0) {
		cdw_vdm ("ERROR: fsync() failed, errno: \"%s\"\n", strerror(errno));
		cdw_assert (rv1 == 0, "ERROR: fsync() returned %d\n", rv1);
	}

	int rv2 = close(graftpoints_file);
	if (rv2 != 0) {
		cdw_vdm ("ERROR: close() failed, errno: \"%s\"\n", strerror(errno));
		cdw_assert (rv2 == 0, "ERROR: close() returned %d\n", rv2);
	} else {
		graftpoints_file = CDW_FILE_CLOSED;
	}

	/* don't free file name buffer yet, we will use it when
	   unlink()ing old file */

	if (rv1 != 0 || rv2 != 0) {
		return CDW_ERROR;
	} else {
		return CDW_OK;
	}
}





/**
   \brief Write given strings to graftpoints file

   The function writes given strings to graftpoints file adding '\n'
   char at the end of line. The strings are written to file like this:
   left=right
   so if \p left is a name in target ISO filesystem, and \p right
   is path to file existing in local filesystem, then mkisofs
   will recognize such line as a valid specification, a graftpoint.
   See mkisofs man page for details.

   Function checks if graftpoints file is open. It returns error if file
   is not open. Use cdw_graftpoints_file_open() first to open the file.

   \param left - string representing point in created ISO file system
   \param right - full path to file existing in local file system

   \return CDW_ERROR if writing to file fails
   \return CDW_OK if success
*/
cdw_rv_t cdw_graftpoints_write_to_file(const char *left, const char *right)
{
	cdw_assert (graftpoints_file != CDW_FILE_CLOSED, "trying to write before opening file\n");
	cdw_assert (left != (char *) NULL && right != (char *) NULL,
		    "ERROR: function called with null argument(s)\n");
	cdw_assert (left[0] != '/', "ERROR: left side of graftpoint starts with '/'\n");

#ifndef NDEBUG /* for testing purposes only */
	size_t llen = strlen(left);
	if (left[llen] == '/') {
		size_t rlen = strlen(right);
		cdw_assert (right[rlen] == '/', "ERROR: left side of graftpoint ends with slash, but right one does not\n");
	} else {
		size_t rlen = strlen(right);
		cdw_assert (right[rlen] != '/', "ERROR: left side of graftpoint doesn't end with slash, but right one does\n");
	}
#endif

	cdw_vdm ("INFO: writing graft point \"%s=%s\"\n", left, right);
	ssize_t rv1 = write(graftpoints_file, left, strlen(left));
	ssize_t rv2 = write(graftpoints_file, "=", strlen("="));
	ssize_t rv3 = write(graftpoints_file, right, strlen(right));
	ssize_t rv4 = write(graftpoints_file, "\n", strlen("\n"));

	if (rv1 == -1 || rv2 == -1 || rv3 == -1 || rv4 == -1) {
		return CDW_ERROR;
	} else {
		fsync(graftpoints_file);
		return CDW_OK;
	}
}





/**
   \brief Set content of current buffer to some random value

   This function sets content of buffer of known size
   (RANDOM_STRING_LEN + 1 for ending '\0') with random readable chars.
   The function is used to prepare random file name.

   It is a string utility, but very specialized, so it won't be moved to
   utils_string.c

   \param buffer - buffer that you want to initialize with random chars
*/
void set_string_random(char *buffer)
{
	time_t t = time((time_t *) NULL);
	static unsigned int seed = 31601; /* some prime number */
	if (t == (time_t) -1) {
		/* pseudo randomness */
		seed = (unsigned int) ((((size_t) buffer) * seed) >> 16);
	} else {
		seed = (unsigned int) t;
	}
	srand(seed);

	int rand1 = rand();
	int rand2 = rand();

	/* 32767 is minimal value of RAND_MAX required by C standard
	   so we are sure to get 5 random digits */
	snprintf(buffer, RANDOM_STRING_LEN + 1, "%05d%05d", rand1 % 32767, rand2 % 32767);

	return;
}