Tarantool development patches archive
 help / color / mirror / Atom feed
* [Tarantool-patches] [PATCH v3 0/2] Fix working with VDBE
@ 2020-12-16 23:09 Leonid Vasiliev
  2020-12-16 23:09 ` [Tarantool-patches] [PATCH v3 1/2] sql: add missing diag_set on failure when working inside os_unix.c Leonid Vasiliev
  2020-12-16 23:09 ` [Tarantool-patches] [PATCH v3 2/2] sql: add panic() call in sql_execute() on complete failure Leonid Vasiliev
  0 siblings, 2 replies; 9+ messages in thread
From: Leonid Vasiliev @ 2020-12-16 23:09 UTC (permalink / raw)
  To: tarantool-patches, v.shpilevoy, imeevma, korablev, sergos

https://github.com/tarantool/tarantool/issues/5537
https://github.com/tarantool/tarantool/tree/lvasilev/gh-5537-sql-vdbe-sort-troubles

@ChangeLog is absent

Changes in v2:
  - add common diag_set if sql_execute is failure.

Changes in v3:
  - "sql: update temporary file name format" patch has been pushed to master
  - add missing diag_set on failure to all functions when working inside os_unix.c

Leonid Vasiliev (2):
  sql: add missing diag_set on failure when working inside os_unix.c
  sql: add panic() call in sql_execute() on complete failure

 src/box/execute.c     | 12 +++++++++-
 src/box/sql/os_unix.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 69 insertions(+), 6 deletions(-)

-- 
2.7.4

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Tarantool-patches] [PATCH v3 1/2] sql: add missing diag_set on failure when working inside os_unix.c
  2020-12-16 23:09 [Tarantool-patches] [PATCH v3 0/2] Fix working with VDBE Leonid Vasiliev
@ 2020-12-16 23:09 ` Leonid Vasiliev
  2020-12-20 16:02   ` Vladislav Shpilevoy
  2020-12-16 23:09 ` [Tarantool-patches] [PATCH v3 2/2] sql: add panic() call in sql_execute() on complete failure Leonid Vasiliev
  1 sibling, 1 reply; 9+ messages in thread
From: Leonid Vasiliev @ 2020-12-16 23:09 UTC (permalink / raw)
  To: tarantool-patches, v.shpilevoy, imeevma, korablev, sergos

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.

Co-authored-by: Mergen Imeev<imeevma@gmail.com>

Follow-up #5537
---
 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
@@ -159,14 +159,17 @@ robust_open(const char *z, int f, mode_t m)
 		if (fd < 0) {
 			if (errno == EINTR)
 				continue;
+			diag_set(SystemError, "failed to open file '%s'", z);
 			break;
 		}
 		if (fd >= SQL_MINIMUM_FILE_DESCRIPTOR)
 			break;
 		close(fd);
 		fd = -1;
-		if (open("/dev/null", f, m) < 0)
+		if (open("/dev/null", f, m) < 0) {
+			diag_set(SystemError, "failed to open '/dev/null'");
 			break;
+		}
 	}
 	if (fd >= 0) {
 		if (m != 0) {
@@ -193,6 +196,10 @@ robust_ftruncate(int h, sql_int64 sz)
 	do {
 		rc = ftruncate(h, sz);
 	} while (rc < 0 && errno == EINTR);
+
+	if (rc < 0)
+		diag_set(SystemError, "failed to truncate file");
+
 	return rc;
 }
 
@@ -395,6 +402,9 @@ findInodeInfo(unixFile * pFile,	/* Unix file with file desc used in the key */
 	fd = pFile->h;
 	rc = fstat(fd, &statbuf);
 	if (rc != 0) {
+		diag_set(SystemError,
+			 "failed to retrive information about the file '%s'",
+			 pFile->zPath);
 		storeLastErrno(pFile, errno);
 		return -1;
 	}
@@ -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);
+	if (rc < 0) {
+		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);
 }
@@ -473,8 +489,12 @@ unixFileLock(unixFile * pFile, struct flock *pLock)
 			lock.l_len = SHARED_SIZE;
 			lock.l_type = F_WRLCK;
 			rc = fcntl(pFile->h, F_SETLK, &lock);
-			if (rc < 0)
+			if (rc < 0) {
+				diag_set(SystemError,
+					 "failed to acquire a lock on the"
+					 " file '%s'", pFile->zPath);
 				return rc;
+			}
 			pInode->bProcessLock = 1;
 			pInode->nLock++;
 		} else {
@@ -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);
+		}
 	}
 	return rc;
 }
@@ -729,6 +754,9 @@ seekAndRead(unixFile * id, sql_int64 offset, void *pBuf, int cnt)
 	do {
 		newOffset = lseek(id->h, offset, SEEK_SET);
 		if (newOffset < 0) {
+			diag_set(SystemError,
+				 "failed to reposition the offset of '%s' file",
+				 id->zPath);
 			storeLastErrno((unixFile *) id, errno);
 			return -1;
 		}
@@ -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;
@@ -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);
+		}
 	} while (rc < 0 && errno == EINTR);
 
 	if (rc < 0)
@@ -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)) {
+			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;
 }
 
@@ -1558,6 +1604,9 @@ getFileMode(const char *zFile,	/* File name */
 		*pUid = sStat.st_uid;
 		*pGid = sStat.st_gid;
 	} else {
+		diag_set(SystemError,
+			 "failed to retrive information about the file '%s'",
+			 zFile);
 		rc = -1;
 	}
 	return rc;
@@ -1813,6 +1862,7 @@ unixDelete(sql_vfs * NotUsed,	/* VFS containing this as the xDelete method */
 	int rc = 0;
 	UNUSED_PARAMETER(NotUsed);
 	if (unlink(zPath) == (-1)) {
+		diag_set(SystemError, "failed to unlink the file '%s'", zPath);
 		return -1;
 	}
 	if ((dirSync & 1) != 0) {
@@ -1821,6 +1871,9 @@ unixDelete(sql_vfs * NotUsed,	/* VFS containing this as the xDelete method */
 		if (rc == 0) {
 			struct stat buf;
 			if (fstat(fd, &buf)) {
+				diag_set(SystemError,
+					 "failed to retrive information about"
+					 " the file '%s'", zPath);
 				rc = -1;
 			}
 			close(fd);
-- 
2.7.4

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Tarantool-patches] [PATCH v3 2/2] sql: add panic() call in sql_execute() on complete failure
  2020-12-16 23:09 [Tarantool-patches] [PATCH v3 0/2] Fix working with VDBE Leonid Vasiliev
  2020-12-16 23:09 ` [Tarantool-patches] [PATCH v3 1/2] sql: add missing diag_set on failure when working inside os_unix.c Leonid Vasiliev
@ 2020-12-16 23:09 ` Leonid Vasiliev
  2020-12-20 16:02   ` Vladislav Shpilevoy
  1 sibling, 1 reply; 9+ messages in thread
From: Leonid Vasiliev @ 2020-12-16 23:09 UTC (permalink / raw)
  To: tarantool-patches, v.shpilevoy, imeevma, korablev, sergos

In SQL, on failure sometimes an error sets to the diag, sometimes not.
And this can dived to situation as in #5537(SEGFAULT).
So, let's call `panic()` in that case, because something is very wrong,
and it is not safe to continue execution.

Follow-up #5537
---
 src/box/execute.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/src/box/execute.c b/src/box/execute.c
index e14da20..a424349 100644
--- a/src/box/execute.c
+++ b/src/box/execute.c
@@ -687,8 +687,18 @@ sql_execute(struct sql_stmt *stmt, struct port *port, struct region *region)
 		rc = sql_step(stmt);
 		assert(rc != SQL_ROW && rc != 0);
 	}
-	if (rc != SQL_DONE)
+	if (rc != SQL_DONE) {
+		/*
+		 * In SQL, on failure sometimes an error sets to the diag,
+		 * sometimes not. So, let's call `panic()` in that case, because
+		 * something is very wrong, and it is not safe to continue
+		 * execution.
+		 */
+		if (diag_is_empty(diag_get()))
+			panic("failed to execute SQL statement");
+
 		return -1;
+	}
 	return 0;
 }
 
-- 
2.7.4

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Tarantool-patches] [PATCH v3 1/2] sql: add missing diag_set on failure when working inside os_unix.c
  2020-12-16 23:09 ` [Tarantool-patches] [PATCH v3 1/2] sql: add missing diag_set on failure when working inside os_unix.c Leonid Vasiliev
@ 2020-12-20 16:02   ` Vladislav Shpilevoy
  2020-12-23 23:27     ` Leonid Vasiliev
  0 siblings, 1 reply; 9+ messages in thread
From: Vladislav Shpilevoy @ 2020-12-20 16:02 UTC (permalink / raw)
  To: Leonid Vasiliev, tarantool-patches, imeevma, korablev, sergos

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.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Tarantool-patches] [PATCH v3 2/2] sql: add panic() call in sql_execute() on complete failure
  2020-12-16 23:09 ` [Tarantool-patches] [PATCH v3 2/2] sql: add panic() call in sql_execute() on complete failure Leonid Vasiliev
@ 2020-12-20 16:02   ` Vladislav Shpilevoy
  2020-12-23 22:38     ` Leonid Vasiliev
  0 siblings, 1 reply; 9+ messages in thread
From: Vladislav Shpilevoy @ 2020-12-20 16:02 UTC (permalink / raw)
  To: Leonid Vasiliev, tarantool-patches, imeevma, korablev, sergos

Thanks for the patch!

On 17.12.2020 00:09, Leonid Vasiliev via Tarantool-patches wrote:
> In SQL, on failure sometimes an error sets to the diag, sometimes not.
> And this can dived to situation as in #5537(SEGFAULT).

dived -> lead.

> So, let's call `panic()` in that case, because something is very wrong,
> and it is not safe to continue execution.
> 
> Follow-up #5537

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Tarantool-patches] [PATCH v3 2/2] sql: add panic() call in sql_execute() on complete failure
  2020-12-20 16:02   ` Vladislav Shpilevoy
@ 2020-12-23 22:38     ` Leonid Vasiliev
  0 siblings, 0 replies; 9+ messages in thread
From: Leonid Vasiliev @ 2020-12-23 22:38 UTC (permalink / raw)
  To: Vladislav Shpilevoy, tarantool-patches, imeevma, korablev, sergos

Hi! Thank you for the review.

On 20.12.2020 19:02, Vladislav Shpilevoy wrote:
> Thanks for the patch!
> 
> On 17.12.2020 00:09, Leonid Vasiliev via Tarantool-patches wrote:
>> In SQL, on failure sometimes an error sets to the diag, sometimes not.
>> And this can dived to situation as in #5537(SEGFAULT).
> 
> dived -> lead.
> 
>> So, let's call `panic()` in that case, because something is very wrong,
>> and it is not safe to continue execution.
>>
>> Follow-up #5537

Updated:

In SQL, on failure sometimes an error sets to the diag, sometimes not.
And this can lead to situation as in #5537(SEGFAULT).
So, let's call `panic()` in that case, because something is very wrong,
and it is not safe to continue execution.

Follow-up #5537

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Tarantool-patches] [PATCH v3 1/2] sql: add missing diag_set on failure when working inside os_unix.c
  2020-12-20 16:02   ` Vladislav Shpilevoy
@ 2020-12-23 23:27     ` Leonid Vasiliev
  2020-12-24 16:00       ` Vladislav Shpilevoy
  0 siblings, 1 reply; 9+ messages in thread
From: Leonid Vasiliev @ 2020-12-23 23:27 UTC (permalink / raw)
  To: Vladislav Shpilevoy, tarantool-patches, imeevma, korablev, sergos

Hi! Thank you for the review.

The updated patch is at the end of the letter.

On 20.12.2020 19:02, Vladislav Shpilevoy wrote:
> 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.

Updated.

> 
> 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?

Yep, right after your question: "Did you think about syncing diag_set
with this one?":)
As far as I can see, `diag_set()` now has more coverage than
`storeLastErrno()`. Seems like some `diag_set` can be moved to
`storeLastErrno()`, but will need to add a `message` argument to
`storeLastErrno()` for `diag_set ()`.
I think such refactoring can be done in a separate ticket if it is
useful, because it isn't related with a crash.
Do you have other thoughts?

> 
> 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.

After your second note, this one is deprecated.

> 
>> +	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.

OMG! That's my mistake. Thank you.
Changes reverted.

> 
>> +		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().

Updated the message.

> 
>> +		}
>>   	}
>>   	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.

AFAIU you talking about `else` case. Updated.

> 
>> @@ -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.

Yep. It's true.

> 
>> +		}
>>   	} 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.

Updated.

> 
>> @@ -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.

Ok. I don't mind.

> 
>> +			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.

Updated.

> 


Updated patch:

SQL module didn't set an error in the diagnostics area on failure
inside os_unix.c. This could lead to a crash like in #5537.

Co-authored-by: Mergen Imeev<imeevma@gmail.com>

Follow-up #5537
---
  src/box/sql/os_unix.c | 73 
++++++++++++++++++++++++++++++++++++++++++++++-----
  1 file changed, 67 insertions(+), 6 deletions(-)

diff --git a/src/box/sql/os_unix.c b/src/box/sql/os_unix.c
index d64f1bd..07461f5 100644
--- a/src/box/sql/os_unix.c
+++ b/src/box/sql/os_unix.c
@@ -159,14 +159,17 @@ robust_open(const char *z, int f, mode_t m)
  		if (fd < 0) {
  			if (errno == EINTR)
  				continue;
+			diag_set(SystemError, "failed to open file '%s'", z);
  			break;
  		}
  		if (fd >= SQL_MINIMUM_FILE_DESCRIPTOR)
  			break;
  		close(fd);
  		fd = -1;
-		if (open("/dev/null", f, m) < 0)
+		if (open("/dev/null", f, m) < 0) {
+			diag_set(SystemError, "failed to open '/dev/null'");
  			break;
+		}
  	}
  	if (fd >= 0) {
  		if (m != 0) {
@@ -193,6 +196,10 @@ robust_ftruncate(int h, sql_int64 sz)
  	do {
  		rc = ftruncate(h, sz);
  	} while (rc < 0 && errno == EINTR);
+
+	if (rc < 0)
+		diag_set(SystemError, "failed to truncate file");
+
  	return rc;
  }

@@ -395,6 +402,9 @@ findInodeInfo(unixFile * pFile,	/* Unix file with 
file desc used in the key */
  	fd = pFile->h;
  	rc = fstat(fd, &statbuf);
  	if (rc != 0) {
+		diag_set(SystemError,
+			 "failed to retrieve information about the file '%s'",
+			 pFile->zPath);
  		storeLastErrno(pFile, errno);
  		return -1;
  	}
@@ -473,8 +483,12 @@ unixFileLock(unixFile * pFile, struct flock *pLock)
  			lock.l_len = SHARED_SIZE;
  			lock.l_type = F_WRLCK;
  			rc = fcntl(pFile->h, F_SETLK, &lock);
-			if (rc < 0)
+			if (rc < 0) {
+				diag_set(SystemError,
+					 "failed to acquire / release a lock on"
+					 " the file '%s'", pFile->zPath);
  				return rc;
+			}
  			pInode->bProcessLock = 1;
  			pInode->nLock++;
  		} else {
@@ -482,6 +496,11 @@ unixFileLock(unixFile * pFile, struct flock *pLock)
  		}
  	} else {
  		rc = fcntl(pFile->h, F_SETLK, pLock);
+		if (rc < 0) {
+			diag_set(SystemError,
+				 "failed to acquire / release a lock on"
+				 " the file '%s'", pFile->zPath);
+		}
  	}
  	return rc;
  }
@@ -729,6 +748,9 @@ seekAndRead(unixFile * id, sql_int64 offset, void 
*pBuf, int cnt)
  	do {
  		newOffset = lseek(id->h, offset, SEEK_SET);
  		if (newOffset < 0) {
+			diag_set(SystemError,
+				 "failed to reposition the offset of '%s' file",
+				 id->zPath);
  			storeLastErrno((unixFile *) id, errno);
  			return -1;
  		}
@@ -740,6 +762,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;
@@ -794,6 +818,11 @@ unixRead(sql_file * id, void *pBuf, int amt, 
sql_int64 offset)
  		/* lastErrno set by seekAndRead */
  		return -1;
  	} else {
+		/* Read less than planned from file. */
+		const char *err = tt_sprintf("read less (%i bytes) than "
+					     "planned (%i bytes) from file: %s",
+					     got, amt, pFile->zPath);
+		diag_set(ClientError, ER_SQL_EXECUTE, err);
  		storeLastErrno(pFile, 0);	/* not a system error */
  		/* Unread parts of the buffer must be zero-filled */
  		memset(&((char *)pBuf)[got], 0, amt - got);
@@ -825,10 +854,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 && errno != EINTR) {
+			diag_set(SystemError,
+				 "failed to write %i bytes to file", nBuf);
+		}
  	} while (rc < 0 && errno == EINTR);

  	if (rc < 0)
@@ -873,6 +908,12 @@ unixWrite(sql_file * id, const void *pBuf, int amt, 
sql_int64 offset)
  			/* lastErrno set by seekAndWrite */
  			return -1;
  		} else {
+			/* Wrote to file less than planned. */
+			const char *err = tt_sprintf("wrote less (%i bytes) "
+						     "than planned (%i bytes) "
+						     "to file: %s",
+						     wrote, amt, pFile->zPath);
+			diag_set(ClientError, ER_SQL_EXECUTE, err);
  			storeLastErrno(pFile, 0);	/* not a system error */
  			return -1;
  		}
@@ -940,8 +981,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) != 0) {
+			diag_set(SystemError,
+				 "failed to retrieve information about the"
+				 " file '%s'", pFile->zPath);
  			return -1;
+		}

  		nSize =
  		    ((nByte + pFile->szChunk -
@@ -1165,8 +1210,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) != 0) {
+			diag_set(SystemError,
+				 "failed to retrieve information about the"
+				 " file '%s'", pFd->zPath);
  			return -1;
+		}
  		nMap = statbuf.st_size;
  	}
  	if (nMap > pFd->mmapSizeMax) {
@@ -1449,6 +1498,8 @@ unixTempFileDir(void)
  			break;
  		zDir = azDirs[i++];
  	}
+	diag_set(ClientError, ER_SYSTEM,
+		 "No access to any temporary directory");
  	return 0;
  }

@@ -1480,8 +1531,11 @@ unixGetTempname(int nBuf, char *zBuf)
  		sql_snprintf(nBuf, zBuf,
  				 "%s/" SQL_TEMP_FILE_PREFIX "%ld_%llx%c", zDir,
  				 (long)randomnessPid, r, 0);
-		if (zBuf[nBuf - 2] != 0 || (iLimit++) > 10)
+		if (zBuf[nBuf - 2] != 0 || (iLimit++) > 10) {
+			diag_set(ClientError, ER_SQL_EXECUTE,
+				 "can't create temporary file");
  			return -1;
+		}
  	} while (access(zBuf, 0) == 0);
  	return 0;
  }
@@ -1558,6 +1612,9 @@ getFileMode(const char *zFile,	/* File name */
  		*pUid = sStat.st_uid;
  		*pGid = sStat.st_gid;
  	} else {
+		diag_set(SystemError,
+			 "failed to retrieve information about the file '%s'",
+			 zFile);
  		rc = -1;
  	}
  	return rc;
@@ -1813,6 +1870,7 @@ unixDelete(sql_vfs * NotUsed,	/* VFS containing 
this as the xDelete method */
  	int rc = 0;
  	UNUSED_PARAMETER(NotUsed);
  	if (unlink(zPath) == (-1)) {
+		diag_set(SystemError, "failed to unlink the file '%s'", zPath);
  		return -1;
  	}
  	if ((dirSync & 1) != 0) {
@@ -1820,7 +1878,10 @@ unixDelete(sql_vfs * NotUsed,	/* VFS containing 
this as the xDelete method */
  		rc = openDirectory(zPath, &fd);
  		if (rc == 0) {
  			struct stat buf;
-			if (fstat(fd, &buf)) {
+			if (fstat(fd, &buf) != 0) {
+				diag_set(SystemError,
+					 "failed to retrieve information about"
+					 " the file '%s'", zPath);
  				rc = -1;
  			}
  			close(fd);
-- 
2.7.4

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Tarantool-patches] [PATCH v3 1/2] sql: add missing diag_set on failure when working inside os_unix.c
  2020-12-23 23:27     ` Leonid Vasiliev
@ 2020-12-24 16:00       ` Vladislav Shpilevoy
  2020-12-24 16:59         ` Nikita Pettik
  0 siblings, 1 reply; 9+ messages in thread
From: Vladislav Shpilevoy @ 2020-12-24 16:00 UTC (permalink / raw)
  To: Leonid Vasiliev, tarantool-patches, imeevma, korablev, sergos

Hi! Thanks for the fixes!

See 1 comment below. After that the patchset will LGTM, and
you can send it to Nikita on a second review.

> diff --git a/src/box/sql/os_unix.c b/src/box/sql/os_unix.c
> index d64f1bd..07461f5 100644
> --- a/src/box/sql/os_unix.c
> +++ b/src/box/sql/os_unix.c
> @@ -873,6 +908,12 @@ unixWrite(sql_file * id, const void *pBuf, int amt, sql_int64 offset)
>              /* lastErrno set by seekAndWrite */
>              return -1;
>          } else {
> +            /* Wrote to file less than planned. */
> +            const char *err = tt_sprintf("wrote less (%i bytes) "
> +                             "than planned (%i bytes) "
> +                             "to file: %s",
> +                             wrote, amt, pFile->zPath);
> +            diag_set(ClientError, ER_SQL_EXECUTE, err);

Planned wasn't amt, because amt is modified in the loop above. Also
'wrote' will be -1 here, because this branch is taked on ENOSPC and
write() returned -1.

Perpahs it is enough to say that didn't have enough space to write
'amt' bytes (this is how much was left to write).

>              storeLastErrno(pFile, 0);    /* not a system error */
>              return -1;
>          }

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Tarantool-patches] [PATCH v3 1/2] sql: add missing diag_set on failure when working inside os_unix.c
  2020-12-24 16:00       ` Vladislav Shpilevoy
@ 2020-12-24 16:59         ` Nikita Pettik
  0 siblings, 0 replies; 9+ messages in thread
From: Nikita Pettik @ 2020-12-24 16:59 UTC (permalink / raw)
  To: Vladislav Shpilevoy; +Cc: tarantool-patches

On 24 Dec 17:00, Vladislav Shpilevoy wrote:
> Hi! Thanks for the fixes!
> 
> See 1 comment below. After that the patchset will LGTM, and
> you can send it to Nikita on a second review.

Right away I can say that it would be super nice to see corresponding test
(using error injections I gueess). Otherwise we can't say for sure that
all this stubs are working...
 
> > diff --git a/src/box/sql/os_unix.c b/src/box/sql/os_unix.c
> > index d64f1bd..07461f5 100644
> > --- a/src/box/sql/os_unix.c
> > +++ b/src/box/sql/os_unix.c
> > @@ -873,6 +908,12 @@ unixWrite(sql_file * id, const void *pBuf, int amt, sql_int64 offset)
> >              /* lastErrno set by seekAndWrite */
> >              return -1;
> >          } else {
> > +            /* Wrote to file less than planned. */
> > +            const char *err = tt_sprintf("wrote less (%i bytes) "
> > +                             "than planned (%i bytes) "
> > +                             "to file: %s",
> > +                             wrote, amt, pFile->zPath);
> > +            diag_set(ClientError, ER_SQL_EXECUTE, err);
> 
> Planned wasn't amt, because amt is modified in the loop above. Also
> 'wrote' will be -1 here, because this branch is taked on ENOSPC and
> write() returned -1.
> 
> Perpahs it is enough to say that didn't have enough space to write
> 'amt' bytes (this is how much was left to write).
> 
> >              storeLastErrno(pFile, 0);    /* not a system error */
> >              return -1;
> >          }

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2020-12-24 16:59 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-16 23:09 [Tarantool-patches] [PATCH v3 0/2] Fix working with VDBE Leonid Vasiliev
2020-12-16 23:09 ` [Tarantool-patches] [PATCH v3 1/2] sql: add missing diag_set on failure when working inside os_unix.c Leonid Vasiliev
2020-12-20 16:02   ` Vladislav Shpilevoy
2020-12-23 23:27     ` Leonid Vasiliev
2020-12-24 16:00       ` Vladislav Shpilevoy
2020-12-24 16:59         ` Nikita Pettik
2020-12-16 23:09 ` [Tarantool-patches] [PATCH v3 2/2] sql: add panic() call in sql_execute() on complete failure Leonid Vasiliev
2020-12-20 16:02   ` Vladislav Shpilevoy
2020-12-23 22:38     ` Leonid Vasiliev

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox