[Tarantool-patches] [PATCH v3 1/2] sql: add missing diag_set on failure when working inside os_unix.c

Vladislav Shpilevoy v.shpilevoy at tarantool.org
Sun Dec 20 19:02:29 MSK 2020


Hi! Thanks for the patch!

On 17.12.2020 00:09, Leonid Vasiliev via Tarantool-patches wrote:
> SQL module didn't set an error in the diagnostics area on failure
> inside unix.c. This could lead to a crash like in #5537.

unix.c -> os_unix.c.

I see os_unix.c already has some kind of diagnostics area filled
by storeLastErrno(). Did you think about syncing diag_set with this
one?

See 8 comments below.

>  src/box/sql/os_unix.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++----
>  1 file changed, 58 insertions(+), 5 deletions(-)
> 
> diff --git a/src/box/sql/os_unix.c b/src/box/sql/os_unix.c
> index d64f1bd..4f59767 100644
> --- a/src/box/sql/os_unix.c
> +++ b/src/box/sql/os_unix.c> @@ -433,7 +443,13 @@ static int
>  fileHasMoved(unixFile * pFile)
>  {
>  	struct stat buf;
> -	return pFile->pInode != NULL && (stat(pFile->zPath, &buf) != 0 ||
> +	int rc = stat(pFile->zPath, &buf);

1. stat() wasn't called if pInode was NULL. Now it is. Lets not
change the behaviour.

> +	if (rc < 0) {

2. Why do you use < 0 here and != just 5 lines below?

But the more important question is why do you even set diag
here? This function (fileHasMoved) can't "fail". It just
returns true or false - moved or not moved. There is no a way
to return an error from it. Any issues with 'stat' were treated
as 'file has moved'. I assume it was even the purpose, because
usually stat() fails if there is no such file.

> +		diag_set(SystemError,
> +			 "failed to retrive information about the file '%s'",
> +			 pFile->zPath);
> +	}
> +	return pFile->pInode != NULL && (rc != 0 ||
>  					 (u64) buf.st_ino !=
>  					 pFile->pInode->fileId.ino);
>  }
> @@ -482,6 +502,11 @@ unixFileLock(unixFile * pFile, struct flock *pLock)
>  		}
>  	} else {
>  		rc = fcntl(pFile->h, F_SETLK, pLock);
> +		if (rc < 0) {
> +			diag_set(SystemError,
> +				 "failed to acquire a lock on the file '%s'",
> +				 pFile->zPath);

3. This function is not only about lock acquire. It is also used
to remove the lock. For instance, by posixUnlock().

> +		}
>  	}
>  	return rc;
>  }
> @@ -740,6 +768,8 @@ seekAndRead(unixFile * id, sql_int64 offset, void *pBuf, int cnt)
>  				got = 1;
>  				continue;
>  			}
> +			diag_set(SystemError, "failed to read from file '%s'",
> +				 id->zPath);
>  			prior = 0;
>  			storeLastErrno((unixFile *) id, errno);
>  			break;

4. unixRead() returns -1 in case seekAndRead() returned 0. But
there is no a diag_set() anywhere for this.

> @@ -825,10 +855,16 @@ seekAndWriteFd(int fd,		/* File descriptor to write to */
>  	do {
>  		i64 iSeek = lseek(fd, iOff, SEEK_SET);
>  		if (iSeek < 0) {
> +			diag_set(SystemError,
> +				 "failed to reposition file offset");
>  			rc = -1;
>  			break;
>  		}
>  		rc = write(fd, pBuf, nBuf);
> +		if (rc < 0) {
> +			diag_set(SystemError,
> +				 "failed to write %i bytes to file", nBuf);

5. The error is supposed to be ignored if errno is EINTR.

> +		}
>  	} while (rc < 0 && errno == EINTR);
>  
>  	if (rc < 0)

6. unixWrite() returns -1 if not everything was written, it
seems. Here:

		if (amt > wrote) {
			if (wrote < 0 && pFile->lastErrno != ENOSPC) {
				/* lastErrno set by seekAndWrite */
				return -1;
			} else {
				storeLastErrno(pFile, 0);	/* not a system error */
				return -1;
			}
		}

The 'else' branch. But I don't understand how it can happen.

> @@ -940,8 +976,12 @@ fcntlSizeHint(unixFile * pFile, i64 nByte)
>  		i64 nSize;	/* Required file size */
>  		struct stat buf;	/* Used to hold return values of fstat() */
>  
> -		if (fstat(pFile->h, &buf))
> +		if (fstat(pFile->h, &buf)) {

7. I suggest to use != 0 explicitly, according to the code
style rules for the new code. The same in the hunk below.

> +			diag_set(SystemError,
> +				 "failed to retrive information about the"
> +				 " file '%s'", pFile->zPath);
>  			return -1;
> +		}
>  
>  		nSize =
>  		    ((nByte + pFile->szChunk -
> @@ -1165,8 +1205,12 @@ unixMapfile(unixFile * pFd, i64 nMap)
>  
>  	if (nMap < 0) {
>  		struct stat statbuf;	/* Low-level file information */
> -		if (fstat(pFd->h, &statbuf))
> +		if (fstat(pFd->h, &statbuf)) {
> +			diag_set(SystemError,
> +				 "failed to retrive information about the"
> +				 " file '%s'", pFd->zPath);
>  			return -1;
> +		}
>  		nMap = statbuf.st_size;
>  	}
>  	if (nMap > pFd->mmapSizeMax) {
> @@ -1449,6 +1493,8 @@ unixTempFileDir(void)
>  			break;
>  		zDir = azDirs[i++];
>  	}
> +	diag_set(ClientError, ER_SYSTEM,
> +		 "No access to any temporary directory");
>  	return 0;
>  }

8. unixGetTempname() does not set diag in one case of -1 return.


More information about the Tarantool-patches mailing list