large-io.c 6.62 KB
Newer Older
1
/*  dvdisaster: Additional error correction for optical media.
2
 *  Copyright (C) 2004-2017 Carsten Gnoerlich.
3
 *
4 5 6 7 8 9
 *  Email: carsten@dvdisaster.org  -or-  cgnoerlich@fsfe.org
 *  Project homepage: http://www.dvdisaster.org
 *
 *  This file is part of dvdisaster.
 *
 *  dvdisaster is free software: you can redistribute it and/or modify
10
 *  it under the terms of the GNU General Public License as published by
11
 *  the Free Software Foundation, either version 3 of the License, or
12 13
 *  (at your option) any later version.
 *
14
 *  dvdisaster is distributed in the hope that it will be useful,
15 16 17 18 19
 *  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
20
 *  along with dvdisaster. If not, see <http://www.gnu.org/licenses/>.
21 22 23 24 25 26 27
 */

#include "dvdisaster.h"

/***
 *** Wrappers around the standard low level file system interface.
 ***
28 29
 * This is pointless for GNU/Linux, but gives us the possibility to 
 * hide differences in OS-specific semantics.
30 31 32 33 34 35
 *
 * Note the different return value semantics from standard functions:
 * - LargeOpen() returns a LargeFile pointer on success and NULL otherwise;
 * - LargeRead() and LargeWrite() return the number of bytes read/written;
 * - the remaining functions return True on success or False on failure.
 *
36
 * Also, individual behaviour may deviate from standard functions. 
37 38 39 40 41 42 43
 */

/*
 * convert special chars in file names to correct OS encoding
 */

static gchar* os_path(char *path_in)
44
{  gchar *cp_path = g_filename_from_utf8(path_in, -1, NULL, NULL, NULL);
45 46 47 48 49 50 51 52 53 54 55 56 57 58

   if(cp_path == NULL)
   {  errno = EINVAL;
      return NULL;
   }
   
   REMEMBER(cp_path);
   return cp_path;
}

/*
 * Large stat replacement (queries only file size)
 */

59
int LargeStat(char *path, guint64 *length_return)
60
{  struct stat mystat;
61
   gchar *cp_path = os_path(path);
62

63
   if(!cp_path) return FALSE;
64

65 66 67
   if(stat(cp_path, &mystat) == -1)
   {  g_free(cp_path);
      return FALSE;
68
   }
69
   g_free(cp_path);
70

71 72
   if(!S_ISREG(mystat.st_mode))
      return FALSE;
73

74
   *length_return = mystat.st_size;
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
   return TRUE;
}

/*
 * Stat() variant for testing directories
 */

int DirStat(char *path)
{  struct stat mystat;
   gchar *cp_path = os_path(path);

   if(!cp_path) return FALSE;

   if(stat(cp_path, &mystat) == -1)
   {  g_free(cp_path);
      return FALSE;
   }
   g_free(cp_path);

   if(!S_ISDIR(mystat.st_mode))
     return FALSE;

   return TRUE;
}

/*
 * Open a file
 */

LargeFile* LargeOpen(char *name, int flags, mode_t mode)
{  LargeFile *lf = g_malloc0(sizeof(LargeFile));
   struct stat mystat;
   gchar *cp_path;

#ifdef HAVE_O_LARGEFILE
   flags |= O_LARGEFILE;
#endif

113 114 115
   cp_path = os_path(name);
   if(!cp_path) 
   {  g_free(lf); return FALSE;
116 117
   }

118
   /* Do not try to open directories etc. */
119 120 121

   if(    (stat(cp_path, &mystat) == 0)
       && !S_ISREG(mystat.st_mode))
122
   {  g_free(cp_path), g_free(lf); return NULL;
123
   }
124 125

   lf->fileHandle = open(cp_path, flags, mode);
126
   g_free(cp_path);
127 128

   if(lf->fileHandle == -1)
129 130 131
   {  g_free(lf); return NULL;
   }

132 133
   lf->path = g_strdup(name);
   LargeStat(name, &lf->size);  /* Do NOT use cp_path! */
134

135
   lf->flags = flags;
136 137 138 139 140 141 142 143
   return lf;
}

/*
 * Seeking in large files.
 * Note: Seeking beyond the end of a split file is undefined.
 */

144
int LargeSeek(LargeFile *lf, off_t pos)
145
{  
146 147 148
   lf->offset = pos;
   if(lseek(lf->fileHandle, pos, SEEK_SET) != pos)
      return FALSE;
149 150 151 152 153 154 155 156 157 158 159

   return TRUE;
}

/*
 * EOF predicate for large files.
 *
 * Note: Works only correctly for read only files!
 */

int LargeEOF(LargeFile *lf)
160 161
{  
   return lf->offset >= lf->size;
162 163 164
}

/*
165
 * Reading large files
166 167 168 169 170
 */

ssize_t LargeRead(LargeFile *lf, void *buf, size_t count)
{  ssize_t n;

171 172
   n = read(lf->fileHandle, buf, count);
   lf->offset += n;
173

174
   return n;
175 176 177
}

/*
178
 * Writing large files
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
 */

static void insert_buttons(GtkDialog *dialog)
{  
  gtk_dialog_add_buttons(dialog, 
			 GTK_STOCK_REDO , 1,
			 GTK_STOCK_CANCEL, 0, NULL);
} 

static ssize_t xwrite(int fdes, void *buf_base, size_t count)
{  unsigned char *buf = (unsigned char*)buf_base;
   ssize_t total = 0;

   /* Simply fail when going out of space in command line mode */

   if(!Closure->guiMode)
   {  while(count)
      {  ssize_t n = write(fdes, buf, count);
      
	 if(n<=0) return total;  /* error occurred */

	 if(n>0)  /* write at least partially successful */
	 {  total += n;
	    count -= n;
	    buf   += n;
	 }
      }
      return total;
   }

   /* Give the user a chance to free more space in GUI mode.
      When running out of space, the last write() may complete
      with n<count but no error condition, so we try writing
      until a real error hits (n = -1). */

   while(count)
   {  ssize_t n = write(fdes, buf, count);

      if(n <= 0) /* error occurred */
      {  int answer; 

	 if(errno != ENOSPC) return total;

	 answer = ModalDialog(GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, insert_buttons,
			      _("Error while writing the file:\n\n%s\n\n"
				"You can redo this operation after freeing some space."),
			      strerror(errno),n);

	 if(!answer) return total; 
      }

      if(n>0)  /* write at least partially successful */
      {  total += n;
	 count -= n;
	 buf   += n;
      }
   }

   return total;
}

ssize_t LargeWrite(LargeFile *lf, void *buf, size_t count)
{  ssize_t n;

243 244
   n = xwrite(lf->fileHandle, buf, count);
   lf->offset += n;
245

246
   return n;
247 248 249 250 251 252 253 254 255
}

/*
 * Large file closing
 */

int LargeClose(LargeFile *lf)
{  int result = TRUE;

256
   result = (close(lf->fileHandle) == 0);
257 258 259

   /* Free the LargeFile struct and return results */

260
   if(lf->path) g_free(lf->path);
261 262 263 264 265 266 267 268 269
   g_free(lf);

   return result;
}

/*
 * Large file truncation
 */

270 271
int LargeTruncate(LargeFile *lf, off_t length)
{  int result;
272

273
   result = (ftruncate(lf->fileHandle, length) == 0);
274

275 276
   if(result)
     lf->size = length;
277

278
   return result;
279 280 281 282 283 284 285
}

/*
 * Large file unlinking
 */

int LargeUnlink(char *path)
286 287
{  gchar *cp_path;
   int result;
288

289 290
   cp_path = os_path(path);
   if(!cp_path) return FALSE;
291

292 293
   result = unlink(cp_path);
   g_free(cp_path);
294

295
   return result == 0;
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
}

/***
 *** Wrappers around other IO
 ***/

FILE *portable_fopen(char *path, char *modes)
{  FILE *file;
   char *cp_path;

   cp_path = os_path(path);
   file = fopen(cp_path, modes);
   g_free(cp_path);

   return file;
}

313 314 315
/***
 *** Convenience functions
 ***/
316

317 318 319 320 321 322 323 324 325
/*
 * Append the given file suffix if none is already present
 */

char *ApplyAutoSuffix(char *filename, char *suffix)
{  char *out;

   if(!filename || !*filename || strrchr(filename, '.')) 
     return filename;
326

327 328 329 330
   out = g_strdup_printf("%s.%s",filename,suffix);
   g_free(filename);
   
   return out;
331
}