[Python-checkins] python/dist/src/Objects fileobject.c,2.181,2.182

tim_one at users.sourceforge.net tim_one at users.sourceforge.net
Sat Sep 6 21:30:20 EDT 2003


Update of /cvsroot/python/python/dist/src/Objects
In directory sc8-pr-cvs1:/tmp/cvs-serv14962/python/Objects

Modified Files:
	fileobject.c 
Log Message:
SF bug  801631:  file.truncate fault on windows.

file_truncate():  C doesn't define what fflush(fp) does if fp is open
for update, and the preceding I/O operation on fp was input.  On Windows,
fflush() actually changes the current file position then.  Because
Windows doesn't support ftruncate() directly, this not only caused
Python's file.truncate() to change the file position (contra our docs),
it also caused the file not to change size.

Repaired by getting the initial file position at the start, restoring
it at the end, and tossing all the complicated micro-efficiency checks
trying to avoid "provably unnecessary" seeks.  file.truncate() can't
be a frequent operation, and seeking to the current file position has
got to be cheap anyway.

Bugfix candidate.


Index: fileobject.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/fileobject.c,v
retrieving revision 2.181
retrieving revision 2.182
diff -C2 -d -r2.181 -r2.182
*** fileobject.c	4 Sep 2003 19:01:46 -0000	2.181
--- fileobject.c	7 Sep 2003 03:30:18 -0000	2.182
***************
*** 97,101 ****
  		char *msg = "Is a directory";
  #endif
! 		PyObject *exc = PyObject_CallFunction(PyExc_IOError, "(is)", 
  						      EISDIR, msg);
  		PyErr_SetObject(PyExc_IOError, exc);
--- 97,101 ----
  		char *msg = "Is a directory";
  #endif
! 		PyObject *exc = PyObject_CallFunction(PyExc_IOError, "(is)",
  						      EISDIR, msg);
  		PyErr_SetObject(PyExc_IOError, exc);
***************
*** 138,142 ****
  	Py_INCREF(Py_None);
  	f->f_encoding = Py_None;
! 	
  	if (f->f_name == NULL || f->f_mode == NULL)
  		return NULL;
--- 138,142 ----
  	Py_INCREF(Py_None);
  	f->f_encoding = Py_None;
! 
  	if (f->f_name == NULL || f->f_mode == NULL)
  		return NULL;
***************
*** 190,195 ****
  #ifdef MS_WINDOWS
  		if (PyUnicode_Check(f->f_name)) {
! 			PyObject *wmode; 
! 			wmode = PyUnicode_DecodeASCII(mode, strlen(mode), NULL); 
  			if (f->f_name && wmode) {
  				Py_BEGIN_ALLOW_THREADS
--- 190,195 ----
  #ifdef MS_WINDOWS
  		if (PyUnicode_Check(f->f_name)) {
! 			PyObject *wmode;
! 			wmode = PyUnicode_DecodeASCII(mode, strlen(mode), NULL);
  			if (f->f_name && wmode) {
  				Py_BEGIN_ALLOW_THREADS
***************
*** 530,545 ****
  file_truncate(PyFileObject *f, PyObject *args)
  {
- 	int ret;
  	Py_off_t newsize;
! 	PyObject *newsizeobj;
  
  	if (f->f_fp == NULL)
  		return err_closed();
- 	newsizeobj = NULL;
  	if (!PyArg_UnpackTuple(args, "truncate", 0, 1, &newsizeobj))
  		return NULL;
  
  	/* Set newsize to current postion if newsizeobj NULL, else to the
! 	   specified value. */
  	if (newsizeobj != NULL) {
  #if !defined(HAVE_LARGEFILE_SUPPORT)
--- 530,560 ----
  file_truncate(PyFileObject *f, PyObject *args)
  {
  	Py_off_t newsize;
! 	PyObject *newsizeobj = NULL;
! 	Py_off_t initialpos;
! 	int ret;
  
  	if (f->f_fp == NULL)
  		return err_closed();
  	if (!PyArg_UnpackTuple(args, "truncate", 0, 1, &newsizeobj))
  		return NULL;
  
+ 	/* Get current file position.  If the file happens to be open for
+ 	 * update and the last operation was an input operation, C doesn't
+ 	 * define what the later fflush() will do, but we promise truncate()
+ 	 * won't change the current position (and fflush() *does* change it
+ 	 * then at least on Windows).  The easiest thing is to capture
+ 	 * current pos now and seek back to it at the end.
+ 	 */
+ 	Py_BEGIN_ALLOW_THREADS
+ 	errno = 0;
+ 	initialpos = _portable_ftell(f->f_fp);
+ 	Py_END_ALLOW_THREADS
+ 	if (initialpos == -1)
+ 		goto onioerror;
+ 
  	/* Set newsize to current postion if newsizeobj NULL, else to the
! 	 * specified value.
! 	 */
  	if (newsizeobj != NULL) {
  #if !defined(HAVE_LARGEFILE_SUPPORT)
***************
*** 553,567 ****
  			return NULL;
  	}
! 	else {
! 		/* Default to current position. */
! 		Py_BEGIN_ALLOW_THREADS
! 		errno = 0;
! 		newsize = _portable_ftell(f->f_fp);
! 		Py_END_ALLOW_THREADS
! 		if (newsize == -1)
! 			goto onioerror;
! 	}
  
! 	/* Flush the file. */
  	Py_BEGIN_ALLOW_THREADS
  	errno = 0;
--- 568,578 ----
  			return NULL;
  	}
! 	else /* default to current position */
! 		newsize = initialpos;
  
! 	/* Flush the stream.  We're mixing stream-level I/O with lower-level
! 	 * I/O, and a flush may be necessary to synch both platform views
! 	 * of the current file state.
! 	 */
  	Py_BEGIN_ALLOW_THREADS
  	errno = 0;
***************
*** 575,604 ****
  	   so don't even try using it. */
  	{
- 		Py_off_t current;	/* current file position */
  		HANDLE hFile;
- 		int error;
  
! 		/* current <- current file postion. */
! 		if (newsizeobj == NULL)
! 			current = newsize;
! 		else {
! 			Py_BEGIN_ALLOW_THREADS
! 			errno = 0;
! 			current = _portable_ftell(f->f_fp);
! 			Py_END_ALLOW_THREADS
! 			if (current == -1)
! 				goto onioerror;
! 		}
! 
! 		/* Move to newsize. */
! 		if (current != newsize) {
! 			Py_BEGIN_ALLOW_THREADS
! 			errno = 0;
! 			error = _portable_fseek(f->f_fp, newsize, SEEK_SET)
! 				!= 0;
! 			Py_END_ALLOW_THREADS
! 			if (error)
! 				goto onioerror;
! 		}
  
  		/* Truncate.  Note that this may grow the file! */
--- 586,598 ----
  	   so don't even try using it. */
  	{
  		HANDLE hFile;
  
! 		/* Have to move current pos to desired endpoint on Windows. */
! 		Py_BEGIN_ALLOW_THREADS
! 		errno = 0;
! 		ret = _portable_fseek(f->f_fp, newsize, SEEK_SET) != 0;
! 		Py_END_ALLOW_THREADS
! 		if (ret)
! 			goto onioerror;
  
  		/* Truncate.  Note that this may grow the file! */
***************
*** 606,629 ****
  		errno = 0;
  		hFile = (HANDLE)_get_osfhandle(fileno(f->f_fp));
! 		error = hFile == (HANDLE)-1;
! 		if (!error) {
! 			error = SetEndOfFile(hFile) == 0;
! 			if (error)
  				errno = EACCES;
  		}
  		Py_END_ALLOW_THREADS
! 		if (error)
  			goto onioerror;
- 
- 		/* Restore original file position. */
- 		if (current != newsize) {
- 			Py_BEGIN_ALLOW_THREADS
- 			errno = 0;
- 			error = _portable_fseek(f->f_fp, current, SEEK_SET)
- 				!= 0;
- 			Py_END_ALLOW_THREADS
- 			if (error)
- 				goto onioerror;
- 		}
  	}
  #else
--- 600,612 ----
  		errno = 0;
  		hFile = (HANDLE)_get_osfhandle(fileno(f->f_fp));
! 		ret = hFile == (HANDLE)-1;
! 		if (ret == 0) {
! 			ret = SetEndOfFile(hFile) == 0;
! 			if (ret)
  				errno = EACCES;
  		}
  		Py_END_ALLOW_THREADS
! 		if (ret)
  			goto onioerror;
  	}
  #else
***************
*** 632,638 ****
  	ret = ftruncate(fileno(f->f_fp), newsize);
  	Py_END_ALLOW_THREADS
! 	if (ret != 0) goto onioerror;
  #endif /* !MS_WINDOWS */
  
  	Py_INCREF(Py_None);
  	return Py_None;
--- 615,630 ----
  	ret = ftruncate(fileno(f->f_fp), newsize);
  	Py_END_ALLOW_THREADS
! 	if (ret != 0)
! 		goto onioerror;
  #endif /* !MS_WINDOWS */
  
+ 	/* Restore original file position. */
+ 	Py_BEGIN_ALLOW_THREADS
+ 	errno = 0;
+ 	ret = _portable_fseek(f->f_fp, initialpos, SEEK_SET) != 0;
+ 	Py_END_ALLOW_THREADS
+ 	if (ret)
+ 		goto onioerror;
+ 
  	Py_INCREF(Py_None);
  	return Py_None;
***************
*** 867,871 ****
  		Py_BEGIN_ALLOW_THREADS
  		errno = 0;
! 		nnow = Py_UniversalNewlineFread(ptr+ndone, ntodo, f->f_fp, 
  						(PyObject *)f);
  		Py_END_ALLOW_THREADS
--- 859,863 ----
  		Py_BEGIN_ALLOW_THREADS
  		errno = 0;
! 		nnow = Py_UniversalNewlineFread(ptr+ndone, ntodo, f->f_fp,
  						(PyObject *)f);
  		Py_END_ALLOW_THREADS
***************
*** 1138,1143 ****
  					skipnextlf = 0;
  					if (c == '\n') {
! 						/* Seeing a \n here with 
! 						 * skipnextlf true means we 
  						 * saw a \r before.
  						 */
--- 1130,1135 ----
  					skipnextlf = 0;
  					if (c == '\n') {
! 						/* Seeing a \n here with
! 						 * skipnextlf true means we
  						 * saw a \r before.
  						 */
***************
*** 1735,1740 ****
  		return Py_BuildValue("(sss)", "\r", "\n", "\r\n");
  	default:
! 		PyErr_Format(PyExc_SystemError, 
! 			     "Unknown newlines value 0x%x\n", 
  			     f->f_newlinetypes);
  		return NULL;
--- 1727,1732 ----
  		return Py_BuildValue("(sss)", "\r", "\n", "\r\n");
  	default:
! 		PyErr_Format(PyExc_SystemError,
! 			     "Unknown newlines value 0x%x\n",
  			     f->f_newlinetypes);
  		return NULL;
***************
*** 1746,1750 ****
  	{"closed", (getter)get_closed, NULL, "True if the file is closed"},
  #ifdef WITH_UNIVERSAL_NEWLINES
! 	{"newlines", (getter)get_newlines, NULL, 
  	 "end-of-line convention used in this file"},
  #endif
--- 1738,1742 ----
  	{"closed", (getter)get_closed, NULL, "True if the file is closed"},
  #ifdef WITH_UNIVERSAL_NEWLINES
! 	{"newlines", (getter)get_newlines, NULL,
  	 "end-of-line convention used in this file"},
  #endif
***************
*** 1761,1766 ****
  }
  
! /* Make sure that file has a readahead buffer with at least one byte 
!    (unless at EOF) and no more than bufsize.  Returns negative value on 
     error */
  static int
--- 1753,1758 ----
  }
  
! /* Make sure that file has a readahead buffer with at least one byte
!    (unless at EOF) and no more than bufsize.  Returns negative value on
     error */
  static int
***************
*** 1770,1774 ****
  
  	if (f->f_buf != NULL) {
! 		if( (f->f_bufend - f->f_bufptr) >= 1) 
  			return 0;
  		else
--- 1762,1766 ----
  
  	if (f->f_buf != NULL) {
! 		if( (f->f_bufend - f->f_bufptr) >= 1)
  			return 0;
  		else
***************
*** 1797,1802 ****
  
  /* Used by file_iternext.  The returned string will start with 'skip'
!    uninitialized bytes followed by the remainder of the line. Don't be 
!    horrified by the recursive call: maximum recursion depth is limited by 
     logarithmic buffer growth to about 50 even when reading a 1gb line. */
  
--- 1789,1794 ----
  
  /* Used by file_iternext.  The returned string will start with 'skip'
!    uninitialized bytes followed by the remainder of the line. Don't be
!    horrified by the recursive call: maximum recursion depth is limited by
     logarithmic buffer growth to about 50 even when reading a 1gb line. */
  
***************
*** 1810,1818 ****
  
  	if (f->f_buf == NULL)
! 		if (readahead(f, bufsize) < 0) 
  			return NULL;
  
  	len = f->f_bufend - f->f_bufptr;
! 	if (len == 0) 
  		return (PyStringObject *)
  			PyString_FromStringAndSize(NULL, skip);
--- 1802,1810 ----
  
  	if (f->f_buf == NULL)
! 		if (readahead(f, bufsize) < 0)
  			return NULL;
  
  	len = f->f_bufend - f->f_bufptr;
! 	if (len == 0)
  		return (PyStringObject *)
  			PyString_FromStringAndSize(NULL, skip);
***************
*** 1823,1827 ****
  		s = (PyStringObject *)
  			PyString_FromStringAndSize(NULL, skip+len);
! 		if (s == NULL) 
  			return NULL;
  		memcpy(PyString_AS_STRING(s)+skip, f->f_bufptr, len);
--- 1815,1819 ----
  		s = (PyStringObject *)
  			PyString_FromStringAndSize(NULL, skip+len);
! 		if (s == NULL)
  			return NULL;
  		memcpy(PyString_AS_STRING(s)+skip, f->f_bufptr, len);
***************
*** 2080,2084 ****
  		}
  #ifdef Py_USING_UNICODE
!                 if ((flags & Py_PRINT_RAW) && 
  		    PyUnicode_Check(v) && enc != Py_None) {
  			char *cenc = PyString_AS_STRING(enc);
--- 2072,2076 ----
  		}
  #ifdef Py_USING_UNICODE
!                 if ((flags & Py_PRINT_RAW) &&
  		    PyUnicode_Check(v) && enc != Py_None) {
  			char *cenc = PyString_AS_STRING(enc);





More information about the Python-checkins mailing list