mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-23 10:22:52 +00:00
partitionmanager: use fatlabel program from dosfstools to set the label of FAT16 filesystems
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
parent
6aff2cd4fd
commit
40be583e14
45 changed files with 4 additions and 6550 deletions
|
@ -49,9 +49,7 @@ add_definitions(${KDE4_ENABLE_EXCEPTIONS})
|
|||
include_directories(
|
||||
${UUID_INCLUDE_DIRS}
|
||||
${BLKID_INCLUDE_DIRS}
|
||||
lib/
|
||||
src/
|
||||
)
|
||||
|
||||
add_subdirectory(lib)
|
||||
add_subdirectory(src)
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
# Copyright (C) 2008 by Volker Lanz <vl@fidra.de>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the
|
||||
# Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
add_subdirectory(fatlabel)
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
# Copyright (C) 2010 by Volker Lanz <vl@fidra.de>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the
|
||||
# Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -fno-strict-aliasing -fvisibility=hidden")
|
||||
|
||||
file(GLOB libfatlabel_SRCS
|
||||
*.c
|
||||
)
|
||||
|
||||
include_directories(.)
|
||||
|
||||
############################################
|
||||
|
||||
add_library(libfatlabel SHARED ${libfatlabel_SRCS})
|
||||
target_link_libraries(libfatlabel)
|
||||
|
||||
set_target_properties(libfatlabel PROPERTIES
|
||||
PREFIX ""
|
||||
OUTPUT_NAME "libpartitionmanagerfatlabel"
|
||||
)
|
||||
|
||||
install(TARGETS libfatlabel ${INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
|
||||
############################################
|
||||
|
||||
option(MLABEL_EXECUTABLE "Build a test executable for libfatlabel" OFF)
|
||||
mark_as_advanced(MLABEL_EXECUTABLE)
|
||||
|
||||
if (MLABEL_EXECUTABLE)
|
||||
set(app_SRCS app/main.c)
|
||||
add_executable(fatlabel ${app_SRCS})
|
||||
target_link_libraries(fatlabel libfatlabel)
|
||||
install(TARGETS fatlabel ${INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
endif (MLABEL_EXECUTABLE)
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2010 by Volker Lanz <vl@fidra.de> *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
|
||||
***************************************************************************/
|
||||
|
||||
#include "fatlabel/fatlabel.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
fprintf(stderr, "usage: %s <device> <label>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* deviceName = argv[1];
|
||||
const char* newLabel = argv[2];
|
||||
|
||||
int ret = fatlabel_set_label(deviceName, newLabel);
|
||||
|
||||
switch(ret)
|
||||
{
|
||||
case -1:
|
||||
fprintf(stderr, "Label too long\n");
|
||||
break;
|
||||
|
||||
case -2:
|
||||
fprintf(stderr, "%s: Cannot initialize drive\n", argv[0]);
|
||||
break;
|
||||
|
||||
case -3:
|
||||
fprintf(stderr, "vfat lookup failed\n");
|
||||
break;
|
||||
|
||||
case 0:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -1,392 +0,0 @@
|
|||
/* Copyright 1997,2001-2003 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Buffer read/write module
|
||||
*/
|
||||
|
||||
#include "msdos.h"
|
||||
#include "mtools.h"
|
||||
#include "buffer.h"
|
||||
#include "force_io.h"
|
||||
#include "stream.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define ROUND_DOWN(value, grain) ((value) - (value) % (grain))
|
||||
#define ROUND_UP(value, grain) ROUND_DOWN((value) + (grain)-1, (grain))
|
||||
|
||||
struct Buffer_t
|
||||
{
|
||||
struct Class_t *Class;
|
||||
int refs;
|
||||
struct Stream_t *Next;
|
||||
struct Stream_t *Buffer;
|
||||
|
||||
size_t size; /* size of read/write buffer */
|
||||
int dirty; /* is the buffer dirty? */
|
||||
|
||||
size_t sectorSize; /* sector size: all operations happen in multiples of this */
|
||||
size_t cylinderSize; /* cylinder size: preferred alignemnt, but for efficiency, less data may be read */
|
||||
int ever_dirty; /* was the buffer ever dirty? */
|
||||
size_t dirty_pos;
|
||||
size_t dirty_end;
|
||||
off_t current; /* first sector in buffer */
|
||||
size_t cur_size; /* the current size */
|
||||
char *buf; /* disk read/write buffer */
|
||||
};
|
||||
|
||||
/*
|
||||
* Flush a dirty buffer to disk. Resets Buffer->dirty to zero.
|
||||
* All errors are fatal.
|
||||
*/
|
||||
|
||||
static int _buf_flush(struct Buffer_t *Buffer)
|
||||
{
|
||||
if (!Buffer->Next || !Buffer->dirty)
|
||||
return 0;
|
||||
|
||||
if(Buffer->current < 0L)
|
||||
{
|
||||
fprintf(stderr,"Should not happen\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = force_write(Buffer->Next, Buffer->buf + Buffer->dirty_pos, Buffer->current + Buffer->dirty_pos, Buffer->dirty_end - Buffer->dirty_pos);
|
||||
|
||||
if (ret != (signed int) (Buffer->dirty_end - Buffer->dirty_pos))
|
||||
{
|
||||
if (ret < 0)
|
||||
perror("buffer_flush: write");
|
||||
else
|
||||
fprintf(stderr,"buffer_flush: short write\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Buffer->dirty = 0;
|
||||
Buffer->dirty_end = 0;
|
||||
Buffer->dirty_pos = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int invalidate_buffer(struct Buffer_t *Buffer, off_t start)
|
||||
{
|
||||
if(_buf_flush(Buffer) < 0)
|
||||
return -1;
|
||||
|
||||
/* start reading at the beginning of start's sector
|
||||
* don't start reading too early, or we might not even reach
|
||||
* start */
|
||||
Buffer->current = ROUND_DOWN(start, Buffer->sectorSize);
|
||||
Buffer->cur_size = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef OFFSET
|
||||
#define OFFSET (start - This->current)
|
||||
|
||||
typedef enum position_t
|
||||
{
|
||||
OUTSIDE,
|
||||
APPEND,
|
||||
INSIDE,
|
||||
ERROR
|
||||
} position_t;
|
||||
|
||||
static position_t isInBuffer(struct Buffer_t *This, off_t start, size_t *len)
|
||||
{
|
||||
if(start >= This->current && start < This->current + (off_t)This->cur_size)
|
||||
{
|
||||
maximize(*len, This->cur_size - OFFSET);
|
||||
return INSIDE;
|
||||
}
|
||||
else if(start == This->current + (off_t)This->cur_size && This->cur_size < This->size && *len >= This->sectorSize)
|
||||
{
|
||||
/* append to the buffer for this, three conditions have to
|
||||
* be met:
|
||||
* 1. The start falls exactly at the end of the currently
|
||||
* loaded data
|
||||
* 2. There is still space
|
||||
* 3. We append at least one sector
|
||||
*/
|
||||
maximize(*len, This->size - This->cur_size);
|
||||
*len = ROUND_DOWN(*len, This->sectorSize);
|
||||
return APPEND;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(invalidate_buffer(This, start) < 0)
|
||||
return ERROR;
|
||||
|
||||
maximize(*len, This->cylinderSize - OFFSET);
|
||||
maximize(*len, This->cylinderSize - This->current % This->cylinderSize);
|
||||
|
||||
return OUTSIDE;
|
||||
}
|
||||
}
|
||||
|
||||
static int buf_read(struct Stream_t *Stream, char *buf, off_t start, size_t len)
|
||||
{
|
||||
DeclareThis(struct Buffer_t);
|
||||
|
||||
if(!len)
|
||||
return 0;
|
||||
|
||||
switch(isInBuffer(This, start, &len))
|
||||
{
|
||||
case OUTSIDE:
|
||||
case APPEND:
|
||||
{
|
||||
/* always load until the end of the cylinder */
|
||||
size_t length = This->cylinderSize - (This->current + This->cur_size) % This->cylinderSize;
|
||||
maximize(length, This->size - This->cur_size);
|
||||
|
||||
/* read it! */
|
||||
int ret = READS(This->Next, This->buf + This->cur_size, This->current + This->cur_size, length);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
This->cur_size += ret;
|
||||
|
||||
if (This->current + (off_t)This->cur_size < start)
|
||||
{
|
||||
fprintf(stderr, "Short buffer fill\n");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case INSIDE:
|
||||
/* nothing to do */
|
||||
break;
|
||||
|
||||
case ERROR:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int offset = OFFSET;
|
||||
char* disk_ptr = This->buf + offset;
|
||||
maximize(len, This->cur_size - offset);
|
||||
memcpy(buf, disk_ptr, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int buf_write(struct Stream_t *Stream, char *buf, off_t start, size_t len)
|
||||
{
|
||||
DeclareThis(struct Buffer_t);
|
||||
size_t offset;
|
||||
|
||||
if(!len)
|
||||
return 0;
|
||||
|
||||
This->ever_dirty = 1;
|
||||
|
||||
switch(isInBuffer(This, start, &len))
|
||||
{
|
||||
case OUTSIDE:
|
||||
if(start % This->cylinderSize || len < This->sectorSize)
|
||||
{
|
||||
size_t readSize;
|
||||
int ret;
|
||||
|
||||
readSize = This->cylinderSize - This->current % This->cylinderSize;
|
||||
|
||||
ret = READS(This->Next, This->buf, This->current, readSize);
|
||||
/* read it! */
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if(ret % This->sectorSize)
|
||||
{
|
||||
fprintf(stderr, "Weird: read size (%d) not a multiple of sector size (%d)\n", ret, (int) This->sectorSize);
|
||||
|
||||
ret -= ret % This->sectorSize;
|
||||
if(ret == 0)
|
||||
{
|
||||
fprintf(stderr, "Nothing left\n");
|
||||
return -1; // TODO: check in caller
|
||||
}
|
||||
}
|
||||
|
||||
This->cur_size = ret;
|
||||
|
||||
/* for dosemu. Autoextend size */
|
||||
if(!This->cur_size)
|
||||
{
|
||||
memset(This->buf,0,readSize);
|
||||
This->cur_size = readSize;
|
||||
}
|
||||
offset = OFFSET;
|
||||
break;
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
|
||||
case APPEND:
|
||||
len = ROUND_DOWN(len, This->sectorSize);
|
||||
offset = OFFSET;
|
||||
maximize(len, This->size - offset);
|
||||
This->cur_size += len;
|
||||
if(This->Next->Class->pre_allocate)
|
||||
PRE_ALLOCATE(This->Next,
|
||||
This->current + This->cur_size);
|
||||
break;
|
||||
|
||||
case INSIDE:
|
||||
/* nothing to do */
|
||||
offset = OFFSET;
|
||||
maximize(len, This->cur_size - offset);
|
||||
break;
|
||||
|
||||
case ERROR:
|
||||
return -1;
|
||||
|
||||
default:
|
||||
return -1; // TODO: check in caller
|
||||
}
|
||||
|
||||
char* disk_ptr = This->buf + offset;
|
||||
|
||||
/* extend if we write beyond end */
|
||||
if(offset + len > This->cur_size) {
|
||||
len -= (offset + len) % This->sectorSize;
|
||||
This->cur_size = len + offset;
|
||||
}
|
||||
|
||||
memcpy(disk_ptr, buf, len);
|
||||
|
||||
if(!This->dirty || offset < This->dirty_pos)
|
||||
This->dirty_pos = ROUND_DOWN(offset, This->sectorSize);
|
||||
|
||||
if(!This->dirty || offset + len > This->dirty_end)
|
||||
This->dirty_end = ROUND_UP(offset + len, This->sectorSize);
|
||||
|
||||
if(This->dirty_end > This->cur_size)
|
||||
{
|
||||
fprintf(stderr, "Internal error, dirty end too big dirty_end=%x cur_size=%x len=%x offset=%d sectorSize=%x\n",
|
||||
(unsigned int) This->dirty_end,
|
||||
(unsigned int) This->cur_size,
|
||||
(unsigned int) len,
|
||||
(int) offset, (int) This->sectorSize);
|
||||
fprintf(stderr, "offset + len + grain - 1 = %x\n", (int) (offset + len + This->sectorSize - 1));
|
||||
fprintf(stderr, "ROUNDOWN(offset + len + grain - 1) = %x\n", (int)ROUND_DOWN(offset + len + This->sectorSize - 1, This->sectorSize));
|
||||
fprintf(stderr, "This->dirty = %d\n", This->dirty);
|
||||
return -1; // TODO: check in caller
|
||||
}
|
||||
|
||||
This->dirty = 1;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int buf_flush(struct Stream_t *Stream)
|
||||
{
|
||||
int ret;
|
||||
DeclareThis(struct Buffer_t);
|
||||
|
||||
if (!This->ever_dirty)
|
||||
return 0;
|
||||
ret = _buf_flush(This);
|
||||
if(ret == 0)
|
||||
This->ever_dirty = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int buf_free(struct Stream_t *Stream)
|
||||
{
|
||||
DeclareThis(struct Buffer_t);
|
||||
|
||||
if(This->buf)
|
||||
free(This->buf);
|
||||
This->buf = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct Class_t BufferClass =
|
||||
{
|
||||
buf_read,
|
||||
buf_write,
|
||||
buf_flush,
|
||||
buf_free,
|
||||
0, /* set_geom */
|
||||
get_data_pass_through, /* get_data */
|
||||
0, /* pre-allocate */
|
||||
get_dosConvert_pass_through /* dos convert */
|
||||
};
|
||||
|
||||
struct Stream_t *buf_init(struct Stream_t *Next, int size, int cylinderSize, int sectorSize)
|
||||
{
|
||||
struct Buffer_t *Buffer;
|
||||
struct Stream_t *Stream;
|
||||
|
||||
if(size % cylinderSize != 0)
|
||||
{
|
||||
fprintf(stderr, "size not multiple of cylinder size\n");
|
||||
return NULL; // TODO: check in caller
|
||||
}
|
||||
|
||||
if(cylinderSize % sectorSize != 0)
|
||||
{
|
||||
fprintf(stderr, "cylinder size not multiple of sector size\n");
|
||||
return NULL; // TODO: check in caller
|
||||
}
|
||||
|
||||
if(Next->Buffer)
|
||||
{
|
||||
Next->refs--;
|
||||
Next->Buffer->refs++;
|
||||
return Next->Buffer;
|
||||
}
|
||||
|
||||
Stream = (struct Stream_t *) malloc (sizeof(struct Buffer_t));
|
||||
|
||||
if(!Stream)
|
||||
return 0;
|
||||
|
||||
Buffer = (struct Buffer_t *) Stream;
|
||||
Buffer->buf = malloc(size);
|
||||
|
||||
if (!Buffer->buf)
|
||||
{
|
||||
free(Stream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Buffer->size = size;
|
||||
Buffer->dirty = 0;
|
||||
Buffer->cylinderSize = cylinderSize;
|
||||
Buffer->sectorSize = sectorSize;
|
||||
|
||||
Buffer->ever_dirty = 0;
|
||||
Buffer->dirty_pos = 0;
|
||||
Buffer->dirty_end = 0;
|
||||
Buffer->current = 0L;
|
||||
Buffer->cur_size = 0; /* buffer currently empty */
|
||||
|
||||
Buffer->Next = Next;
|
||||
Buffer->Class = &BufferClass;
|
||||
Buffer->refs = 1;
|
||||
Buffer->Buffer = 0;
|
||||
Buffer->Next->Buffer = (struct Stream_t *) Buffer;
|
||||
|
||||
return Stream;
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
/* Copyright 1996,1997,2001,2002,2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MTOOLS_BUFFER_H
|
||||
#define MTOOLS_BUFFER_H
|
||||
|
||||
struct Stream_t;
|
||||
|
||||
struct Stream_t *buf_init(struct Stream_t *Next, int size, int cylinderSize, int sectorSize);
|
||||
|
||||
#endif
|
|
@ -1,325 +0,0 @@
|
|||
/* Copyright 2008,2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Various character set conversions used by mtools
|
||||
*/
|
||||
#include "msdos.h"
|
||||
#include "mtools.h"
|
||||
#include "file_name.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <iconv.h>
|
||||
#include <langinfo.h>
|
||||
#include <string.h>
|
||||
|
||||
struct doscp_t
|
||||
{
|
||||
iconv_t from;
|
||||
iconv_t to;
|
||||
} doscp_t;
|
||||
|
||||
static unsigned int mtools_default_codepage = 850;
|
||||
static const char *wcharCp = NULL;
|
||||
|
||||
static const char* wcharTries[] =
|
||||
{
|
||||
"WCHAR_T",
|
||||
"UTF-32BE", "UTF-32LE",
|
||||
"UTF-16BE", "UTF-16LE",
|
||||
"UTF-32", "UTF-16",
|
||||
"UCS-4BE", "UCS-4LE",
|
||||
"UCS-2BE", "UCS-2LE",
|
||||
"UCS-4", "UCS-2"
|
||||
};
|
||||
|
||||
static const wchar_t *testString = L"ab";
|
||||
|
||||
static int tryConvert(const char *testCp)
|
||||
{
|
||||
char *inbuf = (char *)testString;
|
||||
size_t inbufLen = 2*sizeof(wchar_t);
|
||||
char outbuf[3];
|
||||
char *outbufP = outbuf;
|
||||
size_t outbufLen = 2*sizeof(char);
|
||||
iconv_t test = iconv_open("ASCII", testCp);
|
||||
|
||||
if(test == (iconv_t) -1)
|
||||
goto fail0;
|
||||
|
||||
size_t res = iconv(test, &inbuf, &inbufLen, &outbufP, &outbufLen);
|
||||
|
||||
if(res != 0 || outbufLen != 0 || inbufLen != 0)
|
||||
goto fail;
|
||||
|
||||
if(memcmp(outbuf, "ab", 2))
|
||||
goto fail;
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
iconv_close(test);
|
||||
|
||||
fail0:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *getWcharCp()
|
||||
{
|
||||
if(wcharCp != NULL)
|
||||
return wcharCp;
|
||||
|
||||
unsigned int i;
|
||||
for(i = 0; i < sizeof(wcharTries) / sizeof(wcharTries[0]); i++)
|
||||
if(tryConvert(wcharTries[i]))
|
||||
return wcharCp = wcharTries[i];
|
||||
|
||||
fprintf(stderr, "No codepage found for wchar_t\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct doscp_t *cp_open(int codepage)
|
||||
{
|
||||
char dosCp[17];
|
||||
|
||||
if(codepage == 0)
|
||||
codepage = mtools_default_codepage;
|
||||
|
||||
if(codepage < 0 || codepage > 9999)
|
||||
{
|
||||
fprintf(stderr, "Bad codepage %d\n", codepage);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(getWcharCp() == NULL)
|
||||
return NULL;
|
||||
|
||||
sprintf(dosCp, "CP%d", codepage);
|
||||
|
||||
iconv_t *from = iconv_open(wcharCp, dosCp);
|
||||
|
||||
if(from == (iconv_t)-1)
|
||||
{
|
||||
fprintf(stderr, "Error converting to codepage %d %s\n", codepage, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sprintf(dosCp, "CP%d//TRANSLIT", codepage);
|
||||
iconv_t *to = iconv_open(dosCp, wcharCp);
|
||||
if(to == (iconv_t)-1)
|
||||
{
|
||||
/* Transliteration not supported? */
|
||||
sprintf(dosCp, "CP%d", codepage);
|
||||
to = iconv_open(dosCp, wcharCp);
|
||||
}
|
||||
|
||||
if(to == (iconv_t)-1)
|
||||
{
|
||||
iconv_close(from);
|
||||
fprintf(stderr, "Error converting to codepage %d %s\n", codepage, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct doscp_t *ret = New(struct doscp_t);
|
||||
|
||||
if(ret == NULL)
|
||||
return ret;
|
||||
|
||||
ret->from = from;
|
||||
ret->to = to;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void cp_close(struct doscp_t *cp)
|
||||
{
|
||||
iconv_close(cp->to);
|
||||
iconv_close(cp->from);
|
||||
free(cp);
|
||||
}
|
||||
|
||||
int dos_to_wchar(struct doscp_t *cp, char *dos, wchar_t *wchar, size_t len)
|
||||
{
|
||||
size_t in_len = len;
|
||||
size_t out_len = len*sizeof(wchar_t);
|
||||
|
||||
wchar_t *dptr = wchar;
|
||||
|
||||
int r = iconv(cp->from, &dos, &in_len, (char **)&dptr, &out_len);
|
||||
|
||||
if(r < 0)
|
||||
return r;
|
||||
|
||||
*dptr = L'\0';
|
||||
|
||||
return dptr - wchar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts len wide character to destination. Caller's responsibility to
|
||||
* ensure that dest is large enough.
|
||||
* mangled will be set if there has been an untranslatable character.
|
||||
*/
|
||||
static int safe_iconv(iconv_t conv, const wchar_t *wchar, char *dest, size_t len, int *mangled)
|
||||
{
|
||||
size_t in_len = len * sizeof(wchar_t);
|
||||
size_t out_len = len * 4;
|
||||
char *dptr = dest;
|
||||
|
||||
while (in_len > 0)
|
||||
{
|
||||
int r = iconv(conv, (char**)&wchar, &in_len, &dptr, &out_len);
|
||||
|
||||
/* everything transformed, or error that is _not_ a bad character */
|
||||
if(r >= 0 || errno != EILSEQ)
|
||||
break;
|
||||
|
||||
*mangled |= 1;
|
||||
|
||||
if(dptr)
|
||||
*dptr++ = '_';
|
||||
|
||||
in_len--;
|
||||
|
||||
wchar++;
|
||||
out_len--;
|
||||
}
|
||||
|
||||
len = dptr-dest; /* how many dest characters have there been generated */
|
||||
|
||||
/* eliminate question marks which might have been formed by
|
||||
untransliterable characters */
|
||||
unsigned int i;
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
if (dest[i] == '?')
|
||||
{
|
||||
dest[i] = '_';
|
||||
*mangled |= 1;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void wchar_to_dos(struct doscp_t *cp, wchar_t *wchar, char *dos, size_t len, int *mangled)
|
||||
{
|
||||
safe_iconv(cp->to, wchar, dos, len, mangled);
|
||||
}
|
||||
|
||||
static iconv_t to_native = NULL;
|
||||
|
||||
static int initialize_to_native(void)
|
||||
{
|
||||
if(to_native != NULL)
|
||||
return 0;
|
||||
|
||||
char *li = nl_langinfo(CODESET);
|
||||
int len = strlen(li) + 11;
|
||||
|
||||
if(getWcharCp() == NULL)
|
||||
return -1; // TODO: make caller check return code
|
||||
|
||||
char *cp = malloc(len);
|
||||
strcpy(cp, li);
|
||||
strcat(cp, "//TRANSLIT");
|
||||
|
||||
to_native = iconv_open(cp, wcharCp);
|
||||
|
||||
if(to_native == (iconv_t) -1)
|
||||
to_native = iconv_open(li, wcharCp);
|
||||
|
||||
if(to_native == (iconv_t) -1)
|
||||
fprintf(stderr, "Could not allocate iconv for %s\n", cp);
|
||||
|
||||
free(cp);
|
||||
|
||||
if(to_native == (iconv_t) -1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert wchar string to native, converting at most len wchar characters
|
||||
* Returns number of generated native characters
|
||||
*/
|
||||
int wchar_to_native(const wchar_t *wchar, char *native, size_t len)
|
||||
{
|
||||
int mangled;
|
||||
|
||||
initialize_to_native();
|
||||
|
||||
len = wcsnlen(wchar,len);
|
||||
|
||||
int r = safe_iconv(to_native, wchar, native, len, &mangled);
|
||||
|
||||
native[r] = '\0';
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert native string to wchar string, converting at most len wchar
|
||||
* characters. If end is supplied, stop conversion when source pointer
|
||||
* exceeds end. Returns number of converted wchars
|
||||
*/
|
||||
int native_to_wchar(const char *native, wchar_t *wchar, size_t len, const char *end, int *mangled)
|
||||
{
|
||||
mbstate_t ps;
|
||||
|
||||
memset(&ps, 0, sizeof(ps));
|
||||
|
||||
unsigned int i;
|
||||
for(i = 0; i < len && (native < end || !end); i++)
|
||||
{
|
||||
int r = mbrtowc(wchar+i, native, len, &ps);
|
||||
|
||||
if(r < 0)
|
||||
{
|
||||
/* Unconvertible character. Just pretend it's Latin1
|
||||
encoded (if valid Latin1 character) or substitue
|
||||
with an underscore if not
|
||||
*/
|
||||
|
||||
char c = *native;
|
||||
if(c >= '\xa0' && c < '\xff')
|
||||
wchar[i] = c & 0xff;
|
||||
else
|
||||
wchar[i] = '_';
|
||||
|
||||
memset(&ps, 0, sizeof(ps));
|
||||
|
||||
r = 1;
|
||||
}
|
||||
|
||||
if(r == 0)
|
||||
break;
|
||||
|
||||
native += r;
|
||||
}
|
||||
|
||||
if(mangled && end && native < end)
|
||||
*mangled |= 3;
|
||||
|
||||
wchar[i]='\0';
|
||||
|
||||
return (int)i;
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/* Copyright 1986-1992 Emmet P. Gray.
|
||||
* Copyright 1996-2003,2006,2007,2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "mtools.h"
|
||||
#include "devices.h"
|
||||
|
||||
int init_geom(int UNUSED(fd), struct device *dev, struct device *orig_dev, struct stat64 UNUSED(*statbuf))
|
||||
{
|
||||
if(!orig_dev || !orig_dev->tracks || !dev || !dev->tracks)
|
||||
return 0; /* no original device. This is ok */
|
||||
|
||||
return(orig_dev->tracks != dev->tracks ||
|
||||
orig_dev->heads != dev->heads ||
|
||||
orig_dev->sectors != dev->sectors);
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
/* Copyright 1996-2005,2007-2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MTOOLS_DEVICES_H
|
||||
#define MTOOLS_DEVICES_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
struct device
|
||||
{
|
||||
const char *name; /* full path to device */
|
||||
int fat_bits; /* FAT encoding scheme */
|
||||
unsigned int mode; /* any special open() flags */
|
||||
unsigned int tracks; /* tracks */
|
||||
unsigned int heads; /* heads */
|
||||
unsigned int sectors; /* sectors */
|
||||
unsigned int hidden; /* number of hidden sectors. Used for mformatting partitioned devices */
|
||||
off_t offset; /* skip this many bytes */
|
||||
unsigned int partition;
|
||||
unsigned int misc_flags;
|
||||
|
||||
/* Linux only stuff */
|
||||
unsigned int ssize;
|
||||
unsigned int use_2m;
|
||||
|
||||
/* internal variables */
|
||||
int file_nr; /* used during parsing */
|
||||
unsigned int blocksize; /* size of disk block in bytes */
|
||||
int codepage; /* codepage for shortname encoding */
|
||||
const char *cfg_filename; /* used for debugging purposes */
|
||||
};
|
||||
|
||||
int init_geom(int fd, struct device *dev, struct device *orig_dev, struct stat64 *statbuf);
|
||||
|
||||
#endif
|
|
@ -1,335 +0,0 @@
|
|||
/* Copyright 1998,2001-2003,2007-2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "vfat.h"
|
||||
#include "dirCache.h"
|
||||
#include "file.h"
|
||||
#include "mtools.h"
|
||||
|
||||
#include <wctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define BITS_PER_INT (sizeof(unsigned int) * 8)
|
||||
|
||||
static unsigned int rol(unsigned int arg, int shift)
|
||||
{
|
||||
arg &= 0xffffffff; /* for 64 bit machines */
|
||||
return (arg << shift) | (arg >> (32 - shift));
|
||||
}
|
||||
|
||||
static int calcHash(wchar_t *name)
|
||||
{
|
||||
unsigned long hash = 0;
|
||||
int i = 0;
|
||||
|
||||
while(*name)
|
||||
{
|
||||
/* rotate it */
|
||||
hash = rol(hash,5); /* a shift of 5 makes sure we spread quickly
|
||||
* over the whole width, moreover, 5 is
|
||||
* prime with 32, which makes sure that
|
||||
* successive letters cannot cover each
|
||||
* other easily */
|
||||
wchar_t c = towupper(*name);
|
||||
|
||||
hash ^= (c * (c+2)) ^ (i * (i+2));
|
||||
hash &= 0xffffffff;
|
||||
|
||||
i++;
|
||||
name++;
|
||||
}
|
||||
hash = hash * (hash + 2);
|
||||
|
||||
/* the following two xors make sure all info is spread evenly over all
|
||||
* bytes. Important if we only keep the low order bits later on */
|
||||
hash ^= (hash & 0xfff) << 12;
|
||||
hash ^= (hash & 0xff000) << 24;
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static int addBit(unsigned int *bitmap, int hash, int checkOnly)
|
||||
{
|
||||
int bit = 1 << (hash % BITS_PER_INT);
|
||||
int entry = (hash / BITS_PER_INT) % DC_BITMAP_SIZE;
|
||||
|
||||
if(checkOnly)
|
||||
return bitmap[entry] & bit;
|
||||
|
||||
bitmap[entry] |= bit;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _addHash(struct dirCache_t *cache, unsigned int hash, int checkOnly)
|
||||
{
|
||||
return
|
||||
addBit(cache->bm0, hash, checkOnly) &&
|
||||
addBit(cache->bm1, rol(hash,12), checkOnly) &&
|
||||
addBit(cache->bm2, rol(hash,24), checkOnly);
|
||||
}
|
||||
|
||||
|
||||
static void addNameToHash(struct dirCache_t *cache, wchar_t *name)
|
||||
{
|
||||
_addHash(cache, calcHash(name), 0);
|
||||
}
|
||||
|
||||
static void hashDce(struct dirCache_t *cache, struct dirCacheEntry_t *dce)
|
||||
{
|
||||
if(dce->beginSlot != cache->nrHashed)
|
||||
return;
|
||||
|
||||
cache->nrHashed = dce->endSlot;
|
||||
|
||||
if(dce->longName)
|
||||
addNameToHash(cache, dce->longName);
|
||||
|
||||
addNameToHash(cache, dce->shortName);
|
||||
}
|
||||
|
||||
int isHashed(struct dirCache_t *cache, wchar_t *name)
|
||||
{
|
||||
return _addHash(cache, calcHash(name), 1);
|
||||
}
|
||||
|
||||
int growDirCache(struct dirCache_t *cache, int slot)
|
||||
{
|
||||
if(slot < 0)
|
||||
{
|
||||
fprintf(stderr, "Bad slot %d\n", slot);
|
||||
return -1; // TODO: check in caller
|
||||
}
|
||||
|
||||
if( cache->nr_entries <= slot)
|
||||
{
|
||||
cache->entries = realloc(cache->entries, (slot+1) * 2 * sizeof(struct dirCacheEntry_t *));
|
||||
|
||||
if(!cache->entries)
|
||||
return -1;
|
||||
|
||||
for (int i = cache->nr_entries; i < (slot+1) * 2; i++)
|
||||
cache->entries[i] = 0;
|
||||
|
||||
cache->nr_entries = (slot+1) * 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dirCache_t *allocDirCache(struct Stream_t *Stream, int slot)
|
||||
{
|
||||
if(slot < 0)
|
||||
{
|
||||
fprintf(stderr, "Bad slot %d\n", slot);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct dirCache_t **dcp = getDirCacheP(Stream);
|
||||
|
||||
if(!*dcp)
|
||||
{
|
||||
*dcp = New(struct dirCache_t);
|
||||
|
||||
if(!*dcp)
|
||||
return 0;
|
||||
|
||||
(*dcp)->entries = NewArray((slot+1)*2+5, struct dirCacheEntry_t *);
|
||||
|
||||
if(!(*dcp)->entries)
|
||||
{
|
||||
free(*dcp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
(*dcp)->nr_entries = (slot+1) * 2;
|
||||
memset( (*dcp)->bm0, 0, DC_BITMAP_SIZE);
|
||||
memset( (*dcp)->bm1, 0, DC_BITMAP_SIZE);
|
||||
memset( (*dcp)->bm2, 0, DC_BITMAP_SIZE);
|
||||
(*dcp)->nrHashed = 0;
|
||||
}
|
||||
else if(growDirCache(*dcp, slot) < 0)
|
||||
return 0;
|
||||
|
||||
return *dcp;
|
||||
}
|
||||
|
||||
static int freeDirCacheRange(struct dirCache_t *cache, unsigned int beginSlot, unsigned int endSlot)
|
||||
{
|
||||
if(endSlot < beginSlot)
|
||||
{
|
||||
fprintf(stderr, "Bad slots %d %d in free range\n", beginSlot, endSlot);
|
||||
return -1; // TODO: check in caller
|
||||
}
|
||||
|
||||
while(beginSlot < endSlot)
|
||||
{
|
||||
struct dirCacheEntry_t *entry = cache->entries[beginSlot];
|
||||
|
||||
if(!entry)
|
||||
{
|
||||
beginSlot++;
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned int clearEnd = entry->endSlot;
|
||||
|
||||
if(clearEnd > endSlot)
|
||||
clearEnd = endSlot;
|
||||
|
||||
unsigned int clearBegin = beginSlot;
|
||||
|
||||
for(unsigned int i = clearBegin; i <clearEnd; i++)
|
||||
cache->entries[i] = 0;
|
||||
|
||||
if(entry->endSlot == endSlot)
|
||||
entry->endSlot = beginSlot;
|
||||
else if(entry->beginSlot == beginSlot)
|
||||
entry->beginSlot = endSlot;
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Internal error, non contiguous de-allocation\n");
|
||||
fprintf(stderr, "%d %d\n", beginSlot, endSlot);
|
||||
fprintf(stderr, "%d %d\n", entry->beginSlot, entry->endSlot);
|
||||
return -1; // TODO: check in caller
|
||||
}
|
||||
|
||||
if(entry->beginSlot == entry->endSlot)
|
||||
{
|
||||
if(entry->longName)
|
||||
free(entry->longName);
|
||||
|
||||
if(entry->shortName)
|
||||
free(entry->shortName);
|
||||
|
||||
free(entry);
|
||||
}
|
||||
|
||||
beginSlot = clearEnd;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dirCacheEntry_t *allocDirCacheEntry(struct dirCache_t *cache, int beginSlot, int endSlot, dirCacheEntryType_t type)
|
||||
{
|
||||
if(growDirCache(cache, endSlot) < 0)
|
||||
return 0;
|
||||
|
||||
struct dirCacheEntry_t *entry = New(struct dirCacheEntry_t);
|
||||
|
||||
if(!entry)
|
||||
return 0;
|
||||
|
||||
entry->type = type;
|
||||
entry->longName = 0;
|
||||
entry->shortName = 0;
|
||||
entry->beginSlot = beginSlot;
|
||||
entry->endSlot = endSlot;
|
||||
|
||||
freeDirCacheRange(cache, beginSlot, endSlot);
|
||||
|
||||
for(int i = beginSlot; i<endSlot; i++)
|
||||
cache->entries[i] = entry;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
struct dirCacheEntry_t *addUsedEntry(struct dirCache_t *cache, int beginSlot, int endSlot, wchar_t *longName, wchar_t *shortName, struct directory *dir)
|
||||
{
|
||||
if(endSlot < beginSlot)
|
||||
{
|
||||
fprintf(stderr,"Bad slots %d %d in add used entry\n", beginSlot, endSlot);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct dirCacheEntry_t *entry = allocDirCacheEntry(cache, beginSlot, endSlot, DCET_USED);
|
||||
if(!entry)
|
||||
return 0;
|
||||
|
||||
entry->beginSlot = beginSlot;
|
||||
entry->endSlot = endSlot;
|
||||
|
||||
if(longName)
|
||||
entry->longName = wcsdup(longName);
|
||||
|
||||
entry->shortName = wcsdup(shortName);
|
||||
entry->dir = *dir;
|
||||
|
||||
hashDce(cache, entry);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static void mergeFreeSlots(struct dirCache_t *cache, int slot)
|
||||
{
|
||||
if(slot == 0)
|
||||
return;
|
||||
|
||||
struct dirCacheEntry_t *previous = cache->entries[slot-1];
|
||||
struct dirCacheEntry_t *next = cache->entries[slot];
|
||||
|
||||
if(next && next->type == DCET_FREE && previous && previous->type == DCET_FREE)
|
||||
{
|
||||
for(unsigned int i = next->beginSlot; i < next->endSlot; i++)
|
||||
cache->entries[i] = previous;
|
||||
|
||||
previous->endSlot = next->endSlot;
|
||||
free(next);
|
||||
}
|
||||
}
|
||||
|
||||
struct dirCacheEntry_t *addFreeEntry(struct dirCache_t *cache, unsigned int beginSlot, unsigned int endSlot)
|
||||
{
|
||||
if(beginSlot < cache->nrHashed)
|
||||
cache->nrHashed = beginSlot;
|
||||
|
||||
if(endSlot < beginSlot)
|
||||
{
|
||||
fprintf(stderr, "Bad slots %d %d in add free entry\n", beginSlot, endSlot);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(endSlot == beginSlot)
|
||||
return 0;
|
||||
|
||||
allocDirCacheEntry(cache, beginSlot, endSlot, DCET_FREE);
|
||||
mergeFreeSlots(cache, beginSlot);
|
||||
mergeFreeSlots(cache, endSlot);
|
||||
|
||||
return cache->entries[beginSlot];
|
||||
}
|
||||
|
||||
struct dirCacheEntry_t *addEndEntry(struct dirCache_t *cache, int pos)
|
||||
{
|
||||
return allocDirCacheEntry(cache, pos, pos+1, DCET_END);
|
||||
}
|
||||
|
||||
void freeDirCache(struct Stream_t *Stream)
|
||||
{
|
||||
struct dirCache_t **dcp = getDirCacheP(Stream);
|
||||
struct dirCache_t *cache = *dcp;
|
||||
|
||||
if(cache)
|
||||
{
|
||||
freeDirCacheRange(cache, 0, cache->nr_entries);
|
||||
free(cache);
|
||||
*dcp = 0;
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
/* Copyright 1997,1999,2001-2003,2008,2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MTOOLS_DIRCACHE_H
|
||||
#define MTOOLS_DIRCACHE_H
|
||||
|
||||
#include "directory.h"
|
||||
|
||||
#include <wchar.h>
|
||||
|
||||
|
||||
typedef enum {
|
||||
DCET_FREE,
|
||||
DCET_USED,
|
||||
DCET_END
|
||||
} dirCacheEntryType_t;
|
||||
|
||||
#define DC_BITMAP_SIZE 128
|
||||
|
||||
struct dirCacheEntry_t {
|
||||
dirCacheEntryType_t type;
|
||||
unsigned int beginSlot;
|
||||
unsigned int endSlot;
|
||||
wchar_t *shortName;
|
||||
wchar_t *longName;
|
||||
struct directory dir;
|
||||
};
|
||||
|
||||
struct dirCache_t {
|
||||
struct dirCacheEntry_t **entries;
|
||||
int nr_entries;
|
||||
unsigned int nrHashed;
|
||||
unsigned int bm0[DC_BITMAP_SIZE];
|
||||
unsigned int bm1[DC_BITMAP_SIZE];
|
||||
unsigned int bm2[DC_BITMAP_SIZE];
|
||||
};
|
||||
|
||||
int isHashed(struct dirCache_t *cache, wchar_t *name);
|
||||
int growDirCache(struct dirCache_t *cache, int slot);
|
||||
struct dirCache_t *allocDirCache(struct Stream_t *Stream, int slot);
|
||||
struct dirCacheEntry_t *addUsedEntry(struct dirCache_t *Stream, int begin, int end, wchar_t *longName, wchar_t *shortName, struct directory *dir);
|
||||
void freeDirCache(struct Stream_t *Stream);
|
||||
struct dirCacheEntry_t *addFreeEntry(struct dirCache_t *Stream, unsigned int begin, unsigned int end);
|
||||
struct dirCacheEntry_t *addEndEntry(struct dirCache_t *Stream, int pos);
|
||||
#endif
|
|
@ -1,380 +0,0 @@
|
|||
/* Copyright 1995 David C. Niemi
|
||||
* Copyright 1996-2002,2008,2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "directory.h"
|
||||
|
||||
#include "msdos.h"
|
||||
#include "stream.h"
|
||||
#include "mtools.h"
|
||||
#include "file.h"
|
||||
#include "fs.h"
|
||||
#include "file_name.h"
|
||||
#include "fat.h"
|
||||
#include "init.h"
|
||||
#include "force_io.h"
|
||||
#include "buffer.h"
|
||||
#include "nameclash.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static const char* short_illegals = ";+=[]',\"*\\<>/?:|";
|
||||
static const char* long_illegals = "\"*\\<>/?:|\005";
|
||||
|
||||
|
||||
/*
|
||||
* Read a directory entry into caller supplied buffer
|
||||
*/
|
||||
struct directory *dir_read(struct direntry_t *entry, int *error)
|
||||
{
|
||||
int n;
|
||||
*error = 0;
|
||||
if((n=force_read(entry->Dir, (char *) (&entry->dir),
|
||||
(off_t) entry->entry * MDIR_SIZE,
|
||||
MDIR_SIZE)) != MDIR_SIZE) {
|
||||
if (n < 0) {
|
||||
*error = -1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
return &entry->dir;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make a subdirectory grow in length. Only subdirectories (not root)
|
||||
* may grow. Returns a 0 on success, 1 on failure (disk full), or -1
|
||||
* on error.
|
||||
*/
|
||||
|
||||
int dir_grow(struct Stream_t *Dir, int size)
|
||||
{
|
||||
struct Stream_t *Stream = GetFs(Dir);
|
||||
DeclareThis(struct FsPublic_t);
|
||||
int ret;
|
||||
int buflen;
|
||||
char *buffer;
|
||||
|
||||
if (!getfreeMinClusters(Dir, 1))
|
||||
return -1;
|
||||
|
||||
buflen = This->cluster_size * This->sector_size;
|
||||
|
||||
if(! (buffer=malloc(buflen)) ){
|
||||
perror("dir_grow: malloc");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset((char *) buffer, '\0', buflen);
|
||||
ret = force_write(Dir, buffer, (off_t) size * MDIR_SIZE, buflen);
|
||||
free(buffer);
|
||||
if(ret < buflen)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void low_level_dir_write(struct direntry_t *entry)
|
||||
{
|
||||
force_write(entry->Dir,
|
||||
(char *) (&entry->dir),
|
||||
(off_t) entry->entry * MDIR_SIZE, MDIR_SIZE);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Make a directory entry. Builds a directory entry based on the
|
||||
* name, attribute, starting cluster number, and size. Returns a pointer
|
||||
* to a static directory structure.
|
||||
*/
|
||||
|
||||
struct directory *mk_entry(const struct dos_name_t *dn, char attr,
|
||||
unsigned int fat, size_t size, time_t date,
|
||||
struct directory *ndir)
|
||||
{
|
||||
struct tm *now;
|
||||
time_t date2 = date;
|
||||
unsigned char hour, min_hi, min_low, sec;
|
||||
unsigned char year, month_hi, month_low, day;
|
||||
|
||||
now = localtime(&date2);
|
||||
dosnameToDirentry(dn, ndir);
|
||||
ndir->attr = attr;
|
||||
ndir->ctime_ms = 0;
|
||||
hour = now->tm_hour << 3;
|
||||
min_hi = now->tm_min >> 3;
|
||||
min_low = now->tm_min << 5;
|
||||
sec = now->tm_sec / 2;
|
||||
ndir->ctime[1] = ndir->time[1] = hour + min_hi;
|
||||
ndir->ctime[0] = ndir->time[0] = min_low + sec;
|
||||
year = (now->tm_year - 80) << 1;
|
||||
month_hi = (now->tm_mon + 1) >> 3;
|
||||
month_low = (now->tm_mon + 1) << 5;
|
||||
day = now->tm_mday;
|
||||
ndir -> adate[1] = ndir->cdate[1] = ndir->date[1] = year + month_hi;
|
||||
ndir -> adate[0] = ndir->cdate[0] = ndir->date[0] = month_low + day;
|
||||
|
||||
set_word(ndir->start, fat & 0xffff);
|
||||
set_word(ndir->startHi, fat >> 16);
|
||||
set_dword(ndir->size, size);
|
||||
return ndir;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make a directory entry from base name. This is supposed to be used
|
||||
* from places such as mmd for making special entries (".", "..", "/", ...)
|
||||
* Thus it doesn't bother with character set conversions
|
||||
*/
|
||||
struct directory *mk_entry_from_base(const char *base, char attr,
|
||||
unsigned int fat, size_t size, time_t date,
|
||||
struct directory *ndir)
|
||||
{
|
||||
struct dos_name_t dn;
|
||||
strncpy(dn.base, base, 8);
|
||||
strncpy(dn.ext, " ", 3);
|
||||
return mk_entry(&dn, attr, fat, size, date, ndir);
|
||||
}
|
||||
|
||||
void bufferize(struct Stream_t **Dir)
|
||||
{
|
||||
struct Stream_t *BDir;
|
||||
|
||||
if(!*Dir)
|
||||
return;
|
||||
|
||||
BDir = buf_init(*Dir, 64*16384, 512, MDIR_SIZE);
|
||||
|
||||
if(!BDir)
|
||||
{
|
||||
free_stream(Dir);
|
||||
*Dir = NULL;
|
||||
}
|
||||
else
|
||||
*Dir = BDir;
|
||||
}
|
||||
|
||||
void initializeDirentry(struct direntry_t *entry, struct Stream_t *Dir)
|
||||
{
|
||||
entry->entry = -1;
|
||||
entry->Dir = Dir;
|
||||
entry->beginSlot = 0;
|
||||
entry->endSlot = 0;
|
||||
}
|
||||
|
||||
int isNotFound(struct direntry_t *entry)
|
||||
{
|
||||
return entry->entry == -2;
|
||||
}
|
||||
|
||||
static int isSpecial(const char *name)
|
||||
{
|
||||
return name && (name[0] == '\0' || !strcmp(name, ".") || !strcmp(name, "..")) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int convert_to_shortname(struct doscp_t *cp, struct ClashHandling_t *ch, const char *un, struct dos_name_t *dn)
|
||||
{
|
||||
int mangled;
|
||||
|
||||
/* Then do conversion to dn */
|
||||
ch->name_converter(cp, un, &mangled, dn);
|
||||
dn->sentinel = '\0';
|
||||
|
||||
return mangled;
|
||||
}
|
||||
|
||||
static int contains_illegals(const char *string, const char *illegals, int len)
|
||||
{
|
||||
for (; *string && len--; string++)
|
||||
if ((*string < ' ' && *string != '\005' && !(*string & 0x80)) || strchr(illegals, *string))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_reserved(char *ans, int islong)
|
||||
{
|
||||
unsigned int i;
|
||||
static const char *dev3[] = {"CON", "AUX", "PRN", "NUL", " "};
|
||||
static const char *dev4[] = {"COM", "LPT" };
|
||||
|
||||
for (i = 0; i < sizeof(dev3) / sizeof(*dev3); i++)
|
||||
if (!strncasecmp(ans, dev3[i], 3) && ((islong && !ans[3]) || (!islong && !strncmp(ans + 3, " ", 5))))
|
||||
return 1;
|
||||
|
||||
for (i = 0; i < sizeof(dev4)/sizeof(*dev4); i++)
|
||||
if (!strncasecmp(ans, dev4[i], 3) && (ans[3] >= '1' && ans[3] <= '4') && ((islong && !ans[4])
|
||||
|| (!islong && !strncmp(ans + 4, " ", 4))))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static clash_action get_slots(struct Stream_t *Dir,
|
||||
struct dos_name_t *dosname,
|
||||
char *longname,
|
||||
struct scan_state *ssp,
|
||||
struct ClashHandling_t *ch)
|
||||
{
|
||||
struct direntry_t entry;
|
||||
int pessimisticShortRename = 0;
|
||||
|
||||
entry.Dir = Dir;
|
||||
|
||||
if ((is_reserved(longname, 1)) || longname[strspn(longname, ". ")] == '\0'
|
||||
|| contains_illegals(longname, long_illegals, 1024)
|
||||
|| is_reserved(dosname->base, 0)
|
||||
|| contains_illegals(dosname->base, short_illegals, 11))
|
||||
{
|
||||
return NAMEMATCH_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (lookupForInsert(Dir,
|
||||
&entry,
|
||||
dosname, longname, ssp,
|
||||
ch->ignore_entry,
|
||||
ch->source_entry,
|
||||
pessimisticShortRename &&
|
||||
ch->use_longname,
|
||||
ch->use_longname)) {
|
||||
case -1:
|
||||
return NAMEMATCH_ERROR;
|
||||
|
||||
case 0: /* Single-file error error or skip request */
|
||||
return NAMEMATCH_SKIP;
|
||||
|
||||
case 5: /* Grew directory, try again */
|
||||
return NAMEMATCH_GREW;
|
||||
|
||||
case 6:
|
||||
return NAMEMATCH_SUCCESS; /* Success */
|
||||
}
|
||||
}
|
||||
|
||||
return NAMEMATCH_ERROR;
|
||||
}
|
||||
|
||||
static int write_slots(struct Stream_t *Dir, struct dos_name_t *dosname, char *longname, struct scan_state *ssp, write_data_callback *cb, int Case)
|
||||
{
|
||||
/* write the file */
|
||||
if (fat_error(Dir))
|
||||
return -1;
|
||||
|
||||
struct direntry_t entry;
|
||||
|
||||
entry.Dir = Dir;
|
||||
entry.entry = ssp->slot;
|
||||
|
||||
native_to_wchar(longname, entry.name, MAX_VNAMELEN, 0, 0);
|
||||
|
||||
entry.name[MAX_VNAMELEN] = '\0';
|
||||
entry.dir.Case = Case & (EXTCASE | BASECASE);
|
||||
|
||||
if (cb(dosname, &entry) < 0)
|
||||
return -2;
|
||||
|
||||
if ((ssp->size_needed > 1) && (ssp->free_end - ssp->free_start >= ssp->size_needed))
|
||||
ssp->slot = write_vfat(Dir, dosname, longname, ssp->free_start, &entry);
|
||||
else
|
||||
{
|
||||
ssp->size_needed = 1;
|
||||
write_vfat(Dir, dosname, 0, ssp->free_start, &entry);
|
||||
}
|
||||
|
||||
return 0; /* Successfully wrote the file */
|
||||
}
|
||||
|
||||
int mwrite_one(struct Stream_t *Dir, const char *argname, write_data_callback *cb, struct ClashHandling_t *ch)
|
||||
{
|
||||
if (argname == NULL)
|
||||
return -1;
|
||||
|
||||
if (isSpecial(argname))
|
||||
{
|
||||
fprintf(stderr, "Cannot create entry named . or ..\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Copy original argument dstname to working value longname */
|
||||
char longname[VBUFSIZE];
|
||||
strncpy(longname, argname, VBUFSIZE - 1);
|
||||
|
||||
struct doscp_t *cp = GET_DOSCONVERT(Dir);
|
||||
struct dos_name_t dosname;
|
||||
ch->use_longname = convert_to_shortname(cp, ch, longname, &dosname);
|
||||
|
||||
ch->action[0] = ch->namematch_default[0];
|
||||
ch->action[1] = ch->namematch_default[1];
|
||||
|
||||
int expanded = 0;
|
||||
clash_action ret;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
struct scan_state scan;
|
||||
|
||||
switch((ret = get_slots(Dir, &dosname, longname, &scan, ch)))
|
||||
{
|
||||
case NAMEMATCH_ERROR:
|
||||
case NAMEMATCH_SKIP:
|
||||
return -1;
|
||||
|
||||
case NAMEMATCH_GREW:
|
||||
/* No collision, and not enough slots. Try to grow the directory */
|
||||
if (expanded)
|
||||
{
|
||||
/* Already tried this once, no good */
|
||||
fprintf(stderr, "No directory slots\n");
|
||||
return -1;
|
||||
}
|
||||
expanded = 1;
|
||||
|
||||
if (dir_grow(Dir, scan.max_entry))
|
||||
return -1;
|
||||
|
||||
continue;
|
||||
|
||||
case NAMEMATCH_SUCCESS:
|
||||
return write_slots(Dir, &dosname, longname, &scan, cb, ch->use_longname);
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Internal error: clash_action=%d\n", ret);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void init_clash_handling(struct ClashHandling_t *ch)
|
||||
{
|
||||
ch->ignore_entry = -1;
|
||||
ch->source_entry = -2;
|
||||
ch->nowarn = 0; /*Don't ask, just do default action if name collision */
|
||||
ch->namematch_default[0] = NAMEMATCH_SKIP;
|
||||
ch->namematch_default[1] = NAMEMATCH_NONE;
|
||||
ch->name_converter = NULL;
|
||||
ch->source = -2;
|
||||
}
|
||||
|
||||
void dosnameToDirentry(const struct dos_name_t *dn, struct directory *dir)
|
||||
{
|
||||
strncpy(dir->name, dn->base, 8);
|
||||
strncpy(dir->ext, dn->ext, 3);
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
/* Copyright 1996-2005,2007-2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MTOOLS_DIRECTORY_H
|
||||
#define MTOOLS_DIRECTORY_H
|
||||
|
||||
#include "vfat.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct dos_name_t;
|
||||
struct Stream_t;
|
||||
struct scan_state;
|
||||
struct ClashHandling_t;
|
||||
|
||||
struct directory
|
||||
{
|
||||
char name[8]; /* 0 file name */
|
||||
char ext[3]; /* 8 file extension */
|
||||
unsigned char attr; /* 11 attribute byte */
|
||||
unsigned char Case; /* 12 case of short filename */
|
||||
unsigned char ctime_ms; /* 13 creation time, milliseconds (?) */
|
||||
unsigned char ctime[2]; /* 14 creation time */
|
||||
unsigned char cdate[2]; /* 16 creation date */
|
||||
unsigned char adate[2]; /* 18 last access date */
|
||||
unsigned char startHi[2]; /* 20 start cluster, Hi */
|
||||
unsigned char time[2]; /* 22 time stamp */
|
||||
unsigned char date[2]; /* 24 date stamp */
|
||||
unsigned char start[2]; /* 26 starting cluster number */
|
||||
unsigned char size[4]; /* 28 size of the file */
|
||||
};
|
||||
|
||||
struct direntry_t
|
||||
{
|
||||
struct Stream_t *Dir;
|
||||
int entry; /* slot in parent directory (-3 if root) */
|
||||
struct directory dir; /* descriptor in parent directory (random if root)*/
|
||||
wchar_t name[MAX_VNAMELEN + 1]; /* name in its parent directory, or NULL if root */
|
||||
int beginSlot; /* begin and end slot, for delete */
|
||||
int endSlot;
|
||||
};
|
||||
|
||||
struct directory *mk_entry(const struct dos_name_t *filename, char attr, unsigned int fat, size_t size, time_t date, struct directory *ndir);
|
||||
struct directory *mk_entry_from_base(const char *base, char attr, unsigned int fat, size_t size, time_t date, struct directory *ndir);
|
||||
|
||||
int dir_grow(struct Stream_t *Dir, int size);
|
||||
|
||||
void bufferize(struct Stream_t **Dir);
|
||||
|
||||
struct directory *dir_read(struct direntry_t *entry, int *error);
|
||||
|
||||
void initializeDirentry(struct direntry_t *entry, struct Stream_t *Dir);
|
||||
int isNotFound(struct direntry_t *entry);
|
||||
void low_level_dir_write(struct direntry_t *entry);
|
||||
void dosnameToDirentry(const struct dos_name_t *n, struct directory *dir);
|
||||
|
||||
int lookupForInsert(struct Stream_t *Dir, struct direntry_t *direntry, struct dos_name_t *dosname, char *longname, struct scan_state *ssp, int ignore_entry, int source_entry, int pessimisticShortRename, int use_longname);
|
||||
|
||||
typedef int (write_data_callback)(struct dos_name_t*, struct direntry_t*);
|
||||
|
||||
void init_clash_handling(struct ClashHandling_t *ch);
|
||||
int mwrite_one(struct Stream_t *Dir, const char *argname, write_data_callback *cb, struct ClashHandling_t *ch);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -1,796 +0,0 @@
|
|||
/* Copyright 1996-2006,2008,2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "msdos.h"
|
||||
#include "stream.h"
|
||||
#include "mtools.h"
|
||||
#include "fs.h"
|
||||
#include "file_name.h"
|
||||
#include "init.h"
|
||||
#include "force_io.h"
|
||||
#include "llong.h"
|
||||
#include "directory.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef long long fatBitMask;
|
||||
|
||||
struct FatMap_t {
|
||||
unsigned char *data;
|
||||
fatBitMask dirty;
|
||||
fatBitMask valid;
|
||||
};
|
||||
|
||||
#define SECT_PER_ENTRY (sizeof(fatBitMask)*8)
|
||||
#define ONE ((fatBitMask) 1)
|
||||
|
||||
static int readSector(struct Fs_t *This, char *buf, unsigned int off, size_t size)
|
||||
{
|
||||
return READS(This->Next, buf, sectorsToBytes((struct Stream_t *)This, off), size << This->sectorShift);
|
||||
}
|
||||
|
||||
static int forceReadSector(struct Fs_t *This, char *buf, unsigned int off, size_t size)
|
||||
{
|
||||
return force_read(This->Next, buf, sectorsToBytes((struct Stream_t *)This, off), size << This->sectorShift);
|
||||
}
|
||||
|
||||
static int forceWriteSector(struct Fs_t *This, char *buf, unsigned int off,
|
||||
size_t size)
|
||||
{
|
||||
return force_write(This->Next, buf, sectorsToBytes((struct Stream_t*)This, off), size << This->sectorShift);
|
||||
}
|
||||
|
||||
static struct FatMap_t *GetFatMap(struct Fs_t *Stream)
|
||||
{
|
||||
Stream->fat_error = 0;
|
||||
|
||||
int nr_entries = (Stream->fat_len + SECT_PER_ENTRY - 1) / SECT_PER_ENTRY;
|
||||
|
||||
struct FatMap_t *map = NewArray(nr_entries, struct FatMap_t);
|
||||
|
||||
if(!map)
|
||||
return NULL;
|
||||
|
||||
int i;
|
||||
for(i=0; i< nr_entries; i++)
|
||||
{
|
||||
map[i].data = 0;
|
||||
map[i].valid = 0;
|
||||
map[i].dirty = 0;
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
static int locate(struct Fs_t *Stream, size_t offset, int *slot, int *bit)
|
||||
{
|
||||
if(offset >= Stream->fat_len)
|
||||
return -1;
|
||||
|
||||
*slot = offset / SECT_PER_ENTRY;
|
||||
*bit = offset % SECT_PER_ENTRY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fatReadSector(struct Fs_t *This, int sector, int slot, int bit, int dupe, fatBitMask bitmap)
|
||||
{
|
||||
dupe = (dupe + This->primaryFat) % This->num_fat;
|
||||
int fat_start = This->fat_start + This->fat_len * dupe;
|
||||
|
||||
int nr_sectors = (bitmap == 0) ? SECT_PER_ENTRY - bit % SECT_PER_ENTRY : 1;
|
||||
|
||||
/* first, read as much as the buffer can give us */
|
||||
int ret = readSector(This, (char *)(This->FatMap[slot].data+(bit<<This->sectorShift)), fat_start+sector, nr_sectors);
|
||||
|
||||
if(ret < 0)
|
||||
return 0;
|
||||
|
||||
if((unsigned int) ret < This->sector_size)
|
||||
{
|
||||
/* if we got less than one sector's worth, insist to get at
|
||||
* least one sector */
|
||||
ret = forceReadSector(This, (char*) (This->FatMap[slot].data + (bit << This->sectorShift)), fat_start+sector, 1);
|
||||
|
||||
if(ret < (int) This->sector_size)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return ret >> This->sectorShift;
|
||||
}
|
||||
|
||||
static int fatWriteSector(struct Fs_t *This, int sector, int slot, int bit, int dupe)
|
||||
{
|
||||
dupe = (dupe + This->primaryFat) % This->num_fat;
|
||||
|
||||
if (dupe && !This->writeAllFats)
|
||||
return This->sector_size;
|
||||
|
||||
int fat_start = This->fat_start + This->fat_len * dupe;
|
||||
|
||||
return forceWriteSector(This, (char*) (This->FatMap[slot].data + bit * This->sector_size), fat_start + sector, 1);
|
||||
}
|
||||
|
||||
static unsigned char *loadSector(struct Fs_t *This, unsigned int sector, fatAccessMode_t mode, int recurs)
|
||||
{
|
||||
int slot;
|
||||
int bit;
|
||||
|
||||
if(locate(This,sector, &slot, &bit) < 0)
|
||||
return 0;
|
||||
|
||||
if(!This->FatMap[slot].data)
|
||||
{
|
||||
/* allocate the storage space */
|
||||
This->FatMap[slot].data = malloc(This->sector_size * SECT_PER_ENTRY);
|
||||
|
||||
if(!This->FatMap[slot].data)
|
||||
return 0;
|
||||
|
||||
memset(This->FatMap[slot].data, 0xee, This->sector_size * SECT_PER_ENTRY);
|
||||
}
|
||||
|
||||
if(! (This->FatMap[slot].valid & (ONE << bit)))
|
||||
{
|
||||
unsigned int i;
|
||||
int ret = -1;
|
||||
|
||||
for(i=0; i< This->num_fat; i++)
|
||||
{
|
||||
/* read the sector */
|
||||
ret = fatReadSector(This, sector, slot, bit, i,
|
||||
This->FatMap[slot].valid);
|
||||
|
||||
if(ret == 0)
|
||||
{
|
||||
fprintf(stderr, "Error reading fat number %d\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(This->FatMap[slot].valid)
|
||||
/* Set recurs if there have already been
|
||||
* sectors loaded in this bitmap long
|
||||
*/
|
||||
recurs = 1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* all copies bad. Return error */
|
||||
if (ret == 0)
|
||||
return 0;
|
||||
|
||||
for (i=0; (int) i < ret; i++)
|
||||
This->FatMap[slot].valid |= ONE << (bit + i);
|
||||
|
||||
if(!recurs && ret == 1)
|
||||
/* do some prefetching, if we happened to only
|
||||
* get one sector */
|
||||
loadSector(This, sector+1, mode, 1);
|
||||
}
|
||||
|
||||
if(mode == FAT_ACCESS_WRITE)
|
||||
{
|
||||
This->FatMap[slot].dirty |= ONE << bit;
|
||||
This->fat_dirty = 1;
|
||||
}
|
||||
|
||||
return This->FatMap[slot].data + (bit << This->sectorShift);
|
||||
}
|
||||
|
||||
|
||||
static unsigned char *getAddress(struct Fs_t *Stream, unsigned int num, fatAccessMode_t mode)
|
||||
{
|
||||
int sector = num >> Stream->sectorShift;
|
||||
unsigned char *ret = 0;
|
||||
|
||||
if(sector == Stream->lastFatSectorNr && Stream->lastFatAccessMode >= mode)
|
||||
ret = Stream->lastFatSectorData;
|
||||
|
||||
if(!ret)
|
||||
{
|
||||
ret = loadSector(Stream, sector, mode, 0);
|
||||
|
||||
if(!ret)
|
||||
return 0;
|
||||
|
||||
Stream->lastFatSectorNr = sector;
|
||||
Stream->lastFatSectorData = ret;
|
||||
Stream->lastFatAccessMode = mode;
|
||||
}
|
||||
|
||||
return ret + (num & Stream->sectorMask);
|
||||
}
|
||||
|
||||
|
||||
static int readByte(struct Fs_t *Stream, int start)
|
||||
{
|
||||
unsigned char *address = getAddress(Stream, start, FAT_ACCESS_READ);
|
||||
return address ? *address : -1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Fat 12 encoding:
|
||||
* | byte n | byte n+1 | byte n+2 |
|
||||
* |7|6|5|4|3|2|1|0|7|6|5|4|3|2|1|0|7|6|5|4|3|2|1|0|
|
||||
* | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||
* | n+0.0 | n+0.5 | n+1.0 | n+1.5 | n+2.0 | n+2.5 |
|
||||
* \_____ \____ \______/________/_____ /
|
||||
* ____\______\________/ _____/ ____\_/
|
||||
* / \ \ / / \
|
||||
* | n+1.5 | n+0.0 | n+0.5 | n+2.0 | n+2.5 | n+1.0 |
|
||||
* | FAT entry k | FAT entry k+1 |
|
||||
*/
|
||||
|
||||
/*
|
||||
* Get and decode a FAT (file allocation table) entry. Returns the cluster
|
||||
* number on success or 1 on failure.
|
||||
*/
|
||||
|
||||
static unsigned int fat12_decode(struct Fs_t *Stream, unsigned int num)
|
||||
{
|
||||
unsigned int start = num * 3 / 2;
|
||||
int byte0 = readByte(Stream, start);
|
||||
int byte1 = readByte(Stream, start+1);
|
||||
|
||||
if (num < 2 || byte0 < 0 || byte1 < 0 || num > Stream->num_clus+1)
|
||||
{
|
||||
fprintf(stderr,"[1] Bad address %d\n", num);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (num & 1)
|
||||
return (byte1 << 4) | ((byte0 & 0xf0)>>4);
|
||||
else
|
||||
return ((byte1 & 0xf) << 8) | byte0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Puts a code into the FAT table. Is the opposite of fat_decode(). No
|
||||
* sanity checking is done on the code. Returns a 1 on error.
|
||||
*/
|
||||
static void fat12_encode(struct Fs_t *Stream, unsigned int num, unsigned int code)
|
||||
{
|
||||
const int start = num * 3 / 2;
|
||||
unsigned char *address0 = getAddress(Stream, start, FAT_ACCESS_WRITE);
|
||||
unsigned char *address1 = getAddress(Stream, start+1, FAT_ACCESS_WRITE);
|
||||
|
||||
if (num & 1)
|
||||
{
|
||||
/* (odd) not on byte boundary */
|
||||
*address0 = (*address0 & 0x0f) | ((code << 4) & 0xf0);
|
||||
*address1 = (code >> 4) & 0xff;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* (even) on byte boundary */
|
||||
*address0 = code & 0xff;
|
||||
*address1 = (*address1 & 0xf0) | ((code >> 8) & 0x0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Fat 16 encoding:
|
||||
* | byte n | byte n+1 |
|
||||
* |7|6|5|4|3|2|1|0|7|6|5|4|3|2|1|0|
|
||||
* | | | | | | | | | | | | | | | | |
|
||||
* | FAT entry k |
|
||||
*/
|
||||
|
||||
static unsigned int fat16_decode(struct Fs_t *Stream, unsigned int num)
|
||||
{
|
||||
unsigned char *address = getAddress(Stream, num << 1, FAT_ACCESS_READ);
|
||||
|
||||
return address ? _WORD(address) : 1;
|
||||
}
|
||||
|
||||
static void fat16_encode(struct Fs_t *Stream, unsigned int num, unsigned int code)
|
||||
{
|
||||
unsigned char *address = getAddress(Stream, num << 1, FAT_ACCESS_WRITE);
|
||||
set_word(address, code);
|
||||
}
|
||||
|
||||
static unsigned int fast_fat16_decode(struct Fs_t *Stream, unsigned int num)
|
||||
{
|
||||
unsigned short *address = (unsigned short *) getAddress(Stream, num << 1, FAT_ACCESS_READ);
|
||||
return address ? *address : 1;
|
||||
}
|
||||
|
||||
static void fast_fat16_encode(struct Fs_t *Stream, unsigned int num, unsigned int code)
|
||||
{
|
||||
unsigned short *address = (unsigned short *) getAddress(Stream, num << 1, FAT_ACCESS_WRITE);
|
||||
*address = code;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fat 32 encoding
|
||||
*/
|
||||
#define FAT32_HIGH 0xf0000000
|
||||
#define FAT32_ADDR 0x0fffffff
|
||||
|
||||
static unsigned int fat32_decode(struct Fs_t *Stream, unsigned int num)
|
||||
{
|
||||
unsigned char *address = getAddress(Stream, num << 2, FAT_ACCESS_READ);
|
||||
return address ? _DWORD(address) & FAT32_ADDR : 1;
|
||||
}
|
||||
|
||||
static void fat32_encode(struct Fs_t *Stream, unsigned int num, unsigned int code)
|
||||
{
|
||||
unsigned char *address = getAddress(Stream, num << 2, FAT_ACCESS_WRITE);
|
||||
set_dword(address,(code&FAT32_ADDR) | (_DWORD(address)&FAT32_HIGH));
|
||||
}
|
||||
|
||||
static unsigned int fast_fat32_decode(struct Fs_t *Stream, unsigned int num)
|
||||
{
|
||||
unsigned int *address = (unsigned int *) getAddress(Stream, num << 2, FAT_ACCESS_READ);
|
||||
return address ? *address & FAT32_ADDR : 1;
|
||||
}
|
||||
|
||||
static void fast_fat32_encode(struct Fs_t *Stream, unsigned int num, unsigned int code)
|
||||
{
|
||||
unsigned int *address = (unsigned int *) getAddress(Stream, num << 2, FAT_ACCESS_WRITE);
|
||||
*address = (*address & FAT32_HIGH) | (code & FAT32_ADDR);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the FAT table to the disk. Up to now the FAT manipulation has
|
||||
* been done in memory. All errors are fatal. (Might not be too smart
|
||||
* to wait till the end of the program to write the table. Oh well...)
|
||||
*/
|
||||
|
||||
int fat_write(struct Fs_t *This)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int j;
|
||||
unsigned int bit;
|
||||
unsigned int slot;
|
||||
int ret;
|
||||
int fat_start;
|
||||
|
||||
if (!This->fat_dirty)
|
||||
return 0;
|
||||
|
||||
unsigned int dups = This->num_fat;
|
||||
if (This->fat_error)
|
||||
dups = 1;
|
||||
|
||||
for(i = 0; i < dups; i++)
|
||||
{
|
||||
j = 0;
|
||||
fat_start = This->fat_start + i*This->fat_len;
|
||||
|
||||
for (slot = 0; j < This->fat_len; slot++)
|
||||
{
|
||||
if(!This->FatMap[slot].dirty)
|
||||
{
|
||||
j += SECT_PER_ENTRY;
|
||||
continue;
|
||||
}
|
||||
|
||||
for(bit = 0; bit < SECT_PER_ENTRY && j < This->fat_len; bit++, j++)
|
||||
{
|
||||
if(!(This->FatMap[slot].dirty & (ONE << bit)))
|
||||
continue;
|
||||
|
||||
ret = fatWriteSector(This,j,slot, bit, i);
|
||||
|
||||
if (ret < (int) This->sector_size)
|
||||
{
|
||||
if (ret < 0)
|
||||
{
|
||||
perror("error in fat_write");
|
||||
return -1; // TODO: check in caller
|
||||
}
|
||||
|
||||
fprintf(stderr, "end of file in fat_write\n");
|
||||
return -1; // TODO: check in caller
|
||||
}
|
||||
|
||||
/* if last dupe, zero it out */
|
||||
if (i == dups - 1)
|
||||
This->FatMap[slot].dirty &= ~(ONE << bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* write the info sector, if any */
|
||||
if(This->infoSectorLoc && This->infoSectorLoc != MAX32)
|
||||
{
|
||||
/* initialize info sector */
|
||||
struct InfoSector_t *infoSector = malloc(This->sector_size);
|
||||
|
||||
set_dword(infoSector->signature1, INFOSECT_SIGNATURE1);
|
||||
|
||||
memset(infoSector->filler1, 0, sizeof(infoSector->filler1));
|
||||
memset(infoSector->filler2, 0, sizeof(infoSector->filler2));
|
||||
|
||||
set_dword(infoSector->signature2, INFOSECT_SIGNATURE2);
|
||||
set_dword(infoSector->pos, This->last);
|
||||
set_dword(infoSector->count, This->freeSpace);
|
||||
set_dword(infoSector->signature3, 0xaa55);
|
||||
|
||||
if(forceWriteSector(This, (char *)infoSector, This->infoSectorLoc, 1) != (signed int) This->sector_size)
|
||||
fprintf(stderr,"Trouble writing the info sector\n");
|
||||
|
||||
free(infoSector);
|
||||
}
|
||||
|
||||
This->fat_dirty = 0;
|
||||
This->lastFatAccessMode = FAT_ACCESS_READ;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_fat12(struct Fs_t *This)
|
||||
{
|
||||
This->fat_bits = 12;
|
||||
This->end_fat = 0xfff;
|
||||
This->last_fat = 0xff6;
|
||||
This->fat_decode = fat12_decode;
|
||||
This->fat_encode = fat12_encode;
|
||||
}
|
||||
|
||||
static char word_endian_test[] = { 0x34, 0x12 };
|
||||
|
||||
static void set_fat16(struct Fs_t *This)
|
||||
{
|
||||
This->fat_bits = 16;
|
||||
This->end_fat = 0xffff;
|
||||
This->last_fat = 0xfff6;
|
||||
|
||||
if(sizeof(unsigned short) == 2 && * (unsigned short *) word_endian_test == 0x1234) {
|
||||
This->fat_decode = fast_fat16_decode;
|
||||
This->fat_encode = fast_fat16_encode;
|
||||
}
|
||||
else
|
||||
{
|
||||
This->fat_decode = fat16_decode;
|
||||
This->fat_encode = fat16_encode;
|
||||
}
|
||||
}
|
||||
|
||||
static char dword_endian_test[] = { 0x78, 0x56, 0x34, 0x12 };
|
||||
|
||||
static void set_fat32(struct Fs_t *This)
|
||||
{
|
||||
This->fat_bits = 32;
|
||||
This->end_fat = 0xfffffff;
|
||||
This->last_fat = 0xffffff6;
|
||||
|
||||
if(sizeof(unsigned int) == 4 && * (unsigned int *) dword_endian_test == 0x12345678)
|
||||
{
|
||||
This->fat_decode = fast_fat32_decode;
|
||||
This->fat_encode = fast_fat32_encode;
|
||||
}
|
||||
else
|
||||
{
|
||||
This->fat_decode = fat32_decode;
|
||||
This->fat_encode = fat32_encode;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the first sector of FAT table into memory. Crude error detection on
|
||||
* wrong FAT encoding scheme.
|
||||
*/
|
||||
static int check_media_type(struct Fs_t *This, union bootsector UNUSED(*boot), unsigned int tot_sectors)
|
||||
{
|
||||
This->num_clus = (tot_sectors - This->clus_start) / This->cluster_size;
|
||||
|
||||
This->FatMap = GetFatMap(This);
|
||||
|
||||
if (This->FatMap == NULL)
|
||||
{
|
||||
perror("alloc fat map");
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned char* address = getAddress(This, 0, FAT_ACCESS_READ);
|
||||
|
||||
if(!address)
|
||||
{
|
||||
fprintf(stderr, "Could not read first FAT sector\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fat_32_read(struct Fs_t *This, union bootsector *boot, unsigned int tot_sectors)
|
||||
{
|
||||
int size;
|
||||
|
||||
This->fat_len = DWORD(ext.fat32.bigFat);
|
||||
This->writeAllFats = !(boot->boot.ext.fat32.extFlags[0] & 0x80);
|
||||
This->primaryFat = boot->boot.ext.fat32.extFlags[0] & 0xf;
|
||||
This->rootCluster = DWORD(ext.fat32.rootCluster);
|
||||
This->clus_start = This->fat_start + This->num_fat * This->fat_len;
|
||||
|
||||
/* read the info sector */
|
||||
size = This->sector_size;
|
||||
This->infoSectorLoc = WORD(ext.fat32.infoSector);
|
||||
|
||||
if (This->sector_size >= 512 && This->infoSectorLoc && This->infoSectorLoc != MAX32)
|
||||
{
|
||||
struct InfoSector_t *infoSector = (struct InfoSector_t *) malloc(size);
|
||||
|
||||
if (forceReadSector(This, (char *)infoSector, This->infoSectorLoc, 1) == (signed int) This->sector_size
|
||||
&& _DWORD(infoSector->signature1) == INFOSECT_SIGNATURE1
|
||||
&& _DWORD(infoSector->signature2) == INFOSECT_SIGNATURE2)
|
||||
{
|
||||
This->freeSpace = _DWORD(infoSector->count);
|
||||
This->last = _DWORD(infoSector->pos);
|
||||
}
|
||||
|
||||
free(infoSector);
|
||||
}
|
||||
|
||||
set_fat32(This);
|
||||
|
||||
return check_media_type(This, boot, tot_sectors);
|
||||
}
|
||||
|
||||
static int old_fat_read(struct Fs_t *This, union bootsector *boot, int UNUSED(config_fat_bits), size_t tot_sectors, int nodups)
|
||||
{
|
||||
This->writeAllFats = 1;
|
||||
This->primaryFat = 0;
|
||||
This->dir_start = This->fat_start + This->num_fat * This->fat_len;
|
||||
This->clus_start = This->dir_start + This->dir_len;
|
||||
This->infoSectorLoc = MAX32;
|
||||
|
||||
if(nodups)
|
||||
This->num_fat = 1;
|
||||
|
||||
if(check_media_type(This,boot, tot_sectors))
|
||||
return -1;
|
||||
|
||||
if(This->num_clus >= FAT12)
|
||||
set_fat16(This); /* third FAT byte must be 0xff */
|
||||
else
|
||||
set_fat12(This);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the first sector of the FAT table into memory and initialize
|
||||
* structures.
|
||||
*/
|
||||
int fat_read(struct Fs_t *This, union bootsector *boot, int fat_bits, size_t tot_sectors, int nodups)
|
||||
{
|
||||
This->fat_error = 0;
|
||||
This->fat_dirty = 0;
|
||||
This->last = MAX32;
|
||||
This->freeSpace = MAX32;
|
||||
This->lastFatSectorNr = 0;
|
||||
This->lastFatSectorData = 0;
|
||||
|
||||
return (This->fat_len) ? old_fat_read(This, boot, fat_bits, tot_sectors, nodups) : fat_32_read(This, boot, tot_sectors);
|
||||
}
|
||||
|
||||
|
||||
unsigned int fatDecode(struct Fs_t *This, unsigned int pos)
|
||||
{
|
||||
unsigned int ret = This->fat_decode(This, pos);
|
||||
|
||||
if(ret && (ret < 2 || ret > This->num_clus+1) && ret < This->last_fat)
|
||||
{
|
||||
fprintf(stderr, "Bad FAT entry %d at %d\n", ret, pos);
|
||||
This->fat_error++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* append a new cluster */
|
||||
void fatAppend(struct Fs_t *This, unsigned int pos, unsigned int newpos)
|
||||
{
|
||||
This->fat_encode(This, pos, newpos);
|
||||
This->fat_encode(This, newpos, This->end_fat);
|
||||
|
||||
if(This->freeSpace != MAX32)
|
||||
This->freeSpace--;
|
||||
}
|
||||
|
||||
/* de-allocates the given cluster */
|
||||
void fatDeallocate(struct Fs_t *This, unsigned int pos)
|
||||
{
|
||||
This->fat_encode(This, pos, 0);
|
||||
if(This->freeSpace != MAX32)
|
||||
This->freeSpace++;
|
||||
}
|
||||
|
||||
/* allocate a new cluster */
|
||||
void fatAllocate(struct Fs_t *This, unsigned int pos, unsigned int value)
|
||||
{
|
||||
This->fat_encode(This, pos, value);
|
||||
if(This->freeSpace != MAX32)
|
||||
This->freeSpace--;
|
||||
}
|
||||
|
||||
unsigned int get_next_free_cluster(struct Fs_t *This, unsigned int last)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (This->last != MAX32)
|
||||
last = This->last;
|
||||
|
||||
if (last < 2 || last >= This->num_clus+1)
|
||||
last = 1;
|
||||
|
||||
for (i = last + 1; i < This->num_clus + 2; i++)
|
||||
{
|
||||
unsigned int r = fatDecode(This, i);
|
||||
|
||||
if(r == 1)
|
||||
goto exit_0;
|
||||
|
||||
if (!r)
|
||||
{
|
||||
This->last = i;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 2; i < last + 1; i++)
|
||||
{
|
||||
unsigned int r = fatDecode(This, i);
|
||||
|
||||
if(r == 1)
|
||||
goto exit_0;
|
||||
|
||||
if (!r)
|
||||
{
|
||||
This->last = i;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fprintf(stderr,"No free cluster %d %d\n", This->preallocatedClusters, This->last);
|
||||
return 1;
|
||||
|
||||
exit_0:
|
||||
fprintf(stderr, "FAT error\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fat_error(struct Stream_t *Dir)
|
||||
{
|
||||
struct Stream_t *Stream = GetFs(Dir);
|
||||
DeclareThis(struct Fs_t);
|
||||
|
||||
if(This->fat_error)
|
||||
fprintf(stderr,"Fat error detected\n");
|
||||
|
||||
return This->fat_error;
|
||||
}
|
||||
|
||||
int fat32RootCluster(struct Stream_t *Dir)
|
||||
{
|
||||
struct Stream_t *Stream = GetFs(Dir);
|
||||
DeclareThis(struct Fs_t);
|
||||
return This->fat_bits == 32 ? This->rootCluster : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure that there is a minimum of total sectors free
|
||||
*/
|
||||
int getfreeMinClusters(struct Stream_t *Dir, size_t size)
|
||||
{
|
||||
struct Stream_t *Stream = GetFs(Dir);
|
||||
|
||||
DeclareThis(struct Fs_t);
|
||||
|
||||
if(This->freeSpace != MAX32)
|
||||
{
|
||||
if (This->freeSpace >= size)
|
||||
return 1;
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Disk full\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t total = 0;
|
||||
|
||||
/* we start at the same place where we'll start later to actually
|
||||
* allocate the sectors. That way, the same sectors of the FAT, which
|
||||
* are already loaded during getfreeMin will be able to be reused
|
||||
* during get_next_free_cluster */
|
||||
unsigned int last = This->last;
|
||||
|
||||
if (last < 2 || last >= This->num_clus + 2)
|
||||
last = 1;
|
||||
|
||||
unsigned int i;
|
||||
for (i=last+1; i< This->num_clus+2; i++)
|
||||
{
|
||||
unsigned int r = fatDecode(This, i);
|
||||
|
||||
if(r == 1)
|
||||
goto exit_0;
|
||||
|
||||
if (!r)
|
||||
total++;
|
||||
|
||||
if(total >= size)
|
||||
return 1;
|
||||
}
|
||||
|
||||
for(i=2; i < last+1; i++)
|
||||
{
|
||||
unsigned int r = fatDecode(This, i);
|
||||
|
||||
if(r == 1)
|
||||
goto exit_0;
|
||||
|
||||
if (!r)
|
||||
total++;
|
||||
|
||||
if(total >= size)
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Disk full\n");
|
||||
return 0;
|
||||
|
||||
exit_0:
|
||||
fprintf(stderr, "FAT error\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int getStart(struct Stream_t *Dir, struct directory *dir)
|
||||
{
|
||||
struct Stream_t *Stream = GetFs(Dir);
|
||||
unsigned int first = START(dir);
|
||||
|
||||
if(fat32RootCluster(Stream))
|
||||
first |= STARTHI(dir) << 16;
|
||||
|
||||
return first;
|
||||
}
|
||||
|
||||
int fs_free(struct Stream_t *Stream)
|
||||
{
|
||||
DeclareThis(struct Fs_t);
|
||||
|
||||
if (This->FatMap)
|
||||
{
|
||||
const int nr_entries = (This->fat_len + SECT_PER_ENTRY - 1) / SECT_PER_ENTRY;
|
||||
|
||||
for (int i = 0; i < nr_entries; i++)
|
||||
if (This->FatMap[i].data)
|
||||
free(This->FatMap[i].data);
|
||||
|
||||
free(This->FatMap);
|
||||
}
|
||||
|
||||
if (This->cp)
|
||||
cp_close(This->cp);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
/* Copyright 1996-2005,2007-2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MTOOLS_FAT_H
|
||||
#define MTOOLS_FAT_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
struct Stream_t;
|
||||
struct directory;
|
||||
|
||||
int getfreeMinClusters(struct Stream_t *Stream, size_t ref);
|
||||
unsigned int getStart(struct Stream_t *Dir, struct directory *dir);
|
||||
|
||||
#endif
|
|
@ -1,212 +0,0 @@
|
|||
/* Copyright 1986-1992 Emmet P. Gray.
|
||||
* Copyright 1996-1998,2000-2002,2005,2007-2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* mlabel.c
|
||||
* Make an MSDOS volume label
|
||||
*/
|
||||
|
||||
#include "fatlabel.h"
|
||||
|
||||
#include "msdos.h"
|
||||
#include "nameclash.h"
|
||||
#include "init.h"
|
||||
#include "file.h"
|
||||
#include "directory.h"
|
||||
#include "force_io.h"
|
||||
#include "file_name.h"
|
||||
|
||||
#include <wctype.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
/**
|
||||
* Wipe the given entry
|
||||
*/
|
||||
static void wipeEntry(struct direntry_t *entry)
|
||||
{
|
||||
struct direntry_t longNameEntry;
|
||||
|
||||
initializeDirentry(&longNameEntry, entry->Dir);
|
||||
|
||||
for(int i = entry->beginSlot; i< entry->endSlot; i++)
|
||||
{
|
||||
int error;
|
||||
longNameEntry.entry=i;
|
||||
dir_read(&longNameEntry, &error);
|
||||
|
||||
if(error)
|
||||
break;
|
||||
longNameEntry.dir.name[0] = (char) DELMARK;
|
||||
dir_write(&longNameEntry);
|
||||
}
|
||||
|
||||
entry->dir.name[0] = (char) DELMARK;
|
||||
dir_write(entry);
|
||||
}
|
||||
|
||||
void label_name(struct doscp_t *cp, const char *filename, int *mangled, struct dos_name_t *ans)
|
||||
{
|
||||
memset(ans, ' ', 8 + 3);
|
||||
ans->sentinel = '\0';
|
||||
|
||||
wchar_t wbuffer[12];
|
||||
int len = native_to_wchar(filename, wbuffer, 11, 0, 0);
|
||||
|
||||
if (len > 11)
|
||||
{
|
||||
*mangled = 1;
|
||||
len = 11;
|
||||
}
|
||||
else
|
||||
*mangled = 0;
|
||||
|
||||
int have_lower = 0;
|
||||
int have_upper = 0;
|
||||
int i = 0;
|
||||
|
||||
for(i = 0; i < len; i++)
|
||||
{
|
||||
if (islower(wbuffer[i]))
|
||||
have_lower = 1;
|
||||
|
||||
if (isupper(wbuffer[i]))
|
||||
have_upper = 1;
|
||||
|
||||
wbuffer[i] = towupper(wbuffer[i]);
|
||||
|
||||
if (wcschr(L"^+=/[]:,?*\\<>|\".", wbuffer[i]))
|
||||
{
|
||||
*mangled = 1;
|
||||
wbuffer[i] = '~';
|
||||
}
|
||||
}
|
||||
|
||||
if (have_lower && have_upper)
|
||||
*mangled = 1;
|
||||
|
||||
wchar_to_dos(cp, wbuffer, ans->base, len, mangled);
|
||||
}
|
||||
|
||||
int labelit(struct dos_name_t* dosname, struct direntry_t* entry)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
mk_entry(dosname, 0x8, 0, 0, now, &entry->dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fatlabel_set_label(const char* device_name, const char* new_label)
|
||||
{
|
||||
if (strlen(new_label) > VBUFSIZE)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* 1. Init clash handling
|
||||
*/
|
||||
struct ClashHandling_t ch;
|
||||
init_clash_handling(&ch);
|
||||
ch.name_converter = label_name;
|
||||
ch.ignore_entry = -2;
|
||||
|
||||
/*
|
||||
* 2. Open root dir
|
||||
*/
|
||||
struct Stream_t* RootDir = fs_init(device_name, O_RDWR);
|
||||
|
||||
if (RootDir)
|
||||
RootDir = OpenRoot(RootDir);
|
||||
|
||||
if (!RootDir)
|
||||
{
|
||||
fprintf(stderr, "Opening root dir failed.\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
/*
|
||||
* 3. Init dir entry
|
||||
*/
|
||||
struct direntry_t entry;
|
||||
initializeDirentry(&entry, RootDir);
|
||||
|
||||
/*
|
||||
* 4. Lookup vfat
|
||||
*/
|
||||
char longname[VBUFSIZE];
|
||||
char shortname[45];
|
||||
if (vfat_lookup(&entry, 0, 0, ACCEPT_LABEL | MATCH_ANY, shortname, longname) == -2)
|
||||
{
|
||||
fprintf(stderr, "Looking up vfat failed.\n");
|
||||
free_stream(&RootDir);
|
||||
return -3;
|
||||
}
|
||||
|
||||
/*
|
||||
* 5. Wipe existing entry.
|
||||
*/
|
||||
if (!isNotFound(&entry))
|
||||
{
|
||||
/* if we have a label, wipe it out before putting new one */
|
||||
entry.dir.attr = 0; /* for old mlabel */
|
||||
wipeEntry(&entry);
|
||||
}
|
||||
|
||||
/*
|
||||
* 6. Write new entry
|
||||
*/
|
||||
ch.ignore_entry = 1;
|
||||
|
||||
/* don't try to write the label if it's empty */
|
||||
int result = strlen(new_label) ? mwrite_one(RootDir, new_label, labelit, &ch) : 0;
|
||||
|
||||
/*
|
||||
* 7. Load FAT boot record
|
||||
*/
|
||||
union bootsector boot;
|
||||
struct Stream_t* Fs = GetFs(RootDir);
|
||||
int have_boot = force_read(Fs, boot.characters, 0, sizeof(boot)) == sizeof(boot);
|
||||
|
||||
struct label_blk_t* labelBlock = WORD_S(fatlen) ? &boot.boot.ext.old.labelBlock : &boot.boot.ext.fat32.labelBlock;
|
||||
|
||||
/*
|
||||
* 8. Get "dosconvert" struct
|
||||
*/
|
||||
struct dos_name_t dosname;
|
||||
struct doscp_t* cp = GET_DOSCONVERT(Fs);
|
||||
|
||||
/*
|
||||
* 9. Convert label
|
||||
*/
|
||||
int mangled = 0;
|
||||
label_name(cp, new_label, &mangled, &dosname);
|
||||
|
||||
/*
|
||||
* 10. Overwrite FAT boot record
|
||||
*/
|
||||
if (have_boot && boot.boot.descr >= 0xf0 && labelBlock->dos4 == 0x29)
|
||||
{
|
||||
strncpy(labelBlock->label, dosname.base, 11);
|
||||
force_write(Fs, (char *)&boot, 0, sizeof(boot));
|
||||
}
|
||||
|
||||
free_stream(&RootDir);
|
||||
fs_close(Fs);
|
||||
|
||||
return result;
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2010 by Volker Lanz <vl@fidra.de> *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef MTOOLS_MLABEL_H
|
||||
#define MTOOLS_MLABEL_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int __attribute__((visibility("default"))) fatlabel_set_label(const char* device_name, const char* new_label);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,537 +0,0 @@
|
|||
/* Copyright 1996-1999,2001-2003,2007-2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "msdos.h"
|
||||
#include "stream.h"
|
||||
#include "mtools.h"
|
||||
#include "file.h"
|
||||
#include "htable.h"
|
||||
#include "dirCache.h"
|
||||
#include "directory.h"
|
||||
#include "init.h"
|
||||
#include "fat.h"
|
||||
#include "fs.h"
|
||||
#include "llong.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct File_t
|
||||
{
|
||||
struct Class_t *Class;
|
||||
int refs;
|
||||
struct Fs_t *Fs; /* Filesystem that this fat file belongs to */
|
||||
struct Stream_t *Buffer;
|
||||
|
||||
int (*map)(struct File_t *this, off_t where, size_t *len, int mode, off_t *res);
|
||||
size_t FileSize;
|
||||
|
||||
size_t preallocatedSize;
|
||||
int preallocatedClusters;
|
||||
|
||||
/* Absolute position of first cluster of file */
|
||||
unsigned int FirstAbsCluNr;
|
||||
|
||||
/* Absolute position of previous cluster */
|
||||
unsigned int PreviousAbsCluNr;
|
||||
|
||||
/* Relative position of previous cluster */
|
||||
unsigned int PreviousRelCluNr;
|
||||
struct direntry_t direntry;
|
||||
int hint;
|
||||
struct dirCache_t *dcp;
|
||||
|
||||
unsigned int loopDetectRel;
|
||||
unsigned int loopDetectAbs;
|
||||
};
|
||||
|
||||
static struct Class_t FileClass;
|
||||
static struct hashtable *filehash;
|
||||
|
||||
static struct File_t *getUnbufferedFile(struct Stream_t *Stream)
|
||||
{
|
||||
while(Stream->Class != &FileClass)
|
||||
Stream = Stream->Next;
|
||||
return (struct File_t *) Stream;
|
||||
}
|
||||
|
||||
struct dirCache_t **getDirCacheP(struct Stream_t *Stream)
|
||||
{
|
||||
return &getUnbufferedFile(Stream)->dcp;
|
||||
}
|
||||
|
||||
struct direntry_t *getDirentry(struct Stream_t *Stream)
|
||||
{
|
||||
return &getUnbufferedFile(Stream)->direntry;
|
||||
}
|
||||
|
||||
|
||||
static int recalcPreallocSize(struct File_t *This)
|
||||
{
|
||||
struct Fs_t *Fs = This->Fs;
|
||||
int clus_size = Fs->cluster_size * Fs->sector_size;
|
||||
size_t currentClusters = (This->FileSize + clus_size - 1) / clus_size;
|
||||
size_t neededClusters = (This->preallocatedSize + clus_size - 1) / clus_size;
|
||||
int neededPrealloc = neededClusters - currentClusters;
|
||||
|
||||
if (neededPrealloc < 0)
|
||||
neededPrealloc = 0;
|
||||
|
||||
int r = fsPreallocateClusters(Fs, neededPrealloc - This->preallocatedClusters);
|
||||
if(r)
|
||||
return r;
|
||||
|
||||
This->preallocatedClusters = neededPrealloc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _loopDetect(unsigned int* oldrel, unsigned int rel, unsigned int* oldabs, unsigned int absol)
|
||||
{
|
||||
if (*oldrel && rel > *oldrel && absol == *oldabs)
|
||||
{
|
||||
fprintf(stderr, "loop detected! oldrel=%d newrel=%d abs=%d\n", *oldrel, rel, absol);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rel >= 2 * *oldrel + 1)
|
||||
{
|
||||
*oldrel = rel;
|
||||
*oldabs = absol;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int loopDetect(struct File_t* This, unsigned int rel, unsigned int absol)
|
||||
{
|
||||
return _loopDetect(&This->loopDetectRel, rel, &This->loopDetectAbs, absol);
|
||||
}
|
||||
|
||||
static unsigned int _countBlocks(struct Fs_t *This, unsigned int block)
|
||||
{
|
||||
unsigned int blocks = 0;
|
||||
unsigned int rel = 0;
|
||||
unsigned int oldabs = 0;
|
||||
unsigned int oldrel = 0;
|
||||
|
||||
while (block <= This->last_fat && block != 1 && block)
|
||||
{
|
||||
blocks++;
|
||||
block = fatDecode(This, block);
|
||||
rel++;
|
||||
if (_loopDetect(&oldrel, rel, &oldabs, block) < 0)
|
||||
block = -1;
|
||||
}
|
||||
|
||||
return blocks;
|
||||
}
|
||||
|
||||
/* returns number of bytes in a directory. Represents a file size, and
|
||||
* can hence be not bigger than 2^32
|
||||
*/
|
||||
static size_t countBytes(struct Stream_t *Dir, unsigned int block)
|
||||
{
|
||||
struct Stream_t *Stream = GetFs(Dir);
|
||||
DeclareThis(struct Fs_t);
|
||||
|
||||
return _countBlocks(This, block) * This->sector_size * This->cluster_size;
|
||||
}
|
||||
|
||||
static int normal_map(struct File_t *This, off_t where, size_t *len, int mode, off_t *res)
|
||||
{
|
||||
size_t end;
|
||||
int NrClu; /* number of clusters to read */
|
||||
|
||||
struct Fs_t *Fs = This->Fs;
|
||||
|
||||
*res = 0;
|
||||
int clus_size = Fs->cluster_size * Fs->sector_size;
|
||||
int offset = where % clus_size;
|
||||
|
||||
if (mode == MT_READ)
|
||||
maximize(*len, This->FileSize - where);
|
||||
|
||||
if (*len == 0)
|
||||
return 0;
|
||||
|
||||
unsigned int NewCluNr;
|
||||
if (This->FirstAbsCluNr < 2)
|
||||
{
|
||||
if( mode == MT_READ || *len == 0)
|
||||
{
|
||||
*len = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
NewCluNr = get_next_free_cluster(This->Fs, 1);
|
||||
|
||||
if (NewCluNr == 1 )
|
||||
{
|
||||
errno = ENOSPC;
|
||||
return -2;
|
||||
}
|
||||
|
||||
hash_remove(filehash, (void *) This, This->hint);
|
||||
This->FirstAbsCluNr = NewCluNr;
|
||||
hash_add(filehash, (void *) This, &This->hint);
|
||||
fatAllocate(This->Fs, NewCluNr, Fs->end_fat);
|
||||
}
|
||||
|
||||
unsigned int RelCluNr = where / clus_size;
|
||||
unsigned int CurCluNr;
|
||||
unsigned int AbsCluNr;
|
||||
|
||||
if (RelCluNr >= This->PreviousRelCluNr)
|
||||
{
|
||||
CurCluNr = This->PreviousRelCluNr;
|
||||
AbsCluNr = This->PreviousAbsCluNr;
|
||||
}
|
||||
else
|
||||
{
|
||||
CurCluNr = 0;
|
||||
AbsCluNr = This->FirstAbsCluNr;
|
||||
}
|
||||
|
||||
NrClu = (offset + *len - 1) / clus_size;
|
||||
while (CurCluNr <= RelCluNr + NrClu)
|
||||
{
|
||||
if (CurCluNr == RelCluNr)
|
||||
{
|
||||
/* we have reached the beginning of our zone. Save
|
||||
* coordinates */
|
||||
This->PreviousRelCluNr = RelCluNr;
|
||||
This->PreviousAbsCluNr = AbsCluNr;
|
||||
}
|
||||
|
||||
NewCluNr = fatDecode(This->Fs, AbsCluNr);
|
||||
|
||||
if (NewCluNr == 1 || NewCluNr == 0)
|
||||
{
|
||||
fprintf(stderr,"Fat problem while decoding %d %x\n", AbsCluNr, NewCluNr);
|
||||
return -3; // TODO: check return code in caller
|
||||
}
|
||||
|
||||
if (CurCluNr == RelCluNr + NrClu)
|
||||
break;
|
||||
|
||||
if (NewCluNr > Fs->last_fat && mode == MT_WRITE)
|
||||
{
|
||||
/* if at end, and writing, extend it */
|
||||
NewCluNr = get_next_free_cluster(This->Fs, AbsCluNr);
|
||||
|
||||
if (NewCluNr == 1) /* no more space */
|
||||
{
|
||||
errno = ENOSPC;
|
||||
return -2;
|
||||
}
|
||||
|
||||
fatAppend(This->Fs, AbsCluNr, NewCluNr);
|
||||
}
|
||||
|
||||
if (CurCluNr < RelCluNr && NewCluNr > Fs->last_fat)
|
||||
{
|
||||
*len = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (CurCluNr >= RelCluNr && NewCluNr != AbsCluNr + 1)
|
||||
break;
|
||||
|
||||
CurCluNr++;
|
||||
AbsCluNr = NewCluNr;
|
||||
|
||||
if(loopDetect(This, CurCluNr, AbsCluNr))
|
||||
{
|
||||
errno = EIO;
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
maximize(*len, (1 + CurCluNr - RelCluNr) * clus_size - offset);
|
||||
|
||||
end = where + *len;
|
||||
|
||||
if((*len + offset) / clus_size + This->PreviousAbsCluNr-2 > Fs->num_clus)
|
||||
{
|
||||
fprintf(stderr, "cluster too big\n");
|
||||
return -3; // TODO: check return code in caller
|
||||
}
|
||||
|
||||
*res = sectorsToBytes((struct Stream_t*)Fs, (This->PreviousAbsCluNr-2) * Fs->cluster_size + Fs->clus_start) + offset;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int root_map(struct File_t *This, off_t where, size_t *len, int UNUSED(mode), off_t *res)
|
||||
{
|
||||
struct Fs_t *Fs = This->Fs;
|
||||
|
||||
if(Fs->dir_len * Fs->sector_size < (size_t) where)
|
||||
{
|
||||
*len = 0;
|
||||
errno = ENOSPC;
|
||||
return -2;
|
||||
}
|
||||
|
||||
maximize(*len, Fs->dir_len * Fs->sector_size - where);
|
||||
|
||||
if (*len == 0)
|
||||
return 0;
|
||||
|
||||
*res = sectorsToBytes((struct Stream_t*)Fs, Fs->dir_start) + where;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int read_file(struct Stream_t *Stream, char *buf, off_t iwhere, size_t len)
|
||||
{
|
||||
DeclareThis(struct File_t);
|
||||
|
||||
off_t pos;
|
||||
off_t where = truncBytes32(iwhere);
|
||||
|
||||
struct Stream_t *Disk = This->Fs->Next;
|
||||
|
||||
int err = This->map(This, where, &len, MT_READ, &pos);
|
||||
|
||||
return (err <= 0) ? err : READS(Disk, buf, pos, len);
|
||||
}
|
||||
|
||||
static int write_file(struct Stream_t *Stream, char *buf, off_t iwhere, size_t len)
|
||||
{
|
||||
DeclareThis(struct File_t);
|
||||
|
||||
off_t pos;
|
||||
|
||||
struct Stream_t *Disk = This->Fs->Next;
|
||||
off_t where = truncBytes32(iwhere);
|
||||
|
||||
const size_t requestedLen = len;
|
||||
|
||||
int err = This->map(This, where, &len, MT_WRITE, &pos);
|
||||
|
||||
if (err <= 0)
|
||||
return err;
|
||||
|
||||
int ret = WRITES(Disk, buf, pos, len);
|
||||
|
||||
if(ret > (signed int) requestedLen)
|
||||
ret = requestedLen;
|
||||
|
||||
if (ret > 0 && where + ret > (off_t) This->FileSize)
|
||||
This->FileSize = where + ret;
|
||||
|
||||
recalcPreallocSize(This);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int free_file(struct Stream_t *Stream)
|
||||
{
|
||||
DeclareThis(struct File_t);
|
||||
struct Fs_t *Fs = This->Fs;
|
||||
fsPreallocateClusters(Fs, -This->preallocatedClusters);
|
||||
free_stream(&This->direntry.Dir);
|
||||
freeDirCache(Stream);
|
||||
return hash_remove(filehash, (void *) Stream, This->hint);
|
||||
}
|
||||
|
||||
static int flush_file(struct Stream_t *Stream)
|
||||
{
|
||||
DeclareThis(struct File_t);
|
||||
struct direntry_t *entry = &This->direntry;
|
||||
|
||||
if (isRootDir(Stream))
|
||||
return 0;
|
||||
|
||||
if (This->FirstAbsCluNr != getStart(entry->Dir, &entry->dir))
|
||||
{
|
||||
set_word(entry->dir.start, This->FirstAbsCluNr & 0xffff);
|
||||
set_word(entry->dir.startHi, This->FirstAbsCluNr >> 16);
|
||||
dir_write(entry);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pre_allocate_file(struct Stream_t *Stream, size_t isize)
|
||||
{
|
||||
DeclareThis(struct File_t);
|
||||
|
||||
size_t size = truncBytes32(isize);
|
||||
|
||||
if (size > This->FileSize && size > This->preallocatedSize)
|
||||
{
|
||||
This->preallocatedSize = size;
|
||||
return recalcPreallocSize(This);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct Class_t FileClass =
|
||||
{
|
||||
read_file,
|
||||
write_file,
|
||||
flush_file, /* flush */
|
||||
free_file, /* free */
|
||||
0, /* get_geom */
|
||||
0, /* get_file_data */
|
||||
pre_allocate_file,
|
||||
get_dosConvert_pass_through
|
||||
};
|
||||
|
||||
static unsigned int getAbsCluNr(struct File_t *This)
|
||||
{
|
||||
if(This->FirstAbsCluNr)
|
||||
return This->FirstAbsCluNr;
|
||||
if(isRootDir((struct Stream_t *) This))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static unsigned int func1(void *Stream)
|
||||
{
|
||||
DeclareThis(struct File_t);
|
||||
return getAbsCluNr(This) ^ (long) This->Fs;
|
||||
}
|
||||
|
||||
static unsigned int func2(void *Stream)
|
||||
{
|
||||
DeclareThis(struct File_t);
|
||||
return getAbsCluNr(This);
|
||||
}
|
||||
|
||||
static int comp(void *Stream, void *Stream2)
|
||||
{
|
||||
DeclareThis(struct File_t);
|
||||
|
||||
struct File_t *This2 = (struct File_t *) Stream2;
|
||||
|
||||
return This->Fs != This2->Fs || getAbsCluNr(This) != getAbsCluNr(This2);
|
||||
}
|
||||
|
||||
static void init_hash(void)
|
||||
{
|
||||
static int is_initialised = 0;
|
||||
|
||||
if(!is_initialised)
|
||||
{
|
||||
make_ht(func1, func2, comp, 20, &filehash);
|
||||
is_initialised = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static struct Stream_t *_internalFileOpen(struct Stream_t* Dir, unsigned int first, size_t size, struct direntry_t *entry)
|
||||
{
|
||||
struct Stream_t *Stream = GetFs(Dir);
|
||||
DeclareThis(struct Fs_t);
|
||||
struct File_t Pattern;
|
||||
struct File_t *File;
|
||||
|
||||
init_hash();
|
||||
This->refs++;
|
||||
|
||||
if (first != 1)
|
||||
{
|
||||
/* we use the illegal cluster 1 to mark newly created files.
|
||||
* do not manage those by hashtable */
|
||||
Pattern.Fs = This;
|
||||
Pattern.Class = &FileClass;
|
||||
Pattern.map = (first || (entry && !IS_DIR(entry))) ? normal_map : root_map;
|
||||
Pattern.FirstAbsCluNr = first;
|
||||
Pattern.loopDetectRel = 0;
|
||||
Pattern.loopDetectAbs = first;
|
||||
|
||||
if (!hash_lookup(filehash, (T_HashTableEl) &Pattern, (T_HashTableEl **)&File, 0))
|
||||
{
|
||||
File->refs++;
|
||||
This->refs--;
|
||||
return (struct Stream_t*) File;
|
||||
}
|
||||
}
|
||||
|
||||
File = New(struct File_t);
|
||||
if (!File)
|
||||
return NULL;
|
||||
|
||||
File->dcp = 0;
|
||||
File->preallocatedClusters = 0;
|
||||
File->preallocatedSize = 0;
|
||||
|
||||
/* memorize dir for date and attrib */
|
||||
File->direntry = *entry;
|
||||
if (entry->entry == -3)
|
||||
File->direntry.Dir = (struct Stream_t *) File; /* root directory */
|
||||
else
|
||||
copy_stream(File->direntry.Dir);
|
||||
|
||||
File->Class = &FileClass;
|
||||
File->Fs = This;
|
||||
|
||||
File->map = (first || (entry && !IS_DIR(entry))) ? normal_map : root_map;
|
||||
File->FirstAbsCluNr = (first == 1) ? 0 : first;
|
||||
File->loopDetectRel = 0;
|
||||
File->loopDetectAbs = 0;
|
||||
File->PreviousRelCluNr = 0xffff;
|
||||
File->FileSize = size;
|
||||
File->refs = 1;
|
||||
File->Buffer = 0;
|
||||
|
||||
hash_add(filehash, (void *) File, &File->hint);
|
||||
|
||||
return (struct Stream_t *) File;
|
||||
}
|
||||
|
||||
struct Stream_t* OpenRoot(struct Stream_t* Dir)
|
||||
{
|
||||
const unsigned int num = fat32RootCluster(Dir);
|
||||
|
||||
struct direntry_t entry;
|
||||
memset(&entry, 0, sizeof(struct direntry_t));
|
||||
|
||||
/* make the directory entry */
|
||||
entry.entry = -3;
|
||||
entry.name[0] = '\0';
|
||||
mk_entry_from_base("/", ATTR_DIR, num, 0, 0, &entry.dir);
|
||||
|
||||
size_t size;
|
||||
if (num)
|
||||
size = countBytes(Dir, num);
|
||||
else
|
||||
{
|
||||
struct Fs_t* Fs = (struct Fs_t*) GetFs(Dir);
|
||||
size = Fs->dir_len * Fs->sector_size;
|
||||
}
|
||||
|
||||
struct Stream_t* file = _internalFileOpen(Dir, num, size, &entry);
|
||||
bufferize(&file);
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
int isRootDir(struct Stream_t *Stream)
|
||||
{
|
||||
struct File_t *This = getUnbufferedFile(Stream);
|
||||
|
||||
return This->map == root_map;
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
/* Copyright 1996,1997,2001,2002,2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MTOOLS_FILE_H
|
||||
#define MTOOLS_FILE_H
|
||||
|
||||
struct Stream_t;
|
||||
struct dirCache_t;
|
||||
|
||||
struct Stream_t *OpenRoot(struct Stream_t *Dir);
|
||||
struct dirCache_t **getDirCacheP(struct Stream_t *Stream);
|
||||
int isRootDir(struct Stream_t *Stream);
|
||||
|
||||
#endif
|
|
@ -1,112 +0,0 @@
|
|||
/* Copyright 1995 David C. Niemi
|
||||
* Copyright 1996-1998,2000-2002,2008,2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "msdos.h"
|
||||
#include "mtools.h"
|
||||
#include "vfat.h"
|
||||
#include "file_name.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
typedef enum Case_l {
|
||||
NONE,
|
||||
UPPER,
|
||||
LOWER
|
||||
} Case_t;
|
||||
|
||||
|
||||
/*
|
||||
* Get rid of spaces in an MSDOS 'raw' name (one that has come from the
|
||||
* directory structure) so that it can be used for regular expression
|
||||
* matching with a Unix filename. Also used to 'unfix' a name that has
|
||||
* been altered by dos_name().
|
||||
*/
|
||||
|
||||
wchar_t *unix_name(struct doscp_t *dosCp, const char *base, const char *ext, char Case, wchar_t *ret)
|
||||
{
|
||||
char tname[9];
|
||||
|
||||
strncpy(tname, base, 8);
|
||||
tname[8] = '\0';
|
||||
|
||||
char *s;
|
||||
if ((s = strchr(tname, ' ')))
|
||||
*s = '\0';
|
||||
|
||||
int i;
|
||||
if(Case & BASECASE)
|
||||
for(i = 0;i < 8 && tname[i]; i++)
|
||||
tname[i] = tolower(tname[i]);
|
||||
|
||||
char text[4];
|
||||
strncpy(text, ext, 3);
|
||||
text[3] = '\0';
|
||||
if ((s = strchr(text, ' ')))
|
||||
*s = '\0';
|
||||
|
||||
if(Case & EXTCASE)
|
||||
for(i = 0; i < 3 && text[i]; i++)
|
||||
text[i] = tolower(text[i]);
|
||||
|
||||
char ans[13];
|
||||
if (*text)
|
||||
{
|
||||
strcpy(ans, tname);
|
||||
strcat(ans, ".");
|
||||
strcat(ans, text);
|
||||
}
|
||||
else
|
||||
strcpy(ans, tname);
|
||||
|
||||
/* fix special characters (above 0x80) */
|
||||
dos_to_wchar(dosCp, ans, ret, 12);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* If null encountered, set *end to 0x40 and write nulls rest of way
|
||||
* 950820: Win95 does not like this! It complains about bad characters.
|
||||
* So, instead: If null encountered, set *end to 0x40, write the null, and
|
||||
* write 0xff the rest of the way (that is what Win95 seems to do; hopefully
|
||||
* that will make it happy)
|
||||
*/
|
||||
/* Always return num */
|
||||
int unicode_write(wchar_t *in, struct unicode_char *out, int num, int *end_p)
|
||||
{
|
||||
int j;
|
||||
|
||||
for (j = 0; j < num; ++j)
|
||||
{
|
||||
if (*end_p) /* Fill with 0xff */
|
||||
out->uchar = out->lchar = (char) 0xff;
|
||||
else
|
||||
{
|
||||
out->uchar = *in >> 8;
|
||||
out->lchar = *in;
|
||||
if (! *in)
|
||||
*end_p = VSE_LAST;
|
||||
}
|
||||
|
||||
++out;
|
||||
++in;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
/* Copyright 2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef FILE_NAME_H
|
||||
#define FILE_NAME_H
|
||||
|
||||
#include <wchar.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct doscp_t;
|
||||
|
||||
/**
|
||||
* raw dos-name coming straight from the directory entry
|
||||
* MYFILE TXT
|
||||
*/
|
||||
struct dos_name_t
|
||||
{
|
||||
char base[8];
|
||||
char ext[3];
|
||||
char sentinel;
|
||||
};
|
||||
|
||||
struct doscp_t;
|
||||
|
||||
int dos_to_wchar(struct doscp_t *fromDos, char *dos, wchar_t *wchar, size_t len);
|
||||
void wchar_to_dos(struct doscp_t *toDos, wchar_t *wchar, char *dos, size_t len, int *mangled);
|
||||
|
||||
struct doscp_t *cp_open(int codepage);
|
||||
void cp_close(struct doscp_t *cp);
|
||||
|
||||
int wchar_to_native(const wchar_t *wchar, char *native, size_t len);
|
||||
int native_to_wchar(const char *native, wchar_t *wchar, size_t len, const char *end, int *mangled);
|
||||
wchar_t *unix_name(struct doscp_t *fromDos, const char *base, const char *ext, char Case, wchar_t *answer);
|
||||
|
||||
#endif
|
|
@ -1,60 +0,0 @@
|
|||
/* Copyright 1996,1997,1999,2001,2002,2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Force I/O to be done to complete transfer length
|
||||
*
|
||||
* written by:
|
||||
*
|
||||
* Alain L. Knaff
|
||||
* alain@knaff.lu
|
||||
*
|
||||
*/
|
||||
|
||||
#include "msdos.h"
|
||||
#include "stream.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
static int force_io(struct Stream_t *Stream, char *buf, off_t start, size_t len, int (*io)(struct Stream_t *, char *, off_t, size_t))
|
||||
{
|
||||
int done = 0;
|
||||
|
||||
while(len)
|
||||
{
|
||||
int ret = io(Stream, buf, start, len);
|
||||
|
||||
if (ret <= 0 )
|
||||
return done ? done : ret;
|
||||
|
||||
start += ret;
|
||||
done += ret;
|
||||
len -= ret;
|
||||
buf += ret;
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
int force_write(struct Stream_t *Stream, char *buf, off_t start, size_t len)
|
||||
{
|
||||
return force_io(Stream, buf, start, len, Stream->Class->write);
|
||||
}
|
||||
|
||||
int force_read(struct Stream_t *Stream, char *buf, off_t start, size_t len)
|
||||
{
|
||||
return force_io(Stream, buf, start, len, Stream->Class->read);
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/* Copyright 1996-1999,2001,2002,2005,2006,2008,2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef MTOOLS_FORCE_IO_H
|
||||
#define MTOOLS_FORCE_IO_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct Stream_t;
|
||||
|
||||
int force_write(struct Stream_t *Stream, char *buf, off_t start, size_t len);
|
||||
int force_read(struct Stream_t *Stream, char *buf, off_t start, size_t len);
|
||||
|
||||
#endif
|
|
@ -1,108 +0,0 @@
|
|||
/* Copyright 1996,1997,2001,2002,2007,2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MTOOLS_FS_H
|
||||
#define MTOOLS_FS_H
|
||||
|
||||
struct Stream_t;
|
||||
struct Class_t;
|
||||
struct doscp_t;
|
||||
|
||||
struct FsPublic_t
|
||||
{
|
||||
struct Class_t *Class;
|
||||
int refs;
|
||||
struct Stream_t *Next;
|
||||
struct Stream_t *Buffer;
|
||||
|
||||
int serialized;
|
||||
unsigned long serial_number;
|
||||
int cluster_size;
|
||||
unsigned int sector_size;
|
||||
};
|
||||
|
||||
typedef enum fatAccessMode_t
|
||||
{
|
||||
FAT_ACCESS_READ,
|
||||
FAT_ACCESS_WRITE
|
||||
} fatAccessMode_t;
|
||||
|
||||
struct Fs_t
|
||||
{
|
||||
struct Class_t *Class;
|
||||
int refs;
|
||||
struct Stream_t *Next;
|
||||
struct Stream_t *Buffer;
|
||||
|
||||
int serialized;
|
||||
unsigned long serial_number;
|
||||
unsigned int cluster_size;
|
||||
unsigned int sector_size;
|
||||
int fat_error;
|
||||
|
||||
unsigned int (*fat_decode)(struct Fs_t *This, unsigned int num);
|
||||
void (*fat_encode)(struct Fs_t *This, unsigned int num, unsigned int code);
|
||||
|
||||
struct Stream_t *Direct;
|
||||
int fat_dirty;
|
||||
unsigned int fat_start;
|
||||
unsigned int fat_len;
|
||||
|
||||
unsigned int num_fat;
|
||||
unsigned int end_fat;
|
||||
unsigned int last_fat;
|
||||
int fat_bits; /* must be signed, because we use negative values for special purposes */
|
||||
struct FatMap_t *FatMap;
|
||||
|
||||
unsigned int dir_start;
|
||||
unsigned int dir_len;
|
||||
unsigned int clus_start;
|
||||
|
||||
unsigned int num_clus;
|
||||
|
||||
/* fat 32 */
|
||||
unsigned int primaryFat;
|
||||
unsigned int writeAllFats;
|
||||
unsigned int rootCluster;
|
||||
unsigned int infoSectorLoc;
|
||||
unsigned int last; /* last sector allocated, or MAX32 if unknown */
|
||||
unsigned int freeSpace; /* free space, or MAX32 if unknown */
|
||||
int preallocatedClusters;
|
||||
|
||||
int lastFatSectorNr;
|
||||
unsigned char *lastFatSectorData;
|
||||
fatAccessMode_t lastFatAccessMode;
|
||||
int sectorMask;
|
||||
int sectorShift;
|
||||
|
||||
struct doscp_t *cp;
|
||||
};
|
||||
|
||||
int fs_free(struct Stream_t *Stream);
|
||||
unsigned int get_next_free_cluster(struct Fs_t *Fs, unsigned int last);
|
||||
unsigned int fatDecode(struct Fs_t *This, unsigned int pos);
|
||||
void fatAppend(struct Fs_t *This, unsigned int pos, unsigned int newpos);
|
||||
void fatDeallocate(struct Fs_t *This, unsigned int pos);
|
||||
void fatAllocate(struct Fs_t *This, unsigned int pos, unsigned int value);
|
||||
int fat_read(struct Fs_t *This, union bootsector *boot, int fat_bits, size_t tot_sectors, int nodups);
|
||||
int fat_write(struct Fs_t *This);
|
||||
struct Stream_t* fs_init(const char* deviceName, int mode);
|
||||
int fat_error(struct Stream_t *Dir);
|
||||
int fat32RootCluster(struct Stream_t *Dir);
|
||||
|
||||
#endif
|
|
@ -1,234 +0,0 @@
|
|||
/* Copyright 1996,1997,2001,2002,2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* hash.c - hash table.
|
||||
*/
|
||||
|
||||
#include "htable.h"
|
||||
#include "mtools.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
struct hashtable {
|
||||
T_HashFunc f1,f2;
|
||||
T_ComparFunc compar;
|
||||
int size; /* actual size of the array */
|
||||
int fill; /* number of deleted or in use slots */
|
||||
int inuse; /* number of slots in use */
|
||||
int max; /* maximal number of elements to keep efficient */
|
||||
T_HashTableEl *entries;
|
||||
};
|
||||
|
||||
static const int sizes[]=
|
||||
{
|
||||
5, 11, 23, 47, 97, 197, 397, 797, 1597, 3203, 6421, 12853,
|
||||
25717, 51437, 102877, 205759, 411527, 823117, 1646237,
|
||||
3292489, 6584983, 13169977, 26339969, 52679969, 105359939,
|
||||
210719881, 421439783, 842879579, 1685759167, 0
|
||||
};
|
||||
|
||||
static int deleted=0;
|
||||
static int unallocated=0;
|
||||
|
||||
static int alloc_ht(struct hashtable *H, int size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; sizes[i]; i++)
|
||||
if (sizes[i] > size*4)
|
||||
break;
|
||||
|
||||
if (!sizes[i])
|
||||
for(i = 0; sizes[i]; i++)
|
||||
if (sizes[i] > size*2 )
|
||||
break;
|
||||
|
||||
if (!sizes[i])
|
||||
for(i = 0; sizes[i]; i++)
|
||||
if (sizes[i] > size)
|
||||
break;
|
||||
|
||||
if(!sizes[i])
|
||||
return -1;
|
||||
|
||||
size = sizes[i];
|
||||
|
||||
if(size < H->size)
|
||||
size = H->size; /* never shrink the table */
|
||||
|
||||
H->max = size * 4 / 5 - 2;
|
||||
H->size = size;
|
||||
H->fill = 0;
|
||||
H->inuse = 0;
|
||||
H->entries = NewArray(size, T_HashTableEl);
|
||||
|
||||
if (H->entries == NULL)
|
||||
return -1; /* out of memory error */
|
||||
|
||||
for(i = 0; i < size; i++)
|
||||
H->entries[i] = &unallocated;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int make_ht(T_HashFunc f1, T_HashFunc f2, T_ComparFunc c, int size, struct hashtable **H)
|
||||
{
|
||||
*H = New(struct hashtable);
|
||||
if (*H == NULL)
|
||||
return -1; /* out of memory error */
|
||||
|
||||
(*H)->f1 = f1;
|
||||
(*H)->f2 = f2;
|
||||
(*H)->compar = c;
|
||||
(*H)->size = 0;
|
||||
|
||||
if(alloc_ht(*H,size))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* add into hash table without checking for repeats */
|
||||
static int _hash_add(struct hashtable *H,T_HashTableEl *E, int *hint)
|
||||
{
|
||||
int pos = H->f1(E) % H->size;
|
||||
int f2 = -1;
|
||||
int ctr = 0;
|
||||
|
||||
while (H->entries[pos] != &unallocated && H->entries[pos] != &deleted)
|
||||
{
|
||||
if (f2 == -1)
|
||||
f2 = H->f2(E) % (H->size - 1);
|
||||
|
||||
pos = (pos + f2 + 1) % H->size;
|
||||
ctr++;
|
||||
}
|
||||
|
||||
if(H->entries[pos] == &unallocated)
|
||||
H->fill++; /* only increase fill if the previous element was not yet
|
||||
* counted, i.e. unallocated */
|
||||
|
||||
H->inuse++;
|
||||
H->entries[pos] = E;
|
||||
|
||||
if(hint)
|
||||
*hint = pos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rehash(struct hashtable *H)
|
||||
{
|
||||
/* resize the table */
|
||||
|
||||
int size = H->size;
|
||||
T_HashTableEl *oldentries = H->entries;
|
||||
|
||||
if(alloc_ht(H,((H->inuse+1)*4+H->fill)/5))
|
||||
return -1;
|
||||
|
||||
int i;
|
||||
for(i = 0; i < size; i++)
|
||||
{
|
||||
if (oldentries[i] != &unallocated && oldentries[i] != &deleted)
|
||||
_hash_add(H, oldentries[i], 0);
|
||||
}
|
||||
|
||||
free(oldentries);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hash_add(struct hashtable *H, T_HashTableEl *E, int *hint)
|
||||
{
|
||||
if (H->fill >= H->max)
|
||||
rehash(H);
|
||||
|
||||
if (H->fill == H->size)
|
||||
return -1; /*out of memory error */
|
||||
|
||||
return _hash_add(H,E, hint);
|
||||
}
|
||||
|
||||
/* add into hash table without checking for repeats */
|
||||
static int _hash_lookup(struct hashtable *H,T_HashTableEl *E, T_HashTableEl **E2, int *hint, int isIdentity)
|
||||
{
|
||||
int pos = H->f1(E) % H->size;
|
||||
int ttl = H->size;
|
||||
int f2 = -1;
|
||||
int upos = -1;
|
||||
|
||||
while(ttl && H->entries[pos] != &unallocated && (H->entries[pos] == &deleted
|
||||
|| ((isIdentity || H->compar(H->entries[pos], E) != 0) && (!isIdentity || H->entries[pos] != E))))
|
||||
{
|
||||
if (f2 == -1)
|
||||
f2 = H->f2(E) % (H->size - 1);
|
||||
|
||||
if (upos == -1 && H->entries[pos] == &deleted)
|
||||
upos = pos;
|
||||
|
||||
pos = (pos + f2 + 1) % H->size;
|
||||
ttl--;
|
||||
}
|
||||
|
||||
if(H->entries[pos] == &unallocated || !ttl)
|
||||
return -1;
|
||||
|
||||
if (upos != -1)
|
||||
{
|
||||
H->entries[upos] = H->entries[pos];
|
||||
H->entries[pos] = &deleted;
|
||||
pos = upos;
|
||||
}
|
||||
|
||||
if(hint)
|
||||
*hint = pos;
|
||||
|
||||
*E2 = H->entries[pos];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hash_lookup(struct hashtable *H,T_HashTableEl *E, T_HashTableEl **E2, int *hint)
|
||||
{
|
||||
return _hash_lookup(H, E, E2, hint, 0);
|
||||
}
|
||||
|
||||
/* add into hash table without checking for repeats */
|
||||
int hash_remove(struct hashtable *H,T_HashTableEl *E, int hint)
|
||||
{
|
||||
T_HashTableEl *E2;
|
||||
|
||||
if (hint >=0 && hint < H->size && H->entries[hint] == E)
|
||||
{
|
||||
H->inuse--;
|
||||
H->entries[hint] = &deleted;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_hash_lookup(H, E, &E2, &hint, 1))
|
||||
{
|
||||
fprintf(stderr, "Removing non-existent entry\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
H->inuse--;
|
||||
H->entries[hint] = &deleted;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
/* Copyright 1996,1997,2001,2002,2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* hashtable
|
||||
*/
|
||||
|
||||
#ifndef MTOOLS_HTABLE_H
|
||||
#define MTOOLS_HTABLE_H
|
||||
|
||||
struct hashtable;
|
||||
|
||||
typedef void *T_HashTableEl;
|
||||
typedef unsigned int (*T_HashFunc)(void *);
|
||||
typedef int (*T_ComparFunc)(void *, void *);
|
||||
|
||||
int make_ht(T_HashFunc f1, T_HashFunc f2, T_ComparFunc c, int size, struct hashtable **H);
|
||||
int hash_add(struct hashtable *H, T_HashTableEl *E, int *hint);
|
||||
int hash_remove(struct hashtable *H, T_HashTableEl *E, int hint);
|
||||
int hash_lookup(struct hashtable *H, T_HashTableEl *E, T_HashTableEl **E2, int *hint);
|
||||
|
||||
#endif
|
|
@ -1,342 +0,0 @@
|
|||
/* Copyright 1986-1992 Emmet P. Gray.
|
||||
* Copyright 1996-2002,2006-2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "msdos.h"
|
||||
#include "stream.h"
|
||||
#include "mtools.h"
|
||||
#include "fs.h"
|
||||
#include "plain_io.h"
|
||||
#include "buffer.h"
|
||||
#include "file_name.h"
|
||||
#include "fat.h"
|
||||
#include "force_io.h"
|
||||
#include "devices.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define BOOTSIZE 512
|
||||
|
||||
/*
|
||||
* Read the boot sector. We glean the disk parameters from this sector.
|
||||
*/
|
||||
static int read_boot(struct Stream_t *Stream, union bootsector * boot, int size)
|
||||
{
|
||||
/* read the first sector, or part of it */
|
||||
if(!size)
|
||||
size = BOOTSIZE;
|
||||
if(size > MAX_BOOT)
|
||||
size = MAX_BOOT;
|
||||
|
||||
if (force_read(Stream, boot->characters, 0, size) != size)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fs_flush(struct Stream_t *Stream)
|
||||
{
|
||||
DeclareThis(struct Fs_t);
|
||||
|
||||
fat_write(This);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct doscp_t *get_dosConvert(struct Stream_t *Stream)
|
||||
{
|
||||
DeclareThis(struct Fs_t);
|
||||
return This->cp;
|
||||
}
|
||||
|
||||
static unsigned int log_2(int size)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for(i = 0; i < 24; i++)
|
||||
if (1 << i == size)
|
||||
return i;
|
||||
|
||||
return 24;
|
||||
}
|
||||
|
||||
static struct Class_t FsClass = {
|
||||
read_pass_through, /* read */
|
||||
write_pass_through, /* write */
|
||||
fs_flush,
|
||||
fs_free, /* free */
|
||||
0, /* set geometry */
|
||||
get_data_pass_through,
|
||||
0, /* pre allocate */
|
||||
get_dosConvert, /* dosconvert */
|
||||
};
|
||||
|
||||
static int get_media_type(struct Stream_t *St, union bootsector *boot)
|
||||
{
|
||||
int media;
|
||||
|
||||
media = boot->boot.descr;
|
||||
if(media < 0xf0){
|
||||
char temp[512];
|
||||
/* old DOS disk. Media descriptor in the first FAT byte */
|
||||
/* old DOS disk always have 512-byte sectors */
|
||||
if (force_read(St,temp,(off_t) 512,512) == 512)
|
||||
media = (unsigned char) temp[0];
|
||||
else
|
||||
media = 0;
|
||||
} else
|
||||
media += 0x100;
|
||||
return media;
|
||||
}
|
||||
|
||||
struct Stream_t *GetFs(struct Stream_t *Fs)
|
||||
{
|
||||
while(Fs && Fs->Class != &FsClass)
|
||||
Fs = Fs->Next;
|
||||
return Fs;
|
||||
}
|
||||
|
||||
static struct Stream_t *open_stream(const char* deviceName, int mode, struct device *out_dev, union bootsector *boot, int *media, size_t *maxSize)
|
||||
{
|
||||
char errmsg[200];
|
||||
int r;
|
||||
|
||||
snprintf(errmsg, 199, "Drive '%s:' not supported", deviceName);
|
||||
|
||||
struct device dev;
|
||||
memset(&dev, 0, sizeof(dev));
|
||||
dev.name = deviceName;
|
||||
|
||||
*out_dev = dev;
|
||||
|
||||
struct Stream_t *Stream = SimpleFileOpen(out_dev, &dev, dev.name, mode, errmsg, 0, 1, maxSize);
|
||||
|
||||
if (!Stream)
|
||||
{
|
||||
fprintf(stderr, "open_stream: opening file failed: %s.\n", errmsg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* read the boot sector */
|
||||
if ((r = read_boot(Stream, boot, out_dev->blocksize)) < 0)
|
||||
{
|
||||
snprintf(errmsg, 199, "init %s: could not read boot sector", deviceName);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if((*media = get_media_type(Stream, boot)) <= 0xf0 )
|
||||
{
|
||||
if (boot->boot.jump[2]=='L')
|
||||
snprintf(errmsg, 199, "diskette %s: is Linux LILO, not DOS", deviceName);
|
||||
else
|
||||
snprintf(errmsg, 199, "init %s: non DOS media", deviceName);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* set new parameters, if needed */
|
||||
errno = 0;
|
||||
|
||||
if (SET_GEOM(Stream, out_dev, &dev, *media, boot))
|
||||
{
|
||||
if (errno)
|
||||
snprintf(errmsg, 199, "Can't set disk parameters for %s: %s", deviceName, strerror(errno));
|
||||
else
|
||||
snprintf(errmsg, 199, "Can't set disk parameters for %s", deviceName);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
/* print error msg if needed */
|
||||
if (Stream == NULL)
|
||||
{
|
||||
free_stream(&Stream);
|
||||
fprintf(stderr, "%s\n", errmsg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return Stream;
|
||||
}
|
||||
|
||||
struct Stream_t *fs_init(const char* deviceName, int mode)
|
||||
{
|
||||
struct Fs_t *This = New(struct Fs_t);
|
||||
|
||||
if (!This)
|
||||
{
|
||||
fprintf(stderr, "fs_init: Creating fs struct failed.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
This->Direct = NULL;
|
||||
This->Next = NULL;
|
||||
This->refs = 1;
|
||||
This->Buffer = 0;
|
||||
This->Class = &FsClass;
|
||||
This->preallocatedClusters = 0;
|
||||
This->lastFatSectorNr = 0;
|
||||
This->lastFatAccessMode = 0;
|
||||
This->lastFatSectorData = 0;
|
||||
This->last = 0;
|
||||
|
||||
struct device dev;
|
||||
union bootsector boot;
|
||||
int media = 0;
|
||||
size_t maxSize = 0;
|
||||
This->Direct = open_stream(deviceName, mode, &dev, &boot, &media, &maxSize);
|
||||
if(!This->Direct)
|
||||
{
|
||||
fprintf(stderr, "fs_init: opening stream failed.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
This->sector_size = WORD_S(secsiz);
|
||||
|
||||
if(This->sector_size > MAX_SECTOR)
|
||||
{
|
||||
fprintf(stderr,"init %s: sector size too big\n", deviceName);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int i = log_2(This->sector_size);
|
||||
|
||||
if(i == 24)
|
||||
{
|
||||
fprintf(stderr, "init %s: sector size (%d) not a small power of two\n", deviceName, This->sector_size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
This->sectorShift = i;
|
||||
This->sectorMask = This->sector_size - 1;
|
||||
|
||||
int cylinder_size = dev.heads * dev.sectors;
|
||||
This->serialized = 0;
|
||||
|
||||
struct label_blk_t *labelBlock;
|
||||
/*
|
||||
* all numbers are in sectors, except num_clus
|
||||
* (which is in clusters)
|
||||
*/
|
||||
int nhs = 0;
|
||||
size_t tot_sectors = WORD_S(psect);
|
||||
if(!tot_sectors)
|
||||
{
|
||||
tot_sectors = DWORD_S(bigsect);
|
||||
nhs = DWORD_S(nhs);
|
||||
}
|
||||
else
|
||||
nhs = WORD_S(nhs);
|
||||
|
||||
This->cluster_size = boot.boot.clsiz;
|
||||
This->fat_start = WORD_S(nrsvsect);
|
||||
This->fat_len = WORD_S(fatlen);
|
||||
This->dir_len = WORD_S(dirents) * MDIR_SIZE / This->sector_size;
|
||||
This->num_fat = boot.boot.nfat;
|
||||
|
||||
if (This->fat_len)
|
||||
labelBlock = &boot.boot.ext.old.labelBlock;
|
||||
else
|
||||
labelBlock = &boot.boot.ext.fat32.labelBlock;
|
||||
|
||||
if (labelBlock->dos4 == 0x29)
|
||||
{
|
||||
This->serialized = 1;
|
||||
This->serial_number = _DWORD(labelBlock->serial);
|
||||
}
|
||||
|
||||
if (tot_sectors >= (maxSize >> This->sectorShift))
|
||||
{
|
||||
fprintf(stderr, "Big disks not supported on this architecture\n");
|
||||
return NULL; // TODO: make caller check return code
|
||||
}
|
||||
|
||||
int disk_size = cylinder_size;
|
||||
|
||||
if(disk_size > 256)
|
||||
{
|
||||
disk_size = dev.sectors;
|
||||
if(dev.sectors % 2)
|
||||
disk_size <<= 1;
|
||||
}
|
||||
|
||||
if (disk_size % 2)
|
||||
disk_size *= 2;
|
||||
|
||||
int blocksize = (!dev.blocksize || dev.blocksize < This->sector_size) ? This->sector_size : dev.blocksize;
|
||||
|
||||
if (disk_size)
|
||||
This->Next = buf_init(This->Direct, 8 * disk_size * blocksize, disk_size * blocksize, This->sector_size);
|
||||
else
|
||||
This->Next = This->Direct;
|
||||
|
||||
if (This->Next == NULL)
|
||||
{
|
||||
perror("init: allocate buffer");
|
||||
This->Next = This->Direct;
|
||||
}
|
||||
|
||||
/* read the FAT sectors */
|
||||
if(fat_read(This, &boot, dev.fat_bits, tot_sectors, dev.use_2m & 0x7f))
|
||||
{
|
||||
fprintf(stderr, "fs_init: Reading FAT failed.\n");
|
||||
This->num_fat = 1;
|
||||
free_stream(&This->Next);
|
||||
free(This->Next);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Set the codepage */
|
||||
This->cp = cp_open(dev.codepage);
|
||||
if(This->cp == NULL)
|
||||
{
|
||||
fprintf(stderr, "fs_init: Setting code page failed.\n");
|
||||
fs_free((struct Stream_t *)This);
|
||||
free_stream(&This->Next);
|
||||
free(This->Next);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (struct Stream_t*) This;
|
||||
}
|
||||
|
||||
int fs_close(struct Stream_t* Stream)
|
||||
{
|
||||
DeclareThis(struct Fs_t);
|
||||
|
||||
int rval = SimpleFileClose(This->Direct);
|
||||
|
||||
if (This->Next != This->Direct)
|
||||
{
|
||||
free_stream(&This->Next);
|
||||
free(This->Next);
|
||||
}
|
||||
|
||||
fs_free((struct Stream_t *)This);
|
||||
free(This);
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
int fsPreallocateClusters(struct Fs_t *Fs, long size)
|
||||
{
|
||||
if(size > 0 && getfreeMinClusters((struct Stream_t *)Fs, size) != 1)
|
||||
return -1;
|
||||
|
||||
Fs->preallocatedClusters += size;
|
||||
return 0;
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
/* Copyright 1996-2005,2007-2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MTOOLS_INIT_H
|
||||
#define MTOOLS_INIT_H
|
||||
|
||||
struct Stream_t;
|
||||
struct Fs_t;
|
||||
|
||||
struct Stream_t *fs_init(const char* deviceName, int mode);
|
||||
struct Stream_t *GetFs(struct Stream_t *Fs);
|
||||
int fs_close(struct Stream_t* Fs);
|
||||
int fsPreallocateClusters(struct Fs_t *Fs, long);
|
||||
|
||||
#endif
|
|
@ -1,57 +0,0 @@
|
|||
/* Copyright 1999-2003,2006,2008,2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "stream.h"
|
||||
#include "fs.h"
|
||||
#include "llong.h"
|
||||
#include "mtools.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
const off_t max_off_t_32 = MAX_OFF_T_B(32); /* Directory */
|
||||
const off_t max_off_t_seek = MAX_OFF_T_B(SEEK_BITS); /* SCSI */
|
||||
|
||||
static int fileTooBig(off_t off)
|
||||
{
|
||||
return (off & ~max_off_t_32) != 0;
|
||||
}
|
||||
|
||||
off_t truncBytes32(off_t off)
|
||||
{
|
||||
if (fileTooBig(off))
|
||||
{
|
||||
fprintf(stderr, "Internal error, offset too big\n");
|
||||
return off; // TODO: this used to be an exit(1)...
|
||||
}
|
||||
|
||||
return (off_t) off;
|
||||
}
|
||||
|
||||
off_t sectorsToBytes(struct Stream_t *Stream, off_t off)
|
||||
{
|
||||
DeclareThis(struct Fs_t);
|
||||
return (off_t) off << This->sectorShift;
|
||||
}
|
||||
|
||||
int mt_lseek(int fd, off_t where, int whence)
|
||||
{
|
||||
return lseek64(fd, where, whence) >= 0 ? 0 : -1;
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
/* Copyright 1999,2001-2004,2007-2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MTOOLS_LLONG_H
|
||||
#define MTOOLS_LLONG_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define min(a,b) ((a) < (b) ? (a) : (b))
|
||||
#define MAX_OFF_T_B(bits) ((((off_t) 1 << min(bits-1, sizeof(off_t)*8 - 2)) -1) << 1 | 1)
|
||||
|
||||
#define SEEK_BITS 63
|
||||
|
||||
struct Stream_t;
|
||||
|
||||
extern const off_t max_off_t_32;
|
||||
extern const off_t max_off_t_seek;
|
||||
|
||||
off_t truncBytes32(off_t off);
|
||||
off_t sectorsToBytes(struct Stream_t *This, off_t off);
|
||||
int mt_lseek(int fd, off_t where, int whence);
|
||||
|
||||
#endif
|
|
@ -1,186 +0,0 @@
|
|||
/* Copyright 1986-1992 Emmet P. Gray.
|
||||
* Copyright 1996-1998,2001,2002,2008,2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Do shell-style pattern matching for '?', '\', '[..]', and '*' wildcards.
|
||||
* Returns 1 if match, 0 if not.
|
||||
*/
|
||||
|
||||
#include "mtools.h"
|
||||
|
||||
#include <wctype.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
static int casecmp(wchar_t a, wchar_t b)
|
||||
{
|
||||
return towupper(a) == towupper(b);
|
||||
}
|
||||
|
||||
static int exactcmp(wchar_t a,wchar_t b)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
|
||||
static int parse_range(const wchar_t **p, const wchar_t *s, wchar_t *out, int (*compfn)(wchar_t a, wchar_t b))
|
||||
{
|
||||
wchar_t table[256];
|
||||
int reverse;
|
||||
int i;
|
||||
|
||||
if (**p == '^')
|
||||
{
|
||||
reverse = 1;
|
||||
(*p)++;
|
||||
}
|
||||
else
|
||||
reverse = 0;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
table[i] = 0;
|
||||
|
||||
while (**p != ']')
|
||||
{
|
||||
if(!**p)
|
||||
return 0;
|
||||
|
||||
if((*p)[1] == '-')
|
||||
{
|
||||
short first = **p;
|
||||
(*p) += 2;
|
||||
|
||||
short last = (**p == ']') ? 256 : *((*p)++);
|
||||
|
||||
for(i = first; i <= last; i++)
|
||||
table[i] = 1;
|
||||
}
|
||||
else
|
||||
table[(int) *((*p)++)] = 1;
|
||||
}
|
||||
|
||||
if(out)
|
||||
*out = *s;
|
||||
|
||||
if(table[(int) *s])
|
||||
return 1 ^ reverse;
|
||||
|
||||
if(compfn == exactcmp)
|
||||
return reverse;
|
||||
|
||||
if(table[tolower(*s)])
|
||||
{
|
||||
if(out)
|
||||
*out = tolower(*s);
|
||||
|
||||
return 1 ^ reverse;
|
||||
}
|
||||
|
||||
if(table[toupper(*s)])
|
||||
{
|
||||
if(out)
|
||||
*out = toupper(*s);
|
||||
|
||||
return 1 ^ reverse;
|
||||
}
|
||||
|
||||
return reverse;
|
||||
}
|
||||
|
||||
|
||||
static int _match(const wchar_t *s, const wchar_t *p, wchar_t *out, int Case, int length, int (*compfn) (wchar_t a, wchar_t b))
|
||||
{
|
||||
for (; *p != '\0' && length; )
|
||||
{
|
||||
switch (*p)
|
||||
{
|
||||
case '?': /* match any one character */
|
||||
if (*s == '\0')
|
||||
return(0);
|
||||
if(out)
|
||||
*(out++) = *s;
|
||||
break;
|
||||
|
||||
case '*': /* match everything */
|
||||
while (*p == '*' && length)
|
||||
{
|
||||
p++;
|
||||
length--;
|
||||
}
|
||||
|
||||
/* search for next char in pattern */
|
||||
while(*s)
|
||||
{
|
||||
if(_match(s, p, out, Case, length,
|
||||
compfn))
|
||||
return 1;
|
||||
if(out)
|
||||
*out++ = *s;
|
||||
s++;
|
||||
}
|
||||
continue;
|
||||
|
||||
case '[': /* match range of characters */
|
||||
p++;
|
||||
length--;
|
||||
if(!parse_range(&p, s, out++, compfn))
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case '\\': /* Literal match with next character */
|
||||
p++;
|
||||
length--;
|
||||
/* fall thru */
|
||||
|
||||
default:
|
||||
if (!compfn(*s,*p))
|
||||
return(0);
|
||||
|
||||
if(out)
|
||||
*(out++) = *p;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
p++;
|
||||
length--;
|
||||
s++;
|
||||
}
|
||||
|
||||
if(out)
|
||||
*out = '\0';
|
||||
|
||||
/* string ended prematurely ? */
|
||||
return *s != '\0' ? 0 : 1;
|
||||
}
|
||||
|
||||
|
||||
int match(const wchar_t *s, const wchar_t *p, wchar_t *out, int Case, int length)
|
||||
{
|
||||
int (*compfn)(wchar_t a, wchar_t b);
|
||||
|
||||
if(Case)
|
||||
compfn = casecmp;
|
||||
else
|
||||
/*compfn = exactcmp;*/
|
||||
compfn = casecmp;
|
||||
|
||||
return _match(s, p, out, Case, length, compfn);
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
/* Copyright 1996-2005,2007-2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MTOOLS_MATCH_H
|
||||
#define MTOOLS_MATCH_H
|
||||
|
||||
int match(const wchar_t *, const wchar_t *, wchar_t *, int, int);
|
||||
|
||||
#endif
|
|
@ -1,197 +0,0 @@
|
|||
/* Copyright 1986-1992 Emmet P. Gray.
|
||||
* Copyright 1996-1998,2000-2003,2006,2007,2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MTOOLS_MSDOS_H
|
||||
#define MTOOLS_MSDOS_H
|
||||
|
||||
#define MAX_SECTOR 8192 /* largest sector size */
|
||||
#define MDIR_SIZE 32 /* MSDOS directory entry size in bytes*/
|
||||
#define MAX_CLUSTER 8192 /* largest cluster size */
|
||||
#define MAX_DIR_SECS 64 /* largest directory (in sectors) */
|
||||
#define MSECTOR_SIZE msector_size
|
||||
|
||||
#define _WORD(x) ((unsigned short)((unsigned char)(x)[0] + (((unsigned char)(x)[1]) << 8)))
|
||||
#define _DWORD(x) ((unsigned int)(_WORD(x) + (_WORD((x)+2) << 16)))
|
||||
|
||||
#define DELMARK ((char) 0xe5)
|
||||
|
||||
#define EXTCASE 0x10
|
||||
#define BASECASE 0x8
|
||||
|
||||
#define MAX16 0xffff
|
||||
#define MAX32 0xffffffff
|
||||
#define MAX_SIZE 0x7fffffff
|
||||
|
||||
#define FILE_SIZE(dir) (_DWORD((dir)->size))
|
||||
#define START(dir) (_WORD((dir)->start))
|
||||
#define STARTHI(dir) (_WORD((dir)->startHi))
|
||||
|
||||
/* ASSUMPTION: long is at least 32 bits */
|
||||
static inline void set_dword(unsigned char *data, unsigned long value)
|
||||
{
|
||||
data[3] = (value >> 24) & 0xff;
|
||||
data[2] = (value >> 16) & 0xff;
|
||||
data[1] = (value >> 8) & 0xff;
|
||||
data[0] = (value >> 0) & 0xff;
|
||||
}
|
||||
|
||||
/* ASSUMPTION: short is at least 16 bits */
|
||||
static inline void set_word(unsigned char *data, unsigned short value)
|
||||
{
|
||||
data[1] = (value >> 8) & 0xff;
|
||||
data[0] = (value >> 0) & 0xff;
|
||||
}
|
||||
|
||||
struct InfoSector_t
|
||||
{
|
||||
unsigned char signature1[4];
|
||||
unsigned char filler1[0x1e0];
|
||||
unsigned char signature2[4];
|
||||
unsigned char count[4];
|
||||
unsigned char pos[4];
|
||||
unsigned char filler2[14];
|
||||
unsigned char signature3[2];
|
||||
};
|
||||
|
||||
#define INFOSECT_SIGNATURE1 0x41615252
|
||||
#define INFOSECT_SIGNATURE2 0x61417272
|
||||
|
||||
struct label_blk_t
|
||||
{
|
||||
unsigned char physdrive; /* 36 physical drive ? */
|
||||
unsigned char reserved; /* 37 reserved */
|
||||
unsigned char dos4; /* 38 dos > 4.0 diskette */
|
||||
unsigned char serial[4]; /* 39 serial number */
|
||||
char label[11]; /* 43 disk label */
|
||||
char fat_type[8]; /* 54 FAT type */
|
||||
};
|
||||
|
||||
/* FAT32 specific info in the bootsector */
|
||||
struct fat32_t
|
||||
{
|
||||
unsigned char bigFat[4]; /* 36 nb of sectors per FAT */
|
||||
unsigned char extFlags[2]; /* 40 extension flags */
|
||||
unsigned char fsVersion[2]; /* 42 ? */
|
||||
unsigned char rootCluster[4]; /* 44 start cluster of root dir */
|
||||
unsigned char infoSector[2]; /* 48 changeable global info */
|
||||
unsigned char backupBoot[2]; /* 50 back up boot sector */
|
||||
unsigned char reserved[6]; /* 52 ? */
|
||||
unsigned char reserved2[6]; /* 52 ? */
|
||||
struct label_blk_t labelBlock;
|
||||
}; /* ends at 58 */
|
||||
|
||||
struct oldboot_t
|
||||
{
|
||||
struct label_blk_t labelBlock;
|
||||
unsigned char res_2m; /* 62 reserved by 2M */
|
||||
unsigned char CheckSum; /* 63 2M checksum (not used) */
|
||||
unsigned char fmt_2mf; /* 64 2MF format version */
|
||||
unsigned char wt; /* 65 1 if write track after format */
|
||||
unsigned char rate_0; /* 66 data transfer rate on track 0 */
|
||||
unsigned char rate_any; /* 67 data transfer rate on track<>0 */
|
||||
unsigned char BootP[2]; /* 68 offset to boot program */
|
||||
unsigned char Infp0[2]; /* 70 T1: information for track 0 */
|
||||
unsigned char InfpX[2]; /* 72 T2: information for track<>0 */
|
||||
unsigned char InfTm[2]; /* 74 T3: track sectors size table */
|
||||
unsigned char DateF[2]; /* 76 Format date */
|
||||
unsigned char TimeF[2]; /* 78 Format time */
|
||||
unsigned char junk[1024 - 80]; /* 80 remaining data */
|
||||
};
|
||||
|
||||
struct bootsector_s
|
||||
{
|
||||
unsigned char jump[3]; /* 0 Jump to boot code */
|
||||
char banner[8]; /* 3 OEM name & version */
|
||||
unsigned char secsiz[2]; /* 11 Bytes per sector hopefully 512 */
|
||||
unsigned char clsiz; /* 13 Cluster size in sectors */
|
||||
unsigned char nrsvsect[2]; /* 14 Number of reserved (boot) sectors */
|
||||
unsigned char nfat; /* 16 Number of FAT tables hopefully 2 */
|
||||
unsigned char dirents[2]; /* 17 Number of directory slots */
|
||||
unsigned char psect[2]; /* 19 Total sectors on disk */
|
||||
unsigned char descr; /* 21 Media descriptor=first byte of FAT */
|
||||
unsigned char fatlen[2]; /* 22 Sectors in FAT */
|
||||
unsigned char nsect[2]; /* 24 Sectors/track */
|
||||
unsigned char nheads[2]; /* 26 Heads */
|
||||
unsigned char nhs[4]; /* 28 number of hidden sectors */
|
||||
unsigned char bigsect[4]; /* 32 big total sectors */
|
||||
|
||||
union
|
||||
{
|
||||
struct fat32_t fat32;
|
||||
struct oldboot_t old;
|
||||
} ext;
|
||||
};
|
||||
|
||||
#define MAX_BOOT 4096
|
||||
|
||||
union bootsector
|
||||
{
|
||||
unsigned char bytes[MAX_BOOT];
|
||||
char characters[MAX_BOOT];
|
||||
struct bootsector_s boot;
|
||||
};
|
||||
|
||||
#define CHAR(x) (boot->x[0])
|
||||
#define WORD(x) (_WORD(boot->boot.x))
|
||||
#define DWORD(x) (_DWORD(boot->boot.x))
|
||||
|
||||
#define WORD_S(x) (_WORD(boot.boot.x))
|
||||
#define DWORD_S(x) (_DWORD(boot.boot.x))
|
||||
|
||||
#define OFFSET(x) (((char *) (boot->x)) - ((char *)(boot->jump)))
|
||||
|
||||
/* max FAT12/FAT16 sizes, according to
|
||||
|
||||
http://www.microsoft.com/hwdev/download/hardware/fatgen103.pdf
|
||||
|
||||
interestingly enough, another Microsoft document
|
||||
[http://support.microsoft.com/default.aspx?scid=kb%3ben-us%3b67321]
|
||||
gives different values, but the first seems to be more sure about
|
||||
itself, so we believe that one ;-)
|
||||
*/
|
||||
#define FAT12 4085 /* max. number of clusters described by a 12 bit FAT */
|
||||
#define FAT16 65525 /* max number of clusters for a 16 bit FAT */
|
||||
|
||||
#define ATTR_ARCHIVE 0x20
|
||||
#define ATTR_DIR 0x10
|
||||
#define ATTR_LABEL 0x8
|
||||
#define ATTR_SYSTEM 0x4
|
||||
#define ATTR_HIDDEN 0x2
|
||||
#define ATTR_READONLY 0x1
|
||||
|
||||
#define HAS_BIT(entry,x) ((entry)->dir.attr & (x))
|
||||
|
||||
#define IS_ARCHIVE(entry) (HAS_BIT((entry),ATTR_ARCHIVE))
|
||||
#define IS_DIR(entry) (HAS_BIT((entry),ATTR_DIR))
|
||||
#define IS_LABEL(entry) (HAS_BIT((entry),ATTR_LABEL))
|
||||
#define IS_SYSTEM(entry) (HAS_BIT((entry),ATTR_SYSTEM))
|
||||
#define IS_HIDDEN(entry) (HAS_BIT((entry),ATTR_HIDDEN))
|
||||
#define IS_READONLY(entry) (HAS_BIT((entry),ATTR_READONLY))
|
||||
|
||||
|
||||
#define MAX_BYTES_PER_CLUSTER (32*1024)
|
||||
/* Experimentally, it turns out that DOS only accepts cluster sizes
|
||||
* which are powers of two, and less than 128 sectors (else it gets a
|
||||
* divide overflow) */
|
||||
|
||||
#define MT_READ 1
|
||||
#define MT_WRITE 2
|
||||
|
||||
#endif
|
|
@ -1,36 +0,0 @@
|
|||
/* Copyright 1996-2005,2007-2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MTOOLS_MTOOLS_H
|
||||
#define MTOOLS_MTOOLS_H
|
||||
|
||||
#define UNUSED(x) x __attribute__ ((unused))
|
||||
#define New(type) ((type*)(calloc(1,sizeof(type))))
|
||||
#define Grow(adr,n,type) ((type*)(realloc((char *)adr,n*sizeof(type))))
|
||||
#define NewArray(size,type) ((type*)(calloc((size),sizeof(type))))
|
||||
#define maximize(target, max) do { \
|
||||
if(max < 0) \
|
||||
{ \
|
||||
if(target > 0) \
|
||||
target = 0; \
|
||||
} \
|
||||
else if(target > max) \
|
||||
target = max; \
|
||||
} while(0)
|
||||
|
||||
#endif
|
|
@ -1,55 +0,0 @@
|
|||
/* Copyright 1996-1998,2000-2002,2008,2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MTOOLS_NAMECLASH_H
|
||||
#define MTOOLS_NAMECLASH_H
|
||||
|
||||
struct dos_name_t;
|
||||
struct doscp_t;
|
||||
|
||||
typedef enum clash_action
|
||||
{
|
||||
NAMEMATCH_SUCCESS = 0,
|
||||
NAMEMATCH_NONE,
|
||||
NAMEMATCH_SKIP,
|
||||
NAMEMATCH_ERROR,
|
||||
NAMEMATCH_GREW
|
||||
} clash_action;
|
||||
|
||||
/* clash handling structure */
|
||||
struct ClashHandling_t
|
||||
{
|
||||
clash_action action[2];
|
||||
clash_action namematch_default[2];
|
||||
|
||||
int nowarn; /* Don't ask, just do default action if name collision*/
|
||||
int got_slots;
|
||||
int mod_time;
|
||||
char *myname;
|
||||
unsigned char *dosname;
|
||||
int single;
|
||||
|
||||
int use_longname;
|
||||
int ignore_entry;
|
||||
int source; /* to prevent the source from overwriting itself */
|
||||
int source_entry; /* to account for the space freed up by the original name */
|
||||
void (*name_converter)(struct doscp_t *cp, const char *filename, int *mangled, struct dos_name_t *ans);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -1,49 +0,0 @@
|
|||
/* Copyright 1997,1998,2001-2003,2006,2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MTOOLS_PARTITION_H
|
||||
#define MTOOLS_PARTITION_H
|
||||
|
||||
struct hsc
|
||||
{
|
||||
unsigned char byte0;
|
||||
unsigned char head; /* starting head */
|
||||
unsigned char sector; /* starting sector */
|
||||
unsigned char cyl; /* starting cylinder */
|
||||
};
|
||||
|
||||
#define head(x) ((unsigned int)((x).head))
|
||||
#define sector(x) ((unsigned int)((x).sector & 0x3f))
|
||||
#define cyl(x) ((unsigned int)((x).cyl | (((x).sector & 0xc0)<<2)))
|
||||
|
||||
#define BEGIN(p) _DWORD((p).start_sect)
|
||||
#define END(p) (_DWORD((p).start_sect)+(_DWORD((p).nr_sects)))
|
||||
|
||||
#define boot_ind start.byte0
|
||||
#define sys_ind end.byte0
|
||||
|
||||
struct partition
|
||||
{
|
||||
struct hsc start;
|
||||
struct hsc end;
|
||||
unsigned char start_sect[4]; /* starting sector counting from 0 */
|
||||
unsigned char nr_sects[4]; /* nr of sectors in partition */
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -1,426 +0,0 @@
|
|||
/* Copyright 1995-2007,2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Io to a plain file or device
|
||||
*
|
||||
* written by:
|
||||
*
|
||||
* Alain L. Knaff
|
||||
* alain@knaff.lu
|
||||
*
|
||||
*/
|
||||
|
||||
#include "stream.h"
|
||||
#include "mtools.h"
|
||||
#include "msdos.h"
|
||||
#include "plain_io.h"
|
||||
#include "partition.h"
|
||||
#include "llong.h"
|
||||
#include "force_io.h"
|
||||
#include "devices.h"
|
||||
|
||||
#include <sys/file.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
struct SimpleFile_t
|
||||
{
|
||||
struct Class_t *Class;
|
||||
int refs;
|
||||
struct Stream_t *Next;
|
||||
struct Stream_t *Buffer;
|
||||
struct stat64 statbuf;
|
||||
int fd;
|
||||
off_t offset;
|
||||
off_t lastwhere;
|
||||
int seekable;
|
||||
int privileged;
|
||||
int scsi_sector_size;
|
||||
void *extra_data; /* extra system dependant information for scsi */
|
||||
int swap; /* do the word swapping */
|
||||
};
|
||||
|
||||
static int lock_dev(int fd, int mode)
|
||||
{
|
||||
if (flock(fd, (mode ? LOCK_EX : LOCK_SH) | LOCK_NB) < 0)
|
||||
return errno == EINVAL || errno == EOPNOTSUPP ? 0 : 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef int (*iofn) (int, char *, int);
|
||||
|
||||
static void swap_buffer(char *buf, size_t len)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i<len; i+=2)
|
||||
{
|
||||
char temp = buf[i];
|
||||
buf[i] = buf[i+1];
|
||||
buf[i+1] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
static int file_io(struct Stream_t *Stream, char *buf, off_t where, int len, iofn io)
|
||||
{
|
||||
DeclareThis(struct SimpleFile_t);
|
||||
|
||||
where += This->offset;
|
||||
|
||||
if (This->seekable && where != This->lastwhere )
|
||||
{
|
||||
if(mt_lseek( This->fd, where, SEEK_SET) < 0 )
|
||||
{
|
||||
perror("seek");
|
||||
This->lastwhere = (off_t) -1;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int ret = io(This->fd, buf, len);
|
||||
|
||||
if (ret == -1 )
|
||||
{
|
||||
perror("plain_io");
|
||||
This->lastwhere = (off_t) -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
This->lastwhere = where + ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int file_read(struct Stream_t *Stream, char *buf, off_t where, size_t len)
|
||||
{
|
||||
DeclareThis(struct SimpleFile_t);
|
||||
|
||||
int result = file_io(Stream, buf, where, len, (iofn) read);
|
||||
|
||||
if (This->swap)
|
||||
swap_buffer(buf, len);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int file_write(struct Stream_t *Stream, char *buf, off_t where, size_t len)
|
||||
{
|
||||
DeclareThis(struct SimpleFile_t);
|
||||
|
||||
if (!This->swap)
|
||||
return file_io(Stream, buf, where, len, (iofn) write);
|
||||
else
|
||||
{
|
||||
char* swapping = malloc(len);
|
||||
|
||||
memcpy(swapping, buf, len);
|
||||
swap_buffer(swapping, len);
|
||||
|
||||
int result = file_io(Stream, swapping, where, len, (iofn) write);
|
||||
|
||||
free(swapping);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
static int file_flush(struct Stream_t UNUSED(*Stream))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int file_free(struct Stream_t *Stream)
|
||||
{
|
||||
DeclareThis(struct SimpleFile_t);
|
||||
return (This->fd > 2) ? close(This->fd) : 0;
|
||||
}
|
||||
|
||||
static int file_geom(struct Stream_t *Stream, struct device *dev, struct device *orig_dev, int media, union bootsector *boot)
|
||||
{
|
||||
DeclareThis(struct SimpleFile_t);
|
||||
|
||||
dev->ssize = 2; /* allow for init_geom to change it */
|
||||
dev->use_2m = 0x80; /* disable 2m mode to begin */
|
||||
|
||||
if(media == 0xf0 || media >= 0x100)
|
||||
{
|
||||
dev->heads = WORD(nheads);
|
||||
dev->sectors = WORD(nsect);
|
||||
|
||||
size_t tot_sectors = DWORD(bigsect);
|
||||
|
||||
if (WORD(psect))
|
||||
tot_sectors = WORD(psect);
|
||||
|
||||
int sect_per_track = dev->heads * dev->sectors;
|
||||
|
||||
if(sect_per_track == 0)
|
||||
{
|
||||
/* add some fake values if sect_per_track is
|
||||
* zero. Indeed, some atari disks lack the
|
||||
* geometry values (i.e. have zeroes in their
|
||||
* place). In order to avoid division by zero
|
||||
* errors later on, plug 1 everywhere
|
||||
*/
|
||||
dev->heads = 1;
|
||||
dev->sectors = 1;
|
||||
sect_per_track = 1;
|
||||
}
|
||||
|
||||
tot_sectors += sect_per_track - 1; /* round size up */
|
||||
dev->tracks = tot_sectors / sect_per_track;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr,"Unknown media type\n");
|
||||
return -1; // TODO: make sure this is interpreted as invalid return code
|
||||
}
|
||||
|
||||
int sectors = dev->sectors;
|
||||
dev->sectors = dev->sectors * WORD(secsiz) / 512;
|
||||
|
||||
int ret = init_geom(This->fd,dev, orig_dev, &This->statbuf);
|
||||
dev->sectors = sectors;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int file_data(struct Stream_t *Stream, time_t *date, size_t *size,
|
||||
int *type, int *address)
|
||||
{
|
||||
DeclareThis(struct SimpleFile_t);
|
||||
|
||||
if(date)
|
||||
*date = This->statbuf.st_mtime;
|
||||
|
||||
if(size)
|
||||
*size = This->statbuf.st_size;
|
||||
|
||||
if(type)
|
||||
*type = S_ISDIR(This->statbuf.st_mode);
|
||||
|
||||
if(address)
|
||||
*address = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct Class_t SimpleFileClass =
|
||||
{
|
||||
file_read,
|
||||
file_write,
|
||||
file_flush,
|
||||
file_free,
|
||||
file_geom,
|
||||
file_data,
|
||||
0, /* pre_allocate */
|
||||
0
|
||||
};
|
||||
|
||||
|
||||
struct Stream_t *SimpleFileOpen(struct device *dev, struct device *orig_dev,
|
||||
const char *name, int mode, char *errmsg,
|
||||
int mode2, int locked, size_t *maxSize)
|
||||
{
|
||||
struct SimpleFile_t *This;
|
||||
|
||||
This = New(struct SimpleFile_t);
|
||||
|
||||
if (!This)
|
||||
{
|
||||
fprintf(stderr, "%s %d: Allocation memory for simple file failed.\n", __FILE__, __LINE__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
This->scsi_sector_size = 512;
|
||||
This->seekable = 1;
|
||||
|
||||
This->Class = &SimpleFileClass;
|
||||
if (!name || strcmp(name,"-") == 0)
|
||||
{
|
||||
This->fd = mode == O_RDONLY ? 0 : 1;
|
||||
This->seekable = 0;
|
||||
This->refs = 1;
|
||||
This->Next = 0;
|
||||
This->Buffer = 0;
|
||||
|
||||
if (fstat64(This->fd, &This->statbuf) < 0)
|
||||
{
|
||||
free(This);
|
||||
|
||||
if(errmsg)
|
||||
snprintf(errmsg,199,"Can't stat -: %s", strerror(errno));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (struct Stream_t *) This;
|
||||
}
|
||||
|
||||
if(dev)
|
||||
mode |= dev->mode;
|
||||
|
||||
This->fd = open(name, mode | O_LARGEFILE, 0666);
|
||||
|
||||
if (This->fd < 0)
|
||||
{
|
||||
free(This);
|
||||
|
||||
if(errmsg)
|
||||
snprintf(errmsg, 199, "Can't open %s: %s", name, strerror(errno));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fstat64(This->fd, &This->statbuf) < 0)
|
||||
{
|
||||
free(This);
|
||||
|
||||
if(errmsg)
|
||||
snprintf(errmsg, 199,"Can't stat %s: %s", name, strerror(errno));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* lock the device on writes */
|
||||
if (locked && lock_dev(This->fd, mode == O_RDWR))
|
||||
{
|
||||
if(errmsg)
|
||||
snprintf(errmsg, 199, "plain floppy: device \"%s\" busy (%s):", dev ? dev->name : "unknown", strerror(errno));
|
||||
close(This->fd);
|
||||
free(This);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* set default parameters, if needed */
|
||||
if (dev)
|
||||
{
|
||||
if (dev->tracks && init_geom(This->fd, dev, orig_dev, &This->statbuf))
|
||||
{
|
||||
close(This->fd);
|
||||
free(This);
|
||||
|
||||
if(errmsg)
|
||||
sprintf(errmsg,"init: set default params");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
This->offset = (off_t) dev->offset;
|
||||
}
|
||||
else
|
||||
This->offset = 0;
|
||||
|
||||
This->refs = 1;
|
||||
This->Next = 0;
|
||||
This->Buffer = 0;
|
||||
|
||||
if(maxSize)
|
||||
{
|
||||
*maxSize = max_off_t_seek;
|
||||
|
||||
if(This->offset > 0 && (size_t) This->offset > *maxSize)
|
||||
{
|
||||
close(This->fd);
|
||||
free(This);
|
||||
|
||||
if(errmsg)
|
||||
sprintf(errmsg,"init: Big disks not supported");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*maxSize -= This->offset;
|
||||
}
|
||||
/* partitioned drive */
|
||||
|
||||
This->swap = 0;
|
||||
|
||||
if(!(mode2 & NO_OFFSET) && dev && dev->partition > 4)
|
||||
fprintf(stderr, "Invalid partition %d (must be between 0 and 4), ignoring it\n", dev->partition);
|
||||
|
||||
while(!(mode2 & NO_OFFSET) && dev && dev->partition && dev->partition <= 4)
|
||||
{
|
||||
unsigned char buf[2048];
|
||||
struct partition *partTable = (struct partition *)(buf+ 0x1ae);
|
||||
size_t partOff;
|
||||
|
||||
/* read the first sector, or part of it */
|
||||
if (force_read((struct Stream_t *)This, (char*) buf, 0, 512) != 512)
|
||||
break;
|
||||
|
||||
if( _WORD(buf + 510) != 0xaa55)
|
||||
break;
|
||||
|
||||
partOff = BEGIN(partTable[dev->partition]);
|
||||
if (maxSize)
|
||||
{
|
||||
if (partOff > *maxSize >> 9)
|
||||
{
|
||||
close(This->fd);
|
||||
free(This);
|
||||
|
||||
if(errmsg)
|
||||
sprintf(errmsg,"init: Big disks not supported");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*maxSize -= (off_t) partOff << 9;
|
||||
}
|
||||
|
||||
This->offset += (off_t) partOff << 9;
|
||||
if(!partTable[dev->partition].sys_ind)
|
||||
{
|
||||
if(errmsg)
|
||||
sprintf(errmsg, "init: non-existent partition");
|
||||
|
||||
close(This->fd);
|
||||
free(This);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!dev->tracks)
|
||||
{
|
||||
dev->heads = head(partTable[dev->partition].end) + 1;
|
||||
dev->sectors = sector(partTable[dev->partition].end);
|
||||
dev->tracks = cyl(partTable[dev->partition].end) - cyl(partTable[dev->partition].start) + 1;
|
||||
}
|
||||
|
||||
dev->hidden = dev->sectors*head(partTable[dev->partition].start) + sector(partTable[dev->partition].start) - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
This->lastwhere = -This->offset;
|
||||
/* provoke a seek on those devices that don't start on a partition
|
||||
* boundary */
|
||||
|
||||
return (struct Stream_t *) This;
|
||||
}
|
||||
|
||||
int SimpleFileClose(struct Stream_t* Stream)
|
||||
{
|
||||
DeclareThis(struct SimpleFile_t);
|
||||
return close(This->fd);
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
/* Copyright 1996,1997,1999,2001,2002,2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MTOOLS_PLAINIO_H
|
||||
#define MTOOLS_PLAINIO_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
struct device;
|
||||
struct Stream_t;
|
||||
|
||||
#define NO_PRIV 1
|
||||
#define NO_OFFSET 2
|
||||
|
||||
struct Stream_t *SimpleFileOpen(struct device *dev, struct device *orig_dev, const char *name, int mode, char *errmsg, int mode2, int locked, size_t *maxSize);
|
||||
int SimpleFileClose(struct Stream_t* Stream);
|
||||
|
||||
#endif
|
|
@ -1,95 +0,0 @@
|
|||
/* Copyright 1996,1997,1999,2001,2002,2008,2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "msdos.h"
|
||||
#include "stream.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
int flush_stream(struct Stream_t *Stream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if(Stream->Class->flush)
|
||||
ret |= Stream->Class->flush(Stream);
|
||||
|
||||
if(Stream->Next)
|
||||
ret |= flush_stream(Stream->Next);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct Stream_t *copy_stream(struct Stream_t *Stream)
|
||||
{
|
||||
if(Stream)
|
||||
Stream->refs++;
|
||||
|
||||
return Stream;
|
||||
}
|
||||
|
||||
int free_stream(struct Stream_t **Stream)
|
||||
{
|
||||
if(!*Stream)
|
||||
return -1;
|
||||
|
||||
int ret = 0;
|
||||
if(! --(*Stream)->refs)
|
||||
{
|
||||
if((*Stream)->Class->flush)
|
||||
ret |= (*Stream)->Class->flush(*Stream);
|
||||
|
||||
if((*Stream)->Class->freeFunc)
|
||||
ret |= (*Stream)->Class->freeFunc(*Stream);
|
||||
|
||||
if((*Stream)->Next)
|
||||
ret |= free_stream(&(*Stream)->Next);
|
||||
|
||||
free(*Stream);
|
||||
}
|
||||
else if ((*Stream)->Next)
|
||||
ret |= flush_stream((*Stream)->Next);
|
||||
|
||||
*Stream = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#define GET_DATA(stream, date, size, type, address) \
|
||||
(stream)->Class->get_data( (stream), (date), (size), (type), (address) )
|
||||
|
||||
|
||||
int get_data_pass_through(struct Stream_t *Stream, time_t *date, size_t *size, int *type, int *address)
|
||||
{
|
||||
return GET_DATA(Stream->Next, date, size, type, address);
|
||||
}
|
||||
|
||||
int read_pass_through(struct Stream_t *Stream, char *buf, off_t start, size_t len)
|
||||
{
|
||||
return READS(Stream->Next, buf, start, len);
|
||||
}
|
||||
|
||||
int write_pass_through(struct Stream_t *Stream, char *buf, off_t start, size_t len)
|
||||
{
|
||||
return WRITES(Stream->Next, buf, start, len);
|
||||
}
|
||||
|
||||
struct doscp_t *get_dosConvert_pass_through(struct Stream_t *Stream)
|
||||
{
|
||||
return GET_DOSCONVERT(Stream->Next);
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
/* Copyright 1996-1999,2001,2002,2005,2006,2008,2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MTOOLS_STREAM_H
|
||||
#define MTOOLS_STREAM_H
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
struct doscp_t;
|
||||
union bootsector;
|
||||
struct device;
|
||||
|
||||
struct Stream_t
|
||||
{
|
||||
struct Class_t *Class;
|
||||
int refs;
|
||||
struct Stream_t *Next;
|
||||
struct Stream_t *Buffer;
|
||||
};
|
||||
|
||||
struct doscp_t *get_dosConvert_pass_through(struct Stream_t *Stream);
|
||||
|
||||
struct Class_t
|
||||
{
|
||||
int (*read)(struct Stream_t *, char *, off_t, size_t);
|
||||
int (*write)(struct Stream_t *, char *, off_t, size_t);
|
||||
int (*flush)(struct Stream_t *);
|
||||
int (*freeFunc)(struct Stream_t *);
|
||||
int (*set_geom)(struct Stream_t *, struct device *, struct device *, int media, union bootsector *);
|
||||
int (*get_data)(struct Stream_t *, time_t *, size_t *, int *, int *);
|
||||
int (*pre_allocate)(struct Stream_t *, size_t);
|
||||
struct doscp_t *(*get_dosConvert)(struct Stream_t *);
|
||||
};
|
||||
|
||||
#define READS(stream, buf, address, size) \
|
||||
((stream)->Class->read)( (stream), (char *) (buf), (address), (size) )
|
||||
|
||||
#define WRITES(stream, buf, address, size) \
|
||||
((stream)->Class->write)( (stream), (char *) (buf), (address), (size) )
|
||||
|
||||
#define SET_GEOM(stream, dev, orig_dev, media, boot) \
|
||||
(stream)->Class->set_geom( (stream), (dev), (orig_dev), (media), (boot) )
|
||||
|
||||
#define GET_DATA(stream, date, size, type, address) \
|
||||
(stream)->Class->get_data( (stream), (date), (size), (type), (address) )
|
||||
|
||||
#define PRE_ALLOCATE(stream, size) \
|
||||
(stream)->Class->pre_allocate((stream), (size))
|
||||
|
||||
#define GET_DOSCONVERT(stream) \
|
||||
(stream)->Class->get_dosConvert((stream))
|
||||
|
||||
int flush_stream(struct Stream_t *Stream);
|
||||
struct Stream_t *copy_stream(struct Stream_t *Stream);
|
||||
int free_stream(struct Stream_t **Stream);
|
||||
|
||||
#define DeclareThis(x) x *This = (x *) Stream
|
||||
|
||||
int get_data_pass_through(struct Stream_t *Stream, time_t *date, size_t *size, int *type, int *address);
|
||||
int read_pass_through(struct Stream_t *Stream, char *buf, off_t start, size_t len);
|
||||
int write_pass_through(struct Stream_t *Stream, char *buf, off_t start, size_t len);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,714 +0,0 @@
|
|||
/* Copyright 1995 David C. Niemi
|
||||
* Copyright 1996-2003,2005,2007-2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* vfat.c
|
||||
*
|
||||
* Miscellaneous VFAT-related functions
|
||||
*/
|
||||
|
||||
#include "msdos.h"
|
||||
#include "mtools.h"
|
||||
#include "vfat.h"
|
||||
#include "file.h"
|
||||
#include "dirCache.h"
|
||||
#include "file_name.h"
|
||||
#include "match.h"
|
||||
#include "directory.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static int unicode_read(struct unicode_char *in, wchar_t *out, int num)
|
||||
{
|
||||
wchar_t *end_out = out + num;
|
||||
|
||||
while(out < end_out)
|
||||
{
|
||||
*out = in->lchar | ((in->uchar) << 8);
|
||||
++out;
|
||||
++in;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
static void clear_vfat(struct vfat_state *v)
|
||||
{
|
||||
v->subentries = 0;
|
||||
v->status = 0;
|
||||
v->present = 0;
|
||||
}
|
||||
|
||||
/* sum_shortname
|
||||
*
|
||||
* Calculate the checksum that results from the short name in *dir.
|
||||
*
|
||||
* The sum is formed by circularly right-shifting the previous sum
|
||||
* and adding in each character, from left to right, padding both
|
||||
* the name and extension to maximum length with spaces and skipping
|
||||
* the "." (hence always summing exactly 11 characters).
|
||||
*
|
||||
* This exact algorithm is required in order to remain compatible
|
||||
* with Microsoft Windows-95 and Microsoft Windows NT 3.5.
|
||||
* Thanks to Jeffrey Richter of Microsoft Systems Journal for
|
||||
* pointing me to the correct algorithm.
|
||||
*
|
||||
* David C. Niemi (niemi@tuxers.net) 95.01.19
|
||||
*/
|
||||
static unsigned char sum_shortname(const struct dos_name_t *dn)
|
||||
{
|
||||
unsigned char sum = 0;
|
||||
const char *name = dn->base;
|
||||
const char *end = name + 11;
|
||||
|
||||
for (sum = 0; name<end; ++name)
|
||||
sum = ((sum & 1) ? 0x80 : 0) + (sum >> 1) + *name;
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
/* check_vfat
|
||||
*
|
||||
* Inspect a directory and any associated VSEs.
|
||||
* Return 1 if the VSEs comprise a valid long file name,
|
||||
* 0 if not.
|
||||
*/
|
||||
static void check_vfat(struct vfat_state *v, struct directory *dir)
|
||||
{
|
||||
struct dos_name_t dn;
|
||||
|
||||
if (! v->subentries)
|
||||
return;
|
||||
|
||||
memcpy(dn.base, (char *)dir->name, 8);
|
||||
memcpy(dn.ext, (char *)dir->ext, 3);
|
||||
|
||||
if (v->sum != sum_shortname(&dn))
|
||||
return;
|
||||
|
||||
if ((v->status & ((1 << v->subentries) - 1)) != (1 << v->subentries) - 1)
|
||||
return; /* missing entries */
|
||||
|
||||
/* zero out byte following last entry, for good measure */
|
||||
v->name[VSE_NAMELEN * v->subentries] = 0;
|
||||
v->present = 1;
|
||||
}
|
||||
|
||||
|
||||
int clear_vses(struct Stream_t *Dir, int entrySlot, size_t last)
|
||||
{
|
||||
struct dirCache_t *cache = allocDirCache(Dir, last);
|
||||
int error;
|
||||
|
||||
if (!cache)
|
||||
return -1; // TODO: check return code
|
||||
|
||||
struct direntry_t entry;
|
||||
entry.Dir = Dir;
|
||||
entry.entry = entrySlot;
|
||||
|
||||
addFreeEntry(cache, entry.entry, last);
|
||||
|
||||
for (; entry.entry < (signed int) last; ++entry.entry)
|
||||
{
|
||||
dir_read(&entry, &error);
|
||||
|
||||
if(error)
|
||||
return error;
|
||||
|
||||
if(!entry.dir.name[0] || entry.dir.name[0] == DELMARK)
|
||||
break;
|
||||
|
||||
entry.dir.name[0] = DELMARK;
|
||||
|
||||
if (entry.dir.attr == 0xf)
|
||||
entry.dir.attr = '\0';
|
||||
|
||||
low_level_dir_write(&entry);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int write_vfat(struct Stream_t *Dir, struct dos_name_t *shortname, char *longname, int start, struct direntry_t *mainEntry)
|
||||
{
|
||||
struct vfat_subentry *vse;
|
||||
int vse_id;
|
||||
int num_vses;
|
||||
wchar_t *c;
|
||||
struct direntry_t entry;
|
||||
struct dirCache_t *cache;
|
||||
wchar_t unixyName[13];
|
||||
struct doscp_t *cp = GET_DOSCONVERT(Dir);
|
||||
wchar_t wlongname[MAX_VNAMELEN+1];
|
||||
int wlen;
|
||||
|
||||
if(longname)
|
||||
{
|
||||
entry.Dir = Dir;
|
||||
vse = (struct vfat_subentry *) &entry.dir;
|
||||
/* Fill in invariant part of vse */
|
||||
vse->attribute = 0x0f;
|
||||
vse->hash1 = vse->sector_l = vse->sector_u = 0;
|
||||
vse->sum = sum_shortname(shortname);
|
||||
|
||||
wlen = native_to_wchar(longname, wlongname, MAX_VNAMELEN + 1, 0, 0);
|
||||
num_vses = (wlen + VSE_NAMELEN - 1) / VSE_NAMELEN;
|
||||
|
||||
for (vse_id = num_vses; vse_id; --vse_id)
|
||||
{
|
||||
int end = 0;
|
||||
|
||||
c = wlongname + (vse_id - 1) * VSE_NAMELEN;
|
||||
|
||||
c += unicode_write(c, vse->text1, VSE1SIZE, &end);
|
||||
c += unicode_write(c, vse->text2, VSE2SIZE, &end);
|
||||
c += unicode_write(c, vse->text3, VSE3SIZE, &end);
|
||||
|
||||
vse->id = (vse_id == num_vses) ? (vse_id | VSE_LAST) : vse_id;
|
||||
|
||||
entry.entry = start + num_vses - vse_id;
|
||||
low_level_dir_write(&entry);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
num_vses = 0;
|
||||
wlongname[0]='\0';
|
||||
}
|
||||
|
||||
cache = allocDirCache(Dir, start + num_vses + 1);
|
||||
|
||||
if(!cache)
|
||||
return -1; // TODO: check return code
|
||||
|
||||
unix_name(cp, shortname->base, shortname->ext, 0, unixyName);
|
||||
addUsedEntry(cache, start, start + num_vses + 1, wlongname, unixyName, &mainEntry->dir);
|
||||
low_level_dir_write(mainEntry);
|
||||
|
||||
return start + num_vses;
|
||||
}
|
||||
|
||||
int dir_write(struct direntry_t *entry)
|
||||
{
|
||||
if (entry->entry == -3)
|
||||
{
|
||||
fprintf(stderr, "Attempt to write root directory pointer\n");
|
||||
return -2; // TODO: make caller check return code
|
||||
}
|
||||
|
||||
struct dirCache_t *cache = allocDirCache(entry->Dir, entry->entry + 1);
|
||||
|
||||
if (!cache)
|
||||
{
|
||||
fprintf(stderr, "Out of memory error in dir_write\n");
|
||||
return -1; // TODO: make caller check return code
|
||||
}
|
||||
|
||||
struct dirCacheEntry_t *dce = cache->entries[entry->entry];
|
||||
|
||||
if (dce)
|
||||
{
|
||||
if(entry->dir.name[0] == DELMARK)
|
||||
addFreeEntry(cache, dce->beginSlot, dce->endSlot);
|
||||
else
|
||||
dce->dir = entry->dir;
|
||||
}
|
||||
|
||||
low_level_dir_write(entry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The following function translates a series of vfat_subentries into
|
||||
* data suitable for a dircache entry
|
||||
*/
|
||||
static void parse_vses(struct direntry_t *entry, struct vfat_state *v)
|
||||
{
|
||||
struct vfat_subentry *vse = (struct vfat_subentry *) &entry->dir;
|
||||
unsigned char id = vse->id & VSE_MASK;
|
||||
unsigned char last_flag = (vse->id & VSE_LAST);
|
||||
|
||||
if (id > MAX_VFAT_SUBENTRIES)
|
||||
{
|
||||
fprintf(stderr, "parse_vses: invalid VSE ID %d at %d.\n", id, entry->entry);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 950819: This code enforced finding the VSEs in order. Well, Win95
|
||||
* likes to write them in *reverse* order for some bizarre reason! So
|
||||
* we pretty much have to tolerate them coming in any possible order.
|
||||
* So skip this check, we'll do without it (What does this do, Alain?).
|
||||
*
|
||||
* 950820: Totally rearranged code to tolerate any order but to warn if
|
||||
* they are not in reverse order like Win95 uses.
|
||||
*
|
||||
* 950909: Tolerate any order. We recognize new chains by mismatching
|
||||
* checksums. In the event that the checksums match, new entries silently
|
||||
* overwrite old entries of the same id. This should accept all valid
|
||||
* entries, but may fail to reject invalid entries in some rare cases.
|
||||
*/
|
||||
|
||||
/* bad checksum, begin new chain */
|
||||
if(v->sum != vse->sum)
|
||||
{
|
||||
clear_vfat(v);
|
||||
v->sum = vse->sum;
|
||||
}
|
||||
|
||||
v->status |= 1 << (id-1);
|
||||
|
||||
if(last_flag)
|
||||
v->subentries = id;
|
||||
|
||||
wchar_t* c = &(v->name[VSE_NAMELEN * (id-1)]);
|
||||
c += unicode_read(vse->text1, c, VSE1SIZE);
|
||||
c += unicode_read(vse->text2, c, VSE2SIZE);
|
||||
c += unicode_read(vse->text3, c, VSE3SIZE);
|
||||
|
||||
if (last_flag)
|
||||
*c = '\0'; /* Null terminate long name */
|
||||
}
|
||||
|
||||
|
||||
static struct dirCacheEntry_t *vfat_lookup_loop_common(struct doscp_t *cp,
|
||||
struct direntry_t *direntry,
|
||||
struct dirCache_t *cache,
|
||||
int lookForFreeSpace,
|
||||
int *io_error)
|
||||
{
|
||||
int initpos = direntry->entry + 1;
|
||||
struct vfat_state vfat;
|
||||
|
||||
/* not yet cached */
|
||||
*io_error = 0;
|
||||
clear_vfat(&vfat);
|
||||
|
||||
while(1)
|
||||
{
|
||||
++direntry->entry;
|
||||
|
||||
int error;
|
||||
if(!dir_read(direntry, &error))
|
||||
{
|
||||
if(error)
|
||||
{
|
||||
*io_error = error;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
addFreeEntry(cache, initpos, direntry->entry);
|
||||
|
||||
return addEndEntry(cache, direntry->entry);
|
||||
}
|
||||
|
||||
if (direntry->dir.name[0] == '\0')
|
||||
{
|
||||
/* the end of the directory */
|
||||
if(lookForFreeSpace)
|
||||
continue;
|
||||
|
||||
return addEndEntry(cache, direntry->entry);
|
||||
}
|
||||
|
||||
if(direntry->dir.name[0] != DELMARK && direntry->dir.attr == 0x0f)
|
||||
parse_vses(direntry, &vfat);
|
||||
else
|
||||
/* the main entry */
|
||||
break;
|
||||
}
|
||||
|
||||
/* If we get here, it's a short name FAT entry, maybe erased.
|
||||
* thus we should make sure that the vfat structure will be
|
||||
* cleared before the next loop run */
|
||||
|
||||
/* deleted file */
|
||||
if (direntry->dir.name[0] == DELMARK)
|
||||
return addFreeEntry(cache, initpos, direntry->entry + 1);
|
||||
|
||||
check_vfat(&vfat, &direntry->dir);
|
||||
|
||||
if(!vfat.present)
|
||||
vfat.subentries = 0;
|
||||
|
||||
/* mark space between last entry and this one as free */
|
||||
addFreeEntry(cache, initpos, direntry->entry - vfat.subentries);
|
||||
|
||||
wchar_t newfile[13];
|
||||
if (direntry->dir.attr & 0x8)
|
||||
{
|
||||
/* Read entry as a label */
|
||||
wchar_t *ptr = newfile;
|
||||
ptr += dos_to_wchar(cp, direntry->dir.name, ptr, 8);
|
||||
ptr += dos_to_wchar(cp, direntry->dir.ext, ptr, 3);
|
||||
*ptr = '\0';
|
||||
}
|
||||
else
|
||||
unix_name(cp, direntry->dir.name, direntry->dir.ext, direntry->dir.Case, newfile);
|
||||
|
||||
wchar_t* longname = vfat.present ? vfat.name : 0;
|
||||
|
||||
return addUsedEntry(cache, direntry->entry - vfat.subentries, direntry->entry + 1, longname, newfile, &direntry->dir);
|
||||
}
|
||||
|
||||
static struct dirCacheEntry_t *vfat_lookup_loop_for_read(struct doscp_t *cp, struct direntry_t *direntry, struct dirCache_t *cache, int *io_error)
|
||||
{
|
||||
*io_error = 0;
|
||||
struct dirCacheEntry_t *dce = cache->entries[direntry->entry + 1];
|
||||
|
||||
if(dce)
|
||||
{
|
||||
direntry->entry = dce->endSlot - 1;
|
||||
return dce;
|
||||
}
|
||||
else
|
||||
return vfat_lookup_loop_common(cp, direntry, cache, 0, io_error);
|
||||
}
|
||||
|
||||
|
||||
typedef enum result_t {
|
||||
RES_NOMATCH,
|
||||
RES_MATCH,
|
||||
RES_END,
|
||||
RES_ERROR
|
||||
} result_t;
|
||||
|
||||
|
||||
/*
|
||||
* 0 does not match
|
||||
* 1 matches
|
||||
* 2 end
|
||||
*/
|
||||
static result_t checkNameForMatch(struct direntry_t *direntry,
|
||||
struct dirCacheEntry_t *dce,
|
||||
const wchar_t *filename,
|
||||
int length,
|
||||
int flags)
|
||||
{
|
||||
switch(dce->type)
|
||||
{
|
||||
case DCET_FREE:
|
||||
return RES_NOMATCH;
|
||||
|
||||
case DCET_END:
|
||||
return RES_END;
|
||||
|
||||
case DCET_USED:
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Unexpected entry type %d\n", dce->type);
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
direntry->dir = dce->dir;
|
||||
|
||||
/* make sure the entry is of an accepted type */
|
||||
if ((direntry->dir.attr & 0x8) && !(flags & ACCEPT_LABEL))
|
||||
return RES_NOMATCH;
|
||||
|
||||
/*---------- multiple files ----------*/
|
||||
if (!((flags & MATCH_ANY) ||
|
||||
(dce->longName &&
|
||||
match(dce->longName, filename, direntry->name, 0, length)) ||
|
||||
match(dce->shortName, filename, direntry->name, 1, length)))
|
||||
return RES_NOMATCH;
|
||||
|
||||
/* entry of non-requested type, has to come after name
|
||||
* checking because of clash handling */
|
||||
if(IS_DIR(direntry) && !(flags & ACCEPT_DIR)) {
|
||||
if(!(flags & (ACCEPT_LABEL|MATCH_ANY|NO_MSG))) {
|
||||
char tmp[4*13+1];
|
||||
wchar_to_native(dce->shortName,tmp,13);
|
||||
fprintf(stderr, "Skipping \"%s\", is a directory\n",
|
||||
tmp);
|
||||
}
|
||||
return RES_NOMATCH;
|
||||
}
|
||||
|
||||
if (!(direntry->dir.attr & (ATTR_LABEL | ATTR_DIR)) && !(flags & ACCEPT_PLAIN))
|
||||
{
|
||||
if (!(flags & (ACCEPT_LABEL|MATCH_ANY|NO_MSG)))
|
||||
{
|
||||
char tmp[4*13+1];
|
||||
wchar_to_native(dce->shortName,tmp,13);
|
||||
fprintf(stderr, "Skipping \"%s\", is not a directory\n", tmp);
|
||||
}
|
||||
|
||||
return RES_NOMATCH;
|
||||
}
|
||||
|
||||
return RES_MATCH;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* vfat_lookup looks for filenames in directory dir.
|
||||
* if a name if found, it is returned in outname
|
||||
* if applicable, the file is opened and its stream is returned in File
|
||||
*/
|
||||
|
||||
int vfat_lookup(struct direntry_t *direntry, const char *filename, int length, int flags, char *shortname, char *longname)
|
||||
{
|
||||
wchar_t wfilename[MAX_VNAMELEN+1];
|
||||
wchar_t *wfilenamep = wfilename;
|
||||
|
||||
if(length == -1 && filename)
|
||||
length = strlen(filename);
|
||||
|
||||
if(filename != NULL)
|
||||
length = native_to_wchar(filename, wfilename, MAX_VNAMELEN, filename+length, 0);
|
||||
else
|
||||
{
|
||||
wfilenamep = NULL;
|
||||
length = 0;
|
||||
}
|
||||
|
||||
if (direntry->entry == -2)
|
||||
return -1;
|
||||
|
||||
struct dirCache_t *cache = allocDirCache(direntry->Dir, direntry->entry+1);
|
||||
if(!cache)
|
||||
{
|
||||
fprintf(stderr, "Out of memory error in vfat_lookup [0]\n");
|
||||
return -2; // TODO: make caller check return code
|
||||
}
|
||||
|
||||
struct dirCacheEntry_t *dce = NULL;
|
||||
result_t result;
|
||||
struct doscp_t *cp = GET_DOSCONVERT(direntry->Dir);
|
||||
do
|
||||
{
|
||||
int io_error;
|
||||
dce = vfat_lookup_loop_for_read(cp, direntry, cache, &io_error);
|
||||
|
||||
if(!dce)
|
||||
{
|
||||
if (io_error)
|
||||
return -2;
|
||||
|
||||
fprintf(stderr, "Out of memory error in vfat_lookup\n");
|
||||
|
||||
return -2;
|
||||
}
|
||||
|
||||
result = checkNameForMatch(direntry, dce, wfilename, length, flags);
|
||||
} while(result == RES_NOMATCH);
|
||||
|
||||
if(result == RES_MATCH)
|
||||
{
|
||||
if(longname)
|
||||
{
|
||||
if(dce->longName)
|
||||
wchar_to_native(dce->longName, longname, MAX_VNAMELEN);
|
||||
else
|
||||
*longname ='\0';
|
||||
}
|
||||
|
||||
if(shortname)
|
||||
wchar_to_native(dce->shortName, shortname, 12);
|
||||
|
||||
direntry->beginSlot = dce->beginSlot;
|
||||
direntry->endSlot = dce->endSlot-1;
|
||||
return 0; /* file found */
|
||||
}
|
||||
|
||||
direntry->entry = -2;
|
||||
return -1; /* no file found */
|
||||
}
|
||||
|
||||
static struct dirCacheEntry_t *vfat_lookup_loop_for_insert(struct doscp_t *cp,
|
||||
struct direntry_t *direntry,
|
||||
int initpos,
|
||||
struct dirCache_t *cache)
|
||||
{
|
||||
struct dirCacheEntry_t *dce = cache->entries[initpos];
|
||||
int io_error;
|
||||
|
||||
if(dce && dce->type != DCET_END)
|
||||
return dce;
|
||||
|
||||
direntry->entry = initpos - 1;
|
||||
|
||||
dce = vfat_lookup_loop_common(cp, direntry, cache, 1, &io_error);
|
||||
|
||||
if(!dce)
|
||||
{
|
||||
if (io_error)
|
||||
return NULL;
|
||||
|
||||
fprintf(stderr, "Out of memory error in vfat_lookup_loop\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return cache->entries[initpos];
|
||||
}
|
||||
|
||||
static void accountFreeSlots(struct scan_state *ssp, struct dirCacheEntry_t *dce)
|
||||
{
|
||||
if(ssp->got_slots)
|
||||
return;
|
||||
|
||||
if(ssp->free_end != dce->beginSlot)
|
||||
ssp->free_start = dce->beginSlot;
|
||||
|
||||
ssp->free_end = dce->endSlot;
|
||||
|
||||
if(ssp->free_end - ssp->free_start >= ssp->size_needed)
|
||||
{
|
||||
ssp->got_slots = 1;
|
||||
ssp->slot = ssp->free_start + ssp->size_needed - 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void clear_scan(wchar_t *longname, int use_longname, struct scan_state *s)
|
||||
{
|
||||
s->shortmatch = s->longmatch = s->slot = -1;
|
||||
s->free_end = s->got_slots = s->free_start = 0;
|
||||
|
||||
if (use_longname)
|
||||
s->size_needed = 1 + (wcslen(longname) + VSE_NAMELEN - 1) / VSE_NAMELEN;
|
||||
else
|
||||
s->size_needed = 1;
|
||||
}
|
||||
|
||||
/* lookup_for_insert replaces the old scandir function. It directly
|
||||
* calls into vfat_lookup_loop, thus eliminating the overhead of the
|
||||
* normal vfat_lookup
|
||||
*/
|
||||
int lookupForInsert(struct Stream_t *Dir,
|
||||
struct direntry_t *direntry,
|
||||
struct dos_name_t *dosname,
|
||||
char *longname,
|
||||
struct scan_state *ssp,
|
||||
int ignore_entry,
|
||||
int source_entry,
|
||||
int pessimisticShortRename,
|
||||
int use_longname)
|
||||
{
|
||||
wchar_t shortName[13];
|
||||
wchar_t wlongname[MAX_VNAMELEN+1];
|
||||
struct doscp_t *cp = GET_DOSCONVERT(Dir);
|
||||
|
||||
native_to_wchar(longname, wlongname, MAX_VNAMELEN+1, 0, 0);
|
||||
clear_scan(wlongname, use_longname, ssp);
|
||||
|
||||
int ignore_match = ignore_entry == -2;
|
||||
|
||||
struct direntry_t entry;
|
||||
initializeDirentry(&entry, Dir);
|
||||
ssp->match_free = 0;
|
||||
|
||||
/* hash bitmap of already encountered names. Speeds up batch appends
|
||||
* to huge directories, because in the best case, we only need to scan
|
||||
* the new entries rather than the whole directory */
|
||||
struct dirCache_t *cache = allocDirCache(Dir, 1);
|
||||
if(!cache)
|
||||
{
|
||||
fprintf(stderr, "Out of memory error in lookupForInsert\n");
|
||||
return -1; // TODO: make caller check return code?
|
||||
}
|
||||
|
||||
if(!ignore_match)
|
||||
unix_name(cp, dosname->base, dosname->ext, 0, shortName);
|
||||
|
||||
/* position _before_ the next answered entry */
|
||||
int pos = cache->nrHashed;
|
||||
|
||||
if (source_entry >= 0 || (pos && isHashed(cache, wlongname)))
|
||||
pos = 0;
|
||||
else if(pos && !ignore_match && isHashed(cache, shortName))
|
||||
{
|
||||
if (pessimisticShortRename)
|
||||
{
|
||||
ssp->shortmatch = -2;
|
||||
return 1;
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
}
|
||||
else if(growDirCache(cache, pos) < 0)
|
||||
{
|
||||
fprintf(stderr, "Out of memory error in vfat_looup [0]\n");
|
||||
return -1; // TODO: make caller check return code?
|
||||
}
|
||||
|
||||
struct dirCacheEntry_t *dce;
|
||||
do
|
||||
{
|
||||
dce = vfat_lookup_loop_for_insert(cp, &entry, pos, cache);
|
||||
|
||||
switch(dce->type)
|
||||
{
|
||||
case DCET_FREE:
|
||||
accountFreeSlots(ssp, dce);
|
||||
break;
|
||||
|
||||
case DCET_USED:
|
||||
if(!(dce->dir.attr & 0x8) && (signed int)dce->endSlot-1 == source_entry)
|
||||
accountFreeSlots(ssp, dce);
|
||||
|
||||
/* labels never match, neither does the
|
||||
* ignored entry */
|
||||
if( (dce->dir.attr & 0x8) || ((signed int)dce->endSlot-1==ignore_entry))
|
||||
break;
|
||||
|
||||
/* check long name */
|
||||
if((dce->longName && !wcscasecmp(dce->longName, wlongname)) || (dce->shortName && !wcscasecmp(dce->shortName, wlongname)))
|
||||
{
|
||||
ssp->longmatch = dce->endSlot - 1;
|
||||
/* long match is a reason for immediate stop */
|
||||
direntry->beginSlot = dce->beginSlot;
|
||||
direntry->endSlot = dce->endSlot-1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Long name or not, always check for short name match */
|
||||
if (!ignore_match && !wcscasecmp(shortName, dce->shortName))
|
||||
ssp->shortmatch = dce->endSlot - 1;
|
||||
break;
|
||||
|
||||
case DCET_END:
|
||||
break;
|
||||
}
|
||||
|
||||
pos = dce->endSlot;
|
||||
|
||||
} while(dce->type != DCET_END);
|
||||
|
||||
if (ssp->shortmatch > -1)
|
||||
return 1;
|
||||
|
||||
ssp->max_entry = dce->beginSlot;
|
||||
|
||||
if (ssp->got_slots)
|
||||
return 6; /* Success */
|
||||
|
||||
/* Need more room. Can we grow the directory? */
|
||||
if(!isRootDir(Dir))
|
||||
return 5; /* OK, try to grow the directory */
|
||||
|
||||
fprintf(stderr, "No directory slots\n");
|
||||
|
||||
return -1;
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
/* Copyright 1995 David C. Niemi
|
||||
* Copyright 1996-1998,2000-2003,2005,2007-2009 Alain Knaff.
|
||||
* Copyright 2010 Volker Lanz (vl@fidra.de)
|
||||
* This file is part of mtools.
|
||||
*
|
||||
* Mtools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mtools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MTOOLS_VFAT_H
|
||||
#define MTOOLS_VFAT_H
|
||||
|
||||
#include <wchar.h>
|
||||
|
||||
struct dos_name_t;
|
||||
struct direntry_t;
|
||||
|
||||
struct unicode_char
|
||||
{
|
||||
unsigned char lchar;
|
||||
unsigned char uchar;
|
||||
};
|
||||
|
||||
#define MAX_VFAT_SUBENTRIES 20 /* Max useful # of VSEs */
|
||||
#define VSE_NAMELEN 13
|
||||
|
||||
#define VSE1SIZE 5
|
||||
#define VSE2SIZE 6
|
||||
#define VSE3SIZE 2
|
||||
|
||||
#include "stream.h"
|
||||
|
||||
struct vfat_subentry
|
||||
{
|
||||
unsigned char id; /* 0x40 = last; & 0x1f = VSE ID */
|
||||
struct unicode_char text1[VSE1SIZE];
|
||||
unsigned char attribute; /* 0x0f for VFAT */
|
||||
unsigned char hash1; /* Always 0? */
|
||||
unsigned char sum; /* Checksum of short name */
|
||||
struct unicode_char text2[VSE2SIZE];
|
||||
unsigned char sector_l; /* 0 for VFAT */
|
||||
unsigned char sector_u; /* 0 for VFAT */
|
||||
struct unicode_char text3[VSE3SIZE];
|
||||
};
|
||||
|
||||
/* Enough size for a worst case number of full VSEs plus a null */
|
||||
#define VBUFSIZE ((MAX_VFAT_SUBENTRIES*VSE_NAMELEN) + 1)
|
||||
|
||||
/* Max legal length of a VFAT long name */
|
||||
#define MAX_VNAMELEN 255
|
||||
|
||||
#define VSE_PRESENT 0x01
|
||||
#define VSE_LAST 0x40
|
||||
#define VSE_MASK 0x1f
|
||||
|
||||
struct vfat_state
|
||||
{
|
||||
wchar_t name[VBUFSIZE];
|
||||
int status; /* is now a bit map of 32 bits */
|
||||
int subentries;
|
||||
unsigned char sum; /* no need to remember the sum for each entry, it is the same anyways */
|
||||
int present;
|
||||
};
|
||||
|
||||
struct scan_state
|
||||
{
|
||||
int match_free;
|
||||
int shortmatch;
|
||||
int longmatch;
|
||||
unsigned int free_start;
|
||||
unsigned int free_end;
|
||||
int slot;
|
||||
int got_slots;
|
||||
unsigned int size_needed;
|
||||
int max_entry;
|
||||
};
|
||||
|
||||
int unicode_write(wchar_t *, struct unicode_char *, int num, int *end);
|
||||
int clear_vses(struct Stream_t *, int, size_t);
|
||||
void autorename_short(struct dos_name_t *, int);
|
||||
void autorename_long(char *, int);
|
||||
int vfat_lookup(struct direntry_t *entry, const char *filename, int length, int flags, char *shortname, char *longname);
|
||||
int dir_write(struct direntry_t *entry);
|
||||
int write_vfat(struct Stream_t *, struct dos_name_t *, char *, int, struct direntry_t *);
|
||||
|
||||
#define DO_OPEN 1 /* open all files that are found */
|
||||
#define ACCEPT_LABEL 0x08
|
||||
#define ACCEPT_DIR 0x10
|
||||
#define ACCEPT_PLAIN 0x20
|
||||
#define MATCH_ANY 0x40
|
||||
#define NO_MSG 0x80
|
||||
#define NO_DOTS 0x100 /* accept no dots if matched by wildcard */
|
||||
|
||||
#endif
|
|
@ -193,7 +193,6 @@ target_link_libraries(partitionmanagerprivate
|
|||
${KDE4_SOLID_LIBS}
|
||||
${UUID_LIBRARIES}
|
||||
${BLKID_LIBRARIES}
|
||||
libfatlabel
|
||||
)
|
||||
|
||||
if(LIBATASMART_FOUND)
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#include "fs/filesystem.h"
|
||||
|
||||
#include <qglobal.h>
|
||||
#include <fatlabel/partition.h>
|
||||
|
||||
class CoreBackendPartition;
|
||||
class Report;
|
||||
|
|
|
@ -23,8 +23,6 @@
|
|||
#include "util/capacity.h"
|
||||
#include "util/report.h"
|
||||
|
||||
#include "fatlabel/fatlabel.h"
|
||||
|
||||
#include <kdebug.h>
|
||||
#include <klocale.h>
|
||||
|
||||
|
@ -59,7 +57,7 @@ namespace FS
|
|||
m_Create = findExternal("mkfs.msdos") ? cmdSupportFileSystem : cmdSupportNone;
|
||||
m_GetUsed = m_Check = findExternal("fsck.msdos", QStringList(), 2) ? cmdSupportFileSystem : cmdSupportNone;
|
||||
m_GetLabel = cmdSupportCore;
|
||||
m_SetLabel = cmdSupportFileSystem;
|
||||
m_SetLabel = findExternal("fatlabel") ? cmdSupportFileSystem : cmdSupportNone;;
|
||||
m_Move = cmdSupportCore;
|
||||
m_Copy = cmdSupportCore;
|
||||
m_Backup = cmdSupportCore;
|
||||
|
@ -134,9 +132,10 @@ namespace FS
|
|||
|
||||
bool fat16::writeLabel(Report& report, const QString& deviceNode, const QString& newLabel)
|
||||
{
|
||||
report.line() << i18nc("@info/plain", "Setting label for partition <filename>%1</filename> to %2", deviceNode, newLabel);
|
||||
report.line() << i18nc("@info/plain", "Setting label for partition <filename>%1</filename> to %2", deviceNode, newLabel.toUpper());
|
||||
|
||||
return fatlabel_set_label(deviceNode.toLocal8Bit(), newLabel.toLocal8Bit()) == 0;
|
||||
ExternalCommand cmd(report, "fatlabel", QStringList() << deviceNode << newLabel.toUpper());
|
||||
return cmd.run(-1) && cmd.exitCode() == 0;
|
||||
}
|
||||
|
||||
bool fat16::check(Report& report, const QString& deviceNode) const
|
||||
|
|
Loading…
Add table
Reference in a new issue