(root)/
util-linux-2.39/
libblkid/
src/
save.c
       1  /*
       2   * save.c - write the cache struct to disk
       3   *
       4   * Copyright (C) 2001 by Andreas Dilger
       5   * Copyright (C) 2003 Theodore Ts'o
       6   *
       7   * %Begin-Header%
       8   * This file may be redistributed under the terms of the
       9   * GNU Lesser General Public License.
      10   * %End-Header%
      11   */
      12  
      13  #include <stdio.h>
      14  #include <string.h>
      15  #include <stdlib.h>
      16  #include <unistd.h>
      17  #include <sys/types.h>
      18  #ifdef HAVE_SYS_STAT_H
      19  #include <sys/stat.h>
      20  #endif
      21  #ifdef HAVE_ERRNO_H
      22  #include <errno.h>
      23  #endif
      24  
      25  #include "closestream.h"
      26  #include "fileutils.h"
      27  
      28  #include "blkidP.h"
      29  
      30  
      31  static void save_quoted(const char *data, FILE *file)
      32  {
      33  	const char *p;
      34  
      35  	fputc('"', file);
      36  	for (p = data; p && *p; p++) {
      37  		if ((unsigned char) *p == 0x22 ||		/* " */
      38  		    (unsigned char) *p == 0x5c)			/* \ */
      39  			fputc('\\', file);
      40  
      41  		fputc(*p, file);
      42  	}
      43  	fputc('"', file);
      44  }
      45  static int save_dev(blkid_dev dev, FILE *file)
      46  {
      47  	struct list_head *p;
      48  
      49  	if (!dev || dev->bid_name[0] != '/')
      50  		return 0;
      51  
      52  	DBG(SAVE, ul_debug("device %s, type %s", dev->bid_name, dev->bid_type ?
      53  		   dev->bid_type : "(null)"));
      54  
      55  	fprintf(file, "<device DEVNO=\"0x%04lx\" TIME=\"%lld.%lld\"",
      56  			(unsigned long) dev->bid_devno,
      57  			(long long) dev->bid_time,
      58  			(long long) dev->bid_utime);
      59  
      60  	if (dev->bid_pri)
      61  		fprintf(file, " PRI=\"%d\"", dev->bid_pri);
      62  
      63  	list_for_each(p, &dev->bid_tags) {
      64  		blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags);
      65  
      66  		fputc(' ', file);			/* space between tags */
      67  		fputs(tag->bit_name, file);		/* tag NAME */
      68  		fputc('=', file);			/* separator between NAME and VALUE */
      69  		save_quoted(tag->bit_val, file);	/* tag "VALUE" */
      70  	}
      71  	fprintf(file, ">%s</device>\n", dev->bid_name);
      72  
      73  	return 0;
      74  }
      75  
      76  /*
      77   * Write out the cache struct to the cache file on disk.
      78   */
      79  int blkid_flush_cache(blkid_cache cache)
      80  {
      81  	struct list_head *p;
      82  	char *tmp = NULL;
      83  	char *opened = NULL;
      84  	char *filename;
      85  	FILE *file = NULL;
      86  	int fd, ret = 0;
      87  	struct stat st;
      88  
      89  	if (list_empty(&cache->bic_devs) ||
      90  	    !(cache->bic_flags & BLKID_BIC_FL_CHANGED)) {
      91  		DBG(SAVE, ul_debug("skipping cache file write"));
      92  		return 0;
      93  	}
      94  
      95  	filename = cache->bic_filename ? cache->bic_filename :
      96  					 blkid_get_cache_filename(NULL);
      97  	if (!filename)
      98  		return -BLKID_ERR_PARAM;
      99  
     100  	if (strncmp(filename,
     101  	    BLKID_RUNTIME_DIR "/", sizeof(BLKID_RUNTIME_DIR)) == 0) {
     102  
     103  		/* default destination, create the directory if necessary */
     104  		if (stat(BLKID_RUNTIME_DIR, &st)
     105  		    && errno == ENOENT
     106  		    && mkdir(BLKID_RUNTIME_DIR, S_IWUSR|
     107  						S_IRUSR|S_IRGRP|S_IROTH|
     108  						S_IXUSR|S_IXGRP|S_IXOTH) != 0
     109  		    && errno != EEXIST) {
     110  			DBG(SAVE, ul_debug("can't create %s directory for cache file",
     111  					BLKID_RUNTIME_DIR));
     112  			return 0;
     113  		}
     114  	}
     115  
     116  	/* If we can't write to the cache file, then don't even try */
     117  	if (((ret = stat(filename, &st)) < 0 && errno != ENOENT) ||
     118  	    (ret == 0 && access(filename, W_OK) < 0)) {
     119  		DBG(SAVE, ul_debug("can't write to cache file %s", filename));
     120  		return 0;
     121  	}
     122  
     123  	/*
     124  	 * Try and create a temporary file in the same directory so
     125  	 * that in case of error we don't overwrite the cache file.
     126  	 * If the cache file doesn't yet exist, it isn't a regular
     127  	 * file (e.g. /dev/null or a socket), or we couldn't create
     128  	 * a temporary file then we open it directly.
     129  	 */
     130  	if (ret == 0 && S_ISREG(st.st_mode)) {
     131  		size_t len = strlen(filename) + 8;
     132  		tmp = malloc(len);
     133  		if (tmp) {
     134  			snprintf(tmp, len, "%s-XXXXXX", filename);
     135  			fd = mkstemp_cloexec(tmp);
     136  			if (fd >= 0) {
     137  				if (fchmod(fd, 0644) != 0)
     138  					DBG(SAVE, ul_debug("%s: fchmod failed", filename));
     139  				else if ((file = fdopen(fd, "w" UL_CLOEXECSTR)))
     140  					opened = tmp;
     141  				if (!file)
     142  					close(fd);
     143  			}
     144  		}
     145  	}
     146  
     147  	if (!file) {
     148  		file = fopen(filename, "w" UL_CLOEXECSTR);
     149  		opened = filename;
     150  	}
     151  
     152  	DBG(SAVE, ul_debug("writing cache file %s (really %s)",
     153  		   filename, opened));
     154  
     155  	if (!file) {
     156  		ret = errno;
     157  		goto errout;
     158  	}
     159  
     160  	list_for_each(p, &cache->bic_devs) {
     161  		blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs);
     162  		if (!dev->bid_type || (dev->bid_flags & BLKID_BID_FL_REMOVABLE))
     163  			continue;
     164  		if ((ret = save_dev(dev, file)) < 0)
     165  			break;
     166  	}
     167  
     168  	if (ret >= 0) {
     169  		cache->bic_flags &= ~BLKID_BIC_FL_CHANGED;
     170  		ret = 1;
     171  	}
     172  
     173  	if (close_stream(file) != 0)
     174  		DBG(SAVE, ul_debug("write failed: %s", filename));
     175  
     176  	if (opened != filename) {
     177  		if (ret < 0) {
     178  			unlink(opened);
     179  			DBG(SAVE, ul_debug("unlinked temp cache %s", opened));
     180  		} else {
     181  			char *backup;
     182  			size_t len = strlen(filename) + 5;
     183  
     184  			backup = malloc(len);
     185  			if (backup) {
     186  				snprintf(backup, len, "%s.old", filename);
     187  				unlink(backup);
     188  				if (link(filename, backup)) {
     189  					DBG(SAVE, ul_debug("can't link %s to %s",
     190  							filename, backup));
     191  				}
     192  				free(backup);
     193  			}
     194  			if (rename(opened, filename)) {
     195  				ret = errno;
     196  				DBG(SAVE, ul_debug("can't rename %s to %s",
     197  						opened, filename));
     198  			} else {
     199  				DBG(SAVE, ul_debug("moved temp cache %s", opened));
     200  			}
     201  		}
     202  	}
     203  
     204  errout:
     205  	free(tmp);
     206  	if (filename != cache->bic_filename)
     207  		free(filename);
     208  	return ret;
     209  }
     210  
     211  #ifdef TEST_PROGRAM
     212  int main(int argc, char **argv)
     213  {
     214  	blkid_cache cache = NULL;
     215  	int ret;
     216  
     217  	blkid_init_debug(BLKID_DEBUG_ALL);
     218  	if (argc != 2) {
     219  		fprintf(stderr, "Usage: %s [filename]\n"
     220  			"Test loading/saving a cache (filename)\n", argv[0]);
     221  		exit(1);
     222  	}
     223  
     224  	if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) {
     225  		fprintf(stderr, "%s: error creating cache (%d)\n",
     226  			argv[0], ret);
     227  		exit(1);
     228  	}
     229  	if ((ret = blkid_probe_all(cache)) < 0) {
     230  		fprintf(stderr, "error (%d) probing devices\n", ret);
     231  		exit(1);
     232  	}
     233  	cache->bic_filename = strdup(argv[1]);
     234  
     235  	if ((ret = blkid_flush_cache(cache)) < 0) {
     236  		fprintf(stderr, "error (%d) saving cache\n", ret);
     237  		exit(1);
     238  	}
     239  
     240  	blkid_put_cache(cache);
     241  
     242  	return ret;
     243  }
     244  #endif