tools: env: Implement atomic replace for filesystem
If the U-Boot environment is stored in a regular file and redundant operation isn't set, then write to a temporary file and perform an atomic rename. Signed-off-by: Alex Kiernan <alex.kiernan@gmail.com>
This commit is contained in:
		
							parent
							
								
									899b5338fa
								
							
						
					
					
						commit
						dbc3432379
					
				|  | @ -14,6 +14,7 @@ | ||||||
| #include <errno.h> | #include <errno.h> | ||||||
| #include <env_flags.h> | #include <env_flags.h> | ||||||
| #include <fcntl.h> | #include <fcntl.h> | ||||||
|  | #include <libgen.h> | ||||||
| #include <linux/fs.h> | #include <linux/fs.h> | ||||||
| #include <linux/stringify.h> | #include <linux/stringify.h> | ||||||
| #include <ctype.h> | #include <ctype.h> | ||||||
|  | @ -1225,9 +1226,48 @@ static int flash_read(int fd) | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int flash_open_tempfile(const char **dname, const char **target_temp) | ||||||
|  | { | ||||||
|  | 	char *dup_name = strdup(DEVNAME(dev_current)); | ||||||
|  | 	char *temp_name = NULL; | ||||||
|  | 	int rc = -1; | ||||||
|  | 
 | ||||||
|  | 	if (!dup_name) | ||||||
|  | 		return -1; | ||||||
|  | 
 | ||||||
|  | 	*dname = dirname(dup_name); | ||||||
|  | 	if (!*dname) | ||||||
|  | 		goto err; | ||||||
|  | 
 | ||||||
|  | 	rc = asprintf(&temp_name, "%s/XXXXXX", *dname); | ||||||
|  | 	if (rc == -1) | ||||||
|  | 		goto err; | ||||||
|  | 
 | ||||||
|  | 	rc = mkstemp(temp_name); | ||||||
|  | 	if (rc == -1) { | ||||||
|  | 		/* fall back to in place write */ | ||||||
|  | 		fprintf(stderr, | ||||||
|  | 			"Can't create %s: %s\n", temp_name, strerror(errno)); | ||||||
|  | 		free(temp_name); | ||||||
|  | 	} else { | ||||||
|  | 		*target_temp = temp_name; | ||||||
|  | 		/* deliberately leak dup_name as dname /might/ point into
 | ||||||
|  | 		 * it and we need it for our caller | ||||||
|  | 		 */ | ||||||
|  | 		dup_name = NULL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | err: | ||||||
|  | 	if (dup_name) | ||||||
|  | 		free(dup_name); | ||||||
|  | 
 | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int flash_io_write(int fd_current) | static int flash_io_write(int fd_current) | ||||||
| { | { | ||||||
| 	int fd_target, rc, dev_target; | 	int fd_target = -1, rc, dev_target; | ||||||
|  | 	const char *dname, *target_temp = NULL; | ||||||
| 
 | 
 | ||||||
| 	if (have_redund_env) { | 	if (have_redund_env) { | ||||||
| 		/* switch to next partition for writing */ | 		/* switch to next partition for writing */ | ||||||
|  | @ -1242,8 +1282,17 @@ static int flash_io_write(int fd_current) | ||||||
| 			goto exit; | 			goto exit; | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
|  | 		struct stat sb; | ||||||
|  | 
 | ||||||
|  | 		if (fstat(fd_current, &sb) == 0 && S_ISREG(sb.st_mode)) { | ||||||
|  | 			/* if any part of flash_open_tempfile() fails we fall
 | ||||||
|  | 			 * back to in-place writes | ||||||
|  | 			 */ | ||||||
|  | 			fd_target = flash_open_tempfile(&dname, &target_temp); | ||||||
|  | 		} | ||||||
| 		dev_target = dev_current; | 		dev_target = dev_current; | ||||||
| 		fd_target = fd_current; | 		if (fd_target == -1) | ||||||
|  | 			fd_target = fd_current; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	rc = flash_write(fd_current, fd_target, dev_target); | 	rc = flash_write(fd_current, fd_target, dev_target); | ||||||
|  | @ -1254,7 +1303,7 @@ static int flash_io_write(int fd_current) | ||||||
| 			DEVNAME(dev_current), strerror(errno)); | 			DEVNAME(dev_current), strerror(errno)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (have_redund_env) { | 	if (fd_current != fd_target) { | ||||||
| 		if (fsync(fd_target) && | 		if (fsync(fd_target) && | ||||||
| 		    !(errno == EINVAL || errno == EROFS)) { | 		    !(errno == EINVAL || errno == EROFS)) { | ||||||
| 			fprintf(stderr, | 			fprintf(stderr, | ||||||
|  | @ -1268,6 +1317,34 @@ static int flash_io_write(int fd_current) | ||||||
| 				DEVNAME(dev_target), strerror(errno)); | 				DEVNAME(dev_target), strerror(errno)); | ||||||
| 			rc = -1; | 			rc = -1; | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 		if (target_temp) { | ||||||
|  | 			int dir_fd; | ||||||
|  | 
 | ||||||
|  | 			dir_fd = open(dname, O_DIRECTORY | O_RDONLY); | ||||||
|  | 			if (dir_fd == -1) | ||||||
|  | 				fprintf(stderr, | ||||||
|  | 					"Can't open %s: %s\n", | ||||||
|  | 					dname, strerror(errno)); | ||||||
|  | 
 | ||||||
|  | 			if (rename(target_temp, DEVNAME(dev_target))) { | ||||||
|  | 				fprintf(stderr, | ||||||
|  | 					"rename failed %s => %s: %s\n", | ||||||
|  | 					target_temp, DEVNAME(dev_target), | ||||||
|  | 					strerror(errno)); | ||||||
|  | 				rc = -1; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (dir_fd != -1 && fsync(dir_fd)) | ||||||
|  | 				fprintf(stderr, | ||||||
|  | 					"fsync failed on %s: %s\n", | ||||||
|  | 					dname, strerror(errno)); | ||||||
|  | 
 | ||||||
|  | 			if (dir_fd != -1 && close(dir_fd)) | ||||||
|  | 				fprintf(stderr, | ||||||
|  | 					"I/O error on %s: %s\n", | ||||||
|  | 					dname, strerror(errno)); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  exit: |  exit: | ||||||
| 	return rc; | 	return rc; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue