2929 * From FreeBSD fetch(8):
3030 * $FreeBSD: src/usr.bin/fetch/fetch.c,v 1.84.2.1 2009/08/03 08:13:06 kensmith Exp $
3131 */
32+ #include <sys/file.h>
3233#include <sys/param.h>
3334#include <sys/stat.h>
3435#include <sys/time.h>
@@ -94,19 +95,64 @@ xbps_fetch_error_string(void)
9495 return fetchLastErrString ;
9596}
9697
98+ static int
99+ acquire_lock (char * lockfile , size_t lockfilesz , const char * filename )
100+ {
101+ int fd ;
102+ int r ;
103+
104+ r = snprintf (lockfile , lockfilesz , "%s.lock" , filename );
105+ if (r < 0 || (size_t )r >= lockfilesz ) {
106+ errno = ENAMETOOLONG ;
107+ return -1 ;
108+ }
109+
110+ fd = open (lockfile , O_RDWR |O_CREAT |O_CLOEXEC , 0600 );
111+ if (fd == -1 )
112+ return - errno ;
113+
114+ if (flock (fd , LOCK_EX |LOCK_NB ) == -1 ) {
115+ if (errno == EWOULDBLOCK )
116+ xbps_warn_printf (
117+ "%s: file locked, waiting...\n" , filename );
118+ if (errno != EWOULDBLOCK || flock (fd , LOCK_EX ) == -1 ) {
119+ r = - errno ;
120+ (void ) close (fd );
121+ return r ;
122+ }
123+ }
124+
125+ return fd ;
126+ }
127+
128+ static void
129+ release_lock (const char * lockfile , int lockfd )
130+ {
131+ (void ) close (lockfd );
132+ if (remove (lockfile ) == -1 && errno != ENOENT ) {
133+ xbps_warn_printf ("failed to remove lock file: %s: %s\n" ,
134+ lockfile , strerror (errno ));
135+ }
136+ }
137+
97138int
98- xbps_fetch_file_dest_sha256 (struct xbps_handle * xhp , const char * uri , const char * filename , const char * flags , unsigned char * digest , size_t digestlen )
139+ xbps_fetch_file_dest_sha256 (struct xbps_handle * xhp , const char * uri ,
140+ const char * filename , const char * flags , unsigned char * digest ,
141+ size_t digestlen )
99142{
100- struct stat st , st_tmpfile , * stp ;
143+ char buf [BUFSIZ ];
144+ char tempfile [PATH_MAX ];
145+ char lockfile [PATH_MAX ];
146+ struct stat st = {0 }, st_tmpfile = {0 }, * stp ;
101147 struct url * url = NULL ;
102148 struct url_stat url_st ;
103149 struct fetchIO * fio = NULL ;
104150 struct timespec ts [2 ];
105151 off_t bytes_dload = 0 ;
106152 ssize_t bytes_read = 0 , bytes_written = 0 ;
107- char buf [4096 ], * tempfile = NULL ;
108153 char fetch_flags [8 ];
109154 int fd = -1 , rv = 0 ;
155+ int lockfd = -1 ;
110156 bool refetch = false, restart = false;
111157 SHA256_CTX sha256 ;
112158
@@ -132,34 +178,46 @@ xbps_fetch_file_dest_sha256(struct xbps_handle *xhp, const char *uri, const char
132178 if (flags != NULL )
133179 xbps_strlcpy (fetch_flags , flags , 7 );
134180
135- tempfile = xbps_xasprintf ("%s.part" , filename );
136- /*
137- * Check if we have to resume a transfer.
138- */
139- memset (& st_tmpfile , 0 , sizeof (st_tmpfile ));
140- if (stat (tempfile , & st_tmpfile ) == 0 ) {
141- if (st_tmpfile .st_size > 0 )
142- restart = true;
143- } else {
144- if (errno != ENOENT ) {
145- rv = -1 ;
146- goto fetch_file_out ;
147- }
181+ rv = snprintf (tempfile , sizeof (tempfile ), "%s.part" , filename );
182+ if (rv < 0 || (size_t )rv >= sizeof (tempfile )) {
183+ errno = ENAMETOOLONG ;
184+ return -1 ;
185+ }
186+
187+ lockfd = acquire_lock (lockfile , sizeof (lockfile ), filename );
188+ if (lockfd < 0 ) {
189+ xbps_error_printf ("failed to lock file: %s: %s\n" , filename ,
190+ strerror (- lockfd ));
191+ return -1 ;
148192 }
193+
149194 /*
150195 * Check if we have to refetch a transfer.
151196 */
152- memset (& st , 0 , sizeof (st ));
153197 if (stat (filename , & st ) == 0 ) {
154198 refetch = true;
155199 url -> last_modified = st .st_mtime ;
156200 xbps_strlcat (fetch_flags , "i" , sizeof (fetch_flags ));
157- } else {
158- if (errno != ENOENT ) {
159- rv = -1 ;
160- goto fetch_file_out ;
161- }
201+ } else if (errno != ENOENT ) {
202+ xbps_error_printf ("failed to stat target file: %s: %s\n" ,
203+ filename , strerror (errno ));
204+ rv = -1 ;
205+ goto fetch_file_out ;
206+ }
207+
208+ /*
209+ * Check if we have to resume a transfer.
210+ */
211+ if (stat (tempfile , & st_tmpfile ) == 0 ) {
212+ if (st_tmpfile .st_size > 0 )
213+ restart = true;
214+ } else if (errno != ENOENT ) {
215+ xbps_error_printf ("failed to stat temporary file: %s: %s\n" ,
216+ tempfile , strerror (errno ));
217+ rv = -1 ;
218+ goto fetch_file_out ;
162219 }
220+
163221 if (refetch && !restart ) {
164222 /* fetch the whole file, filename available */
165223 stp = & st ;
@@ -184,6 +242,7 @@ xbps_fetch_file_dest_sha256(struct xbps_handle *xhp, const char *uri, const char
184242 if (fio == NULL ) {
185243 if (fetchLastErrCode == FETCH_UNCHANGED ) {
186244 /* Last-Modified matched */
245+ rv = 0 ;
187246 goto fetch_file_out ;
188247 } else if (fetchLastErrCode == FETCH_PROTO && url_st .size == stp -> st_size ) {
189248 /* 413, requested offset == length */
@@ -312,7 +371,8 @@ xbps_fetch_file_dest_sha256(struct xbps_handle *xhp, const char *uri, const char
312371rename_file :
313372 /* File downloaded successfully, rename to destfile */
314373 if (rename (tempfile , filename ) == -1 ) {
315- xbps_dbg_printf ("failed to rename %s to %s: %s" ,
374+ xbps_error_printf (
375+ "failed to rename temporary file: %s: to: %s: %s\n" ,
316376 tempfile , filename , strerror (errno ));
317377 rv = -1 ;
318378 goto fetch_file_out ;
@@ -329,9 +389,8 @@ xbps_fetch_file_dest_sha256(struct xbps_handle *xhp, const char *uri, const char
329389 (void )close (fd );
330390 if (url != NULL )
331391 fetchFreeURL (url );
332-
333- free (tempfile );
334-
392+ if (lockfd != -1 )
393+ release_lock (lockfile , lockfd );
335394 return rv ;
336395}
337396
0 commit comments