[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-core-devel
Subject:    Patch for mmap problem in KConfigBackEnd, Bug #72586
From:       "Dr. Juergen Pfennig" <info () j-pfennig ! de>
Date:       2004-01-15 13:20:16
Message-ID: 200401151420.17108.info () j-pfennig ! de
[Download RAW message or body]

Hello whoever,

I send you a patch to avoid that Konqueror hangs and Syslogd quickly fills 
your harddisk with error messages. The problem occurs with mmap() and files 
that are accessed via smb (or nfs) and are not accessible due to ownership 
and protection on the server side. See the remark concerning nfs in 'man 2 
access'. Typically the problem shows up with konqueror and the .directory 
file. Lots of lines containing just the work "SIGBUS" are written to stderr 
and konqueror hangs.

About the current implementation in kconfigbackend.cpp: mmap() is used always  
to read conf files. Interrestingly the author was aware of a SIGBUS problem 
and tried to recover from the SIGBUS. BUT THIS DOES NOT WORK. Maybe it never 
worked? Or maybe it's a compiler optimization problem.

About the patch: as mmap() may return unaccessible memory, the use of mmap is 
restricted now to large files. In any case a classical read() syscall is used 
either to read an entiere small conf file, or to probe if the (large) file is 
readable. If the read succeeds for a large file, mmap is used as before. mmap 
is not trivial for the kernel, maybe this patch also gives a little speed 
improvement. The patch is against today's HEAD (3.1.94 plus updates).

I also attach a little test program that can be used to play with access() and 
mmap() if you don't trust me. My kernel is a 2.4.21 from Mantel/SuSE. I am 
using a P4 and the problem occurred also in older Versions of KDE.

Yours Jürgen

["jpf.patch" (text/x-diff)]

--- kconfigbackend.cpp	2004-01-15 13:16:49.000000000 +0100
+++ kconfigbackend.cpp	2004-01-15 13:11:06.000000000 +0100
@@ -367,13 +367,14 @@
 
 #ifdef HAVE_MMAP
 #ifdef SIGBUS
-static const char **mmap_pEof;
 
 static void mmap_sigbus_handler(int)
 {
-   *mmap_pEof = 0;
-   write(2, "SIGBUS\n", 7);
-   signal(SIGBUS, mmap_sigbus_handler);
+   const char* pMsg = "FATAL: KConfigBackend "
+                      "cannot recover from SIGBUS error on mmap'ed config file\n";
+   write(2, pMsg, strlen(pMsg));
+   // we must commit suicide, otherwise we an infinite loop
+   _exit(2);
 }
 #endif
 #endif
@@ -399,33 +400,58 @@
 
    unsigned int ll = localeString.length();
 
-#ifdef HAVE_MMAP
-   const char *map = ( const char* ) mmap(0, rFile.size(), PROT_READ, MAP_PRIVATE,
-                                          rFile.handle(), 0);
+   bool fileOptionImmutable = false;
+   bool groupOptionImmutable = false;
+   bool groupSkip = false;
 
-   if (map)
-   {
-      s = map;
+   // mmap is fast for large config files but can fail to return readable
+   // memory for smb or nfs mounts (see access() manpage). The fastest
+   // workaround is to probe files using read(). Small files are read
+   // directly, large files are left to mmap().
+
+   char	sBuffer[1024];
+   int	iOK;
+   if(rFile.size() <= sizeof(sBuffer))
+   {  s = sBuffer;
       eof = s + rFile.size();
-
-#ifdef SIGBUS
-      mmap_pEof = &eof;
-      old_sighandler = signal(SIGBUS, mmap_sigbus_handler);
-#endif
+      iOK = read(rFile.handle(), sBuffer, rFile.size());
    }
    else
+   {  s = eof = 0;
+      iOK = read(rFile.handle(), sBuffer, 1);
+   }
+   if(iOK < 0)
+   {	// kdDebug cannot be used here?
+      fprintf(stderr, "KConfigBackend cannot read file: %s\n", rFile.name().latin1());
+      return;
+   }
+
+#ifdef HAVE_MMAP
+   const char *map = 0;		// we need it for munmap()
 #endif
+   if(s == 0)
    {
-      rFile.at(0);
-      data = rFile.readAll();
-      s = data.data();
-      eof = s + data.size();
+#ifdef HAVE_MMAP
+      map = (const char*)mmap(0, rFile.size(), PROT_READ, MAP_PRIVATE,
+                                  rFile.handle(), 0);
+      if(map)
+      {  s = map;
+         eof = s + rFile.size();
+         // SIGBUS cannot be used to recover invalid memory access!
+#ifdef SIGBUS
+         old_sighandler = signal(SIGBUS, mmap_sigbus_handler);
+#endif
+      }
+      else
+#endif
+      {  // use qt if mmap is not availlable
+         rFile.at(0);
+         data = rFile.readAll();
+         s = data.data();
+         eof = s + data.size();
+      }
    }
 
-   bool fileOptionImmutable = false;
-   bool groupOptionImmutable = false;
-   bool groupSkip = false;
-
    int line = 0;
    for(; s < eof; s++)
    {
@@ -447,7 +473,7 @@
       if (*s == '[')  //group
       {
          // In a group [[ and ]] have a special meaning
-         while ((s < eof) && (*s != '\n')) 
+         while ((s < eof) && (*s != '\n'))
          {
             if (*s == ']')
             {

["smbproblem.cpp" (text/x-c++src)]

#include	<stdlib.h>
#include	<stdio.h>
#include	<unistd.h>
#include	<sys/types.h>
#include	<sys/stat.h>
#include	<fcntl.h>
#include	<errno.h>
#include	<sys/mman.h>
#include	<signal.h>

char*	pX;
void sighandler(int iSig)
{
	printf("sighandler %d\n", iSig);
	pX  = "";
}

int main()
{
	const char*	pszFile = "/Filme/Server/.directory";
	printf("testing: %s\n", pszFile);

	// access won't help ...
	int		ia = access(pszFile, R_OK);
	printf("access returns %x\n", ia);

	// open doesn't fail ...
	int io = open(pszFile, O_RDONLY);
	printf("open returns %x\n", io);

	// fstat works ...
	struct stat st;
	int is = fstat(io, &st);
	printf("fstat() returns %x  mode %x  uid %d  gid %d\n",
			 is, st.st_mode, st.st_uid, st.st_gid);

	// read fails ...
	char	buff[128];
	int ir = read(io, buff, st.st_size);
	if(ir >= 0)	printf("read() returns %d\n", ir);
	else		perror("read() status");

	// mmap succeeds ...
	void*	pMap = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, io, 0);
	if(pMap)	printf("mmap() returns %x\n", pMap);
	else	    perror("mmap() status");

	// write using the mmap memory fails
	int iw = write(0, pMap, 1);
	if(iw >= 0)	printf("write() returns %x\n", iw);
	else		perror("write() status");

	// THIS IS CURRENTLY IN KDE AND IS WRONG!
//	signal(SIGBUS, sighandler);
//	printf("data %x\n", pX[0]);

	return 0;
}


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic