본문 바로가기

공부/컴퓨터

Java의 Windows FileOutputStream 은 안전하지 않다..

반응형

오랜만에 공부하는 포스팅 ㅎㅎ..

Windows에서 Java의 FileOutputStream을 사용하게 되면,
다음과 같은 네이티브 코드를 사용하게 된다.


void
fileOpen(JNIEnv *env, jobject this, jstring path, jfieldID fid, int flags)
{
  DWORD access = 0;
  DWORD sharing = FILE_SHARE_READ | FILE_SHARE_WRITE;
  DWORD disposition = OPEN_EXISTING;
  DWORD flagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
  HANDLE h = NULL;
  int pathlen = 0;

  /* Note: O_TRUNC overrides O_CREAT */
  if (flags & O_TRUNC)        {    disposition = CREATE_ALWAYS;     }
  else if (flags & O_CREAT) {    disposition = OPEN_ALWAYS;         }

  if (flags & O_SYNC) {    flagsAndAttributes = FILE_FLAG_WRITE_THROUGH;    }

  if (flags & O_DSYNC) {  flagsAndAttributes = FILE_FLAG_WRITE_THROUGH;    }

  if (flags & O_RDONLY) { access = GENERIC_READ;   }

  if (flags & O_WRONLY) { access = GENERIC_WRITE;  }

  if (flags & O_RDWR)     { access = GENERIC_READ | GENERIC_WRITE;   }

  if (flags == 0)              {  access = GENERIC_READ;  }

  if (onNT) {
       WCHAR *pathbuf = pathToNTPath(env, path, JNI_TRUE);
       if (pathbuf == NULL) {
              /* Exception already pending */
             return;
       }

       h = CreateFileW(
           pathbuf,   /* Wide char path name */
           access,    /* Combine read and/or write permission */
           sharing,   /* File sharing flags */
           NULL,      /* Security attributes */
           disposition,         /* creation disposition */
           flagsAndAttributes,  /* flags and attributes */
           NULL);

       free(pathbuf);

  } else {

       WITH_PLATFORM_STRING(env, path, _ps) {
             h = CreateFile(_ps, access, sharing, NULL, disposition,
                                   flagsAndAttributes, NULL);
       } END_PLATFORM_STRING(env, _ps);
}

if (h == INVALID_HANDLE_VALUE) {
       int error = GetLastError();
       if (error == ERROR_TOO_MANY_OPEN_FILES) {
             JNU_ThrowByName(env, JNU_JAVAIOPKG "IOException",
                               "Too many open files");
            return;
       }
       throwFileNotFoundException(env, path);
       return;
  }
  SET_FD(this, (jlong)h, fid);
}


위의 코드는 그리 어렵지 않다. 각 속성을 조합한 뒤에 결국
Windows 에서 제공해주는 API인 CreateFile을 호출하게 된다.


하지만 이 옵션들 중에서 조정이 불가능한 옵션이 있으니. 이 옵션은.

  DWORD sharing = FILE_SHARE_READ | FILE_SHARE_WRITE;

이다.


이 공유 속성은 내가 지금 쓰려고 하는 화일을
다른 프로세스에서 접근하면 어떤 동작이 가능하게 할것인가를 나타내주는 속성이다.


자바에서는 FileOutputStream을 만들거나 혹은  RandomAccessFile을 생성하게 되면,
fileOpen(...) 함수를 사용하게 된다.
결국 createFile을 쓰게 되는데, 이때 다른곳에서 접근하는 화일들에게도 쓰기 권한을 준 상태로 핸들을 만들게 된다.


그러므로, 자바에서 FileOutputStream을 만들고, 쓰고 있는 도중에,
다른 프로그램에서 해당 화일에 대해서 쓰기 권한을 가지고 화일을 열게 되면..
(  DWORD access = GENERIC_WRITE; )
화일이 깨질 수 있는 문제가 언제든지 발생할 수 있다.


그러므로 FileOutputStream을 사용하여 화일을 쓸려고 할때에는,
되도록이면 Channel을 구한뒤에 lock()을 건 뒤에 사용해주는것이 좋다.



자바에서는  shared Lock 과 exclusive Lock을 제공하는데
( 정확하게 이야기 하면 os 에서 제공하는것이지만 ),


자바에서 exclusive Lock를 잡고 있다면,

다른 프로세스에서 CreateFile을 사용할때 공유 속성에 WRITE가 없다면
핸들을 만드는것 조차 실패한다.

공유속성에 WRITE가 있으면 핸들을 만드는것은 성공한다.
하지만 exclusive Lock를 잡고 있기 때문에 WriteFile(..)등의 함수를 사용하여
화일에 데이터를 쓰려고 하면 실패하게 된다.



자바에서 shared Lock를 잡았을때에도
다른 프로세스에서 해당 화일에 데이터를 쓸려고 열어도
(  DWORD access = GENERIC_WRITE; )
화일 핸들은 잘 만들어지나 WriteFile 에서 데이터를 쓰려고 할때 실패하게 된다.



그러므로, 화일을 쓸때에는 lock를 잡고 쓰는것이 매우 안전하다고 볼 수 있다.


반응형