1    	// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2    	// vim: ts=8 sw=2 smarttab
3    	/*
4    	 * Ceph - scalable distributed file system
5    	 *
6    	 * Copyright (C) 2011 New Dream Network
7    	 *
8    	 * This is free software; you can redistribute it and/or
9    	 * modify it under the terms of the GNU Lesser General Public
10   	 * License version 2.1, as published by the Free Software
11   	 * Foundation.  See file COPYING.
12   	 *
13   	 */
14   	
15   	#include "common/safe_io.h"
16   	#include "include/compat.h"
17   	
18   	#include <stdio.h>
19   	#include <string.h>
20   	#include <unistd.h>
21   	#include <errno.h>
22   	#include <fcntl.h>
23   	#include <limits.h>
24   	
25   	ssize_t safe_read(int fd, void *buf, size_t count)
26   	{
27   		size_t cnt = 0;
28   	
(1) Event cond_true: Condition "cnt < count", taking true branch.
(6) Event loop_begin: Jumped back to beginning of loop.
(7) Event cond_true: Condition "cnt < count", taking true branch.
29   		while (cnt < count) {
(2) Event tainted_data_argument: Calling function "read" taints parameter "*buf".
30   			ssize_t r = read(fd, buf, count - cnt);
(3) Event cond_false: Condition "r <= 0", taking false branch.
(8) Event cond_true: Condition "r <= 0", taking true branch.
31   			if (r <= 0) {
(9) Event cond_true: Condition "r == 0", taking true branch.
32   				if (r == 0) {
33   					// EOF
34   					return cnt;
35   				}
36   				if (errno == EINTR)
37   					continue;
38   				return -errno;
(4) Event if_end: End of if statement.
39   			}
40   			cnt += r;
41   			buf = (char *)buf + r;
(5) Event loop: Jumping back to the beginning of the loop.
42   		}
43   		return cnt;
44   	}
45   	
46   	ssize_t safe_read_exact(int fd, void *buf, size_t count)
47   	{
48   	        ssize_t ret = safe_read(fd, buf, count);
49   		if (ret < 0)
50   			return ret;
51   		if ((size_t)ret != count)
52   			return -EDOM;
53   		return 0;
54   	}
55   	 
56   	ssize_t safe_write(int fd, const void *buf, size_t count)
57   	{
58   		while (count > 0) {
59   			ssize_t r = write(fd, buf, count);
60   			if (r < 0) {
61   				if (errno == EINTR)
62   					continue;
63   				return -errno;
64   			}
65   			count -= r;
66   			buf = (char *)buf + r;
67   		}
68   		return 0;
69   	}
70   	
71   	ssize_t safe_pread(int fd, void *buf, size_t count, off_t offset)
72   	{
73   		size_t cnt = 0;
74   		char *b = (char*)buf;
75   	
76   		while (cnt < count) {
77   			ssize_t r = pread(fd, b + cnt, count - cnt, offset + cnt);
78   			if (r <= 0) {
79   				if (r == 0) {
80   					// EOF
81   					return cnt;
82   				}
83   				if (errno == EINTR)
84   					continue;
85   				return -errno;
86   			}
87   	
88   			cnt += r;
89   		}
90   		return cnt;
91   	}
92   	
93   	ssize_t safe_pread_exact(int fd, void *buf, size_t count, off_t offset)
94   	{
95   		ssize_t ret = safe_pread(fd, buf, count, offset);
96   		if (ret < 0)
97   			return ret;
98   		if ((size_t)ret != count)
99   			return -EDOM;
100  		return 0;
101  	}
102  	
103  	ssize_t safe_pwrite(int fd, const void *buf, size_t count, off_t offset)
104  	{
105  		while (count > 0) {
106  			ssize_t r = pwrite(fd, buf, count, offset);
107  			if (r < 0) {
108  				if (errno == EINTR)
109  					continue;
110  				return -errno;
111  			}
112  			count -= r;
113  			buf = (char *)buf + r;
114  			offset += r;
115  		}
116  		return 0;
117  	}
118  	
119  	#ifdef CEPH_HAVE_SPLICE
120  	ssize_t safe_splice(int fd_in, off_t *off_in, int fd_out, off_t *off_out,
121  			    size_t len, unsigned int flags)
122  	{
123  	  size_t cnt = 0;
124  	
125  	  while (cnt < len) {
126  	    ssize_t r = splice(fd_in, off_in, fd_out, off_out, len - cnt, flags);
127  	    if (r <= 0) {
128  	      if (r == 0) {
129  		// EOF
130  		return cnt;
131  	      }
132  	      if (errno == EINTR)
133  		continue;
134  	      if (errno == EAGAIN)
135  		break;
136  	      return -errno;
137  	    }
138  	    cnt += r;
139  	  }
140  	  return cnt;
141  	}
142  	
143  	ssize_t safe_splice_exact(int fd_in, off_t *off_in, int fd_out,
144  				  off_t *off_out, size_t len, unsigned int flags)
145  	{
146  	  ssize_t ret = safe_splice(fd_in, off_in, fd_out, off_out, len, flags);
147  	  if (ret < 0)
148  	    return ret;
149  	  if ((size_t)ret != len)
150  	    return -EDOM;
151  	  return 0;
152  	}
153  	#endif
154  	
155  	int safe_write_file(const char *base, const char *file,
156  			    const char *val, size_t vallen,
157  			    unsigned mode)
158  	{
159  	  int ret;
160  	  char fn[PATH_MAX];
161  	  char tmp[PATH_MAX];
162  	  int fd;
163  	
164  	  // does the file already have correct content?
165  	  char oldval[80];
166  	  ret = safe_read_file(base, file, oldval, sizeof(oldval));
167  	  if (ret == (int)vallen && memcmp(oldval, val, vallen) == 0)
168  	    return 0;  // yes.
169  	
170  	  snprintf(fn, sizeof(fn), "%s/%s", base, file);
171  	  snprintf(tmp, sizeof(tmp), "%s/%s.tmp", base, file);
172  	  fd = open(tmp, O_WRONLY|O_CREAT|O_TRUNC, mode);
173  	  if (fd < 0) {
174  	    ret = errno;
175  	    return -ret;
176  	  }
177  	  ret = safe_write(fd, val, vallen);
178  	  if (ret) {
179  	    VOID_TEMP_FAILURE_RETRY(close(fd));
180  	    return ret;
181  	  }
182  	
183  	  ret = fsync(fd);
184  	  if (ret < 0) ret = -errno;
185  	  VOID_TEMP_FAILURE_RETRY(close(fd));
186  	  if (ret < 0) {
187  	    unlink(tmp);
188  	    return ret;
189  	  }
190  	  ret = rename(tmp, fn);
191  	  if (ret < 0) {
192  	    ret = -errno;
193  	    unlink(tmp);
194  	    return ret;
195  	  }
196  	
197  	  fd = open(base, O_RDONLY);
198  	  if (fd < 0) {
199  	    ret = -errno;
200  	    return ret;
201  	  }
202  	  ret = fsync(fd);
203  	  if (ret < 0) ret = -errno;
204  	  VOID_TEMP_FAILURE_RETRY(close(fd));
205  	
206  	  return ret;
207  	}
208  	
209  	int safe_read_file(const char *base, const char *file,
210  			   char *val, size_t vallen)
211  	{
212  	  char fn[PATH_MAX];
213  	  int fd, len;
214  	
215  	  snprintf(fn, sizeof(fn), "%s/%s", base, file);
216  	  fd = open(fn, O_RDONLY);
217  	  if (fd < 0) {
218  	    return -errno;
219  	  }
220  	  len = safe_read(fd, val, vallen);
221  	  if (len < 0) {
222  	    VOID_TEMP_FAILURE_RETRY(close(fd));
223  	    return len;
224  	  }
225  	  // close sometimes returns errors, but only after write()
226  	  VOID_TEMP_FAILURE_RETRY(close(fd));
227  	
228  	  return len;
229  	}
230