/* * Copyright (C) 2002-2015 The DOSBox Team * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Wengier: LFN support */ #include #include #include #include #include #ifdef VITA #include #include #endif #include "dosbox.h" #include "dos_inc.h" #include "drives.h" #include "support.h" #include "cross.h" #include "inout.h" class localFile : public DOS_File { public: localFile(const char* name, FILE * handle); bool Read(Bit8u * data,Bit16u * size); bool Write(Bit8u * data,Bit16u * size); bool Seek(Bit32u * pos,Bit32u type); bool Close(); Bit16u GetInformation(void); bool UpdateDateTimeFromHost(void); void FlagReadOnlyMedium(void); void Flush(void); private: FILE * fhandle; bool read_only_medium; enum { NONE,READ,WRITE } last_action; }; bool localDrive::FileCreate(DOS_File * * file,char * name,Bit16u /*attributes*/) { //TODO Maybe care for attributes but not likely char newname[CROSS_LEN]; strcpy(newname,basedir); strcat(newname,name); CROSS_FILENAME(newname); char* temp_name = dirCache.GetExpandName(newname); //Can only be used in till a new drive_cache action is preformed */ /* Test if file exists (so we need to truncate it). don't add to dirCache then */ bool existing_file=false; FILE * test=fopen(temp_name,"rb+"); if(test) { fclose(test); existing_file=true; } FILE * hand=fopen(temp_name,"wb+"); if (!hand){ LOG_MSG("Warning: file creation failed: %s",newname); return false; } if(!existing_file) dirCache.AddEntry(newname, true); /* Make the 16 bit device information */ *file=new localFile(name,hand); (*file)->flags=OPEN_READWRITE; return true; } bool localDrive::FileOpen(DOS_File * * file,char * name,Bit32u flags) { const char* type; switch (flags&0xf) { case OPEN_READ: type = "rb" ; break; case OPEN_WRITE: type = "rb+"; break; case OPEN_READWRITE: type = "rb+"; break; case OPEN_READ_NO_MOD: type = "rb" ; break; //No modification of dates. LORD4.07 uses this default: DOS_SetError(DOSERR_ACCESS_CODE_INVALID); return false; } char newname[CROSS_LEN]; strcpy(newname,basedir); strcat(newname,name); CROSS_FILENAME(newname); dirCache.ExpandName(newname); //Flush the buffer of handles for the same file. (Betrayal in Antara) Bit8u i,drive=DOS_DRIVES; localFile *lfp; for (i=0;iIsOpen() && Files[i]->GetDrive()==drive && Files[i]->IsName(name)) { lfp=dynamic_cast(Files[i]); if (lfp) lfp->Flush(); } } FILE * hand=fopen(newname,type); // Bit32u err=errno; if (!hand) { if((flags&0xf) != OPEN_READ) { FILE * hmm=fopen(newname,"rb"); if (hmm) { fclose(hmm); LOG_MSG("Warning: file %s exists and failed to open in write mode.\nPlease Remove write-protection",newname); } } return false; } *file=new localFile(name,hand); (*file)->flags=flags; //for the inheritance flag and maybe check for others. // (*file)->SetFileName(newname); return true; } FILE * localDrive::GetSystemFilePtr(char const * const name, char const * const type) { char newname[CROSS_LEN]; strcpy(newname,basedir); strcat(newname,name); CROSS_FILENAME(newname); dirCache.ExpandName(newname); return fopen(newname,type); } bool localDrive::GetSystemFilename(char *sysName, char const * const dosName) { strcpy(sysName, basedir); strcat(sysName, dosName); CROSS_FILENAME(sysName); dirCache.ExpandName(sysName); return true; } bool localDrive::FileUnlink(char * name) { char newname[CROSS_LEN]; strcpy(newname,basedir); strcat(newname,name); CROSS_FILENAME(newname); char *fullname = dirCache.GetExpandName(newname); if (unlink(fullname)) { //Unlink failed for some reason try finding it. struct stat buffer; if(stat(fullname,&buffer)) return false; // File not found. FILE* file_writable = fopen(fullname,"rb+"); if(!file_writable) return false; //No acces ? ERROR MESSAGE NOT SET. FIXME ? fclose(file_writable); //File exists and can technically be deleted, nevertheless it failed. //This means that the file is probably open by some process. //See if We have it open. bool found_file = false; for(Bitu i = 0;i < DOS_FILES;i++){ if(Files[i] && Files[i]->IsName(name)) { Bitu max = DOS_FILES; while(Files[i]->IsOpen() && max--) { Files[i]->Close(); if (Files[i]->RemoveRef()<=0) break; } found_file=true; } } if(!found_file) return false; if (!unlink(fullname)) { dirCache.DeleteEntry(newname); return true; } return false; } else { dirCache.DeleteEntry(newname); return true; } } bool localDrive::FindFirst(char * _dir,DOS_DTA & dta,bool fcb_findfirst) { char tempDir[CROSS_LEN]; strcpy(tempDir,basedir); strcat(tempDir,_dir); CROSS_FILENAME(tempDir); for (unsigned int i=0;iisRemote() && this->isRemovable()) { // cdroms behave a bit different than regular drives if (sAttr == DOS_ATTR_VOLUME) { dta.SetResult(dirCache.GetLabel(),dirCache.GetLabel(),0,0,0,DOS_ATTR_VOLUME); return true; } } else { if (sAttr == DOS_ATTR_VOLUME) { if ( strcmp(dirCache.GetLabel(), "") == 0 ) { // LOG(LOG_DOSMISC,LOG_ERROR)("DRIVELABEL REQUESTED: none present, returned NOLABEL"); // dta.SetResult("NO_LABEL",0,0,0,DOS_ATTR_VOLUME); // return true; DOS_SetError(DOSERR_NO_MORE_FILES); return false; } dta.SetResult(dirCache.GetLabel(),dirCache.GetLabel(),0,0,0,DOS_ATTR_VOLUME); return true; } else if ((sAttr & DOS_ATTR_VOLUME) && (*_dir == 0) && !fcb_findfirst) { //should check for a valid leading directory instead of 0 //exists==true if the volume label matches the searchmask and the path is valid if (WildFileCmp(dirCache.GetLabel(),tempDir)) { dta.SetResult(dirCache.GetLabel(),dirCache.GetLabel(),0,0,0,DOS_ATTR_VOLUME); return true; } } } return FindNext(dta); } bool localDrive::FindNext(DOS_DTA & dta) { char *dir_ent, *ldir_ent; struct stat stat_block; char full_name[CROSS_LEN]; char dir_entcopy[CROSS_LEN], ldir_entcopy[CROSS_LEN]; Bit8u srch_attr;char srch_pattern[LFN_NAMELENGTH+1]; Bit8u find_attr; dta.GetSearchParams(srch_attr,srch_pattern,true); Bit16u id = dta.GetDirID(); again: if (!dirCache.FindNext(id,dir_ent,ldir_ent)) { DOS_SetError(DOSERR_NO_MORE_FILES); return false; } if(!WildFileCmp(dir_ent,srch_pattern)&&!LWildFileCmp(ldir_ent,srch_pattern)) goto again; strcpy(full_name,srchInfo[id].srch_dir); strcat(full_name,dir_ent); //GetExpandName might indirectly destroy dir_ent (by caching in a new directory //and due to its design dir_ent might be lost.) //Copying dir_ent first strcpy(dir_entcopy,dir_ent); strcpy(ldir_entcopy,ldir_ent); if (stat(dirCache.GetExpandName(full_name),&stat_block)!=0) { goto again;//No symlinks and such } if(stat_block.st_mode & S_IFDIR) find_attr=DOS_ATTR_DIRECTORY; else find_attr=DOS_ATTR_ARCHIVE; if (~srch_attr & find_attr & (DOS_ATTR_DIRECTORY | DOS_ATTR_HIDDEN | DOS_ATTR_SYSTEM)) goto again; /*file is okay, setup everything to be copied in DTA Block */ char find_name[DOS_NAMELENGTH_ASCII], *lfind_name=ldir_ent; Bit16u find_date,find_time;Bit32u find_size; if(strlen(dir_entcopy)tm_year+1900),(Bit16u)(time->tm_mon+1),(Bit16u)time->tm_mday); find_time=DOS_PackTime((Bit16u)time->tm_hour,(Bit16u)time->tm_min,(Bit16u)time->tm_sec); } else { find_time=6; find_date=4; } dta.SetResult(find_name,lfind_name,find_size,find_date,find_time,find_attr); return true; } bool localDrive::GetFileAttr(char * name,Bit16u * attr) { char newname[CROSS_LEN]; strcpy(newname,basedir); strcat(newname,name); CROSS_FILENAME(newname); dirCache.ExpandName(newname); struct stat status; if (stat(newname,&status)==0) { *attr=DOS_ATTR_ARCHIVE; if(status.st_mode & S_IFDIR) *attr|=DOS_ATTR_DIRECTORY; return true; } *attr=0; return false; } bool localDrive::GetFileAttrEx(char* name, struct stat *status) { char newname[CROSS_LEN]; strcpy(newname,basedir); strcat(newname,name); CROSS_FILENAME(newname); dirCache.ExpandName(newname); return !stat(newname,status); } Bit32u localDrive::GetCompressedSize(char* name) { //win32 is not special return 0; /* #if !defined (WIN32) return 0; #else char newname[CROSS_LEN]; strcpy(newname,basedir); strcat(newname,name); CROSS_FILENAME(newname); dirCache.ExpandName(newname); Bit32u size = GetCompressedFileSize(newname, NULL); if (size != INVALID_FILE_SIZE) { if (size != 0 && size == GetFileSize(newname, NULL)) { Bit32u sectors_per_cluster, bytes_per_sector, free_clusters, total_clusters; if (GetDiskFreeSpace(newname, §ors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters)) { size = ((size - 1) | (sectors_per_cluster * bytes_per_sector - 1)) + 1; } } return size; } else { DOS_SetError((Bit16u)GetLastError()); return -1; } #endif */ } void* localDrive::CreateOpenFile(const char* name) { char newname[CROSS_LEN]; strcpy(newname,basedir); strcat(newname,name); CROSS_FILENAME(newname); dirCache.ExpandName(newname); //win32 is not special return NULL; /* #if defined (WIN32) HANDLE handle=CreateFile(newname, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (handle==INVALID_HANDLE_VALUE) DOS_SetError((Bit16u)GetLastError()); return handle; #else return NULL; #endif */ } bool localDrive::MakeDir(char * dir) { char newdir[CROSS_LEN]; strcpy(newdir,basedir); strcat(newdir,dir); CROSS_FILENAME(newdir); #if defined (WIN32) /* MS Visual C++ */ int temp=mkdir(dirCache.GetExpandName(newdir)); #elif defined(VITA) int temp=sceIoMkdir(dirCache.GetExpandName(newdir), 0777); #else int temp=mkdir(dirCache.GetExpandName(newdir),0700); #endif if (temp==0) dirCache.CacheOut(newdir,true); return (temp==0);// || ((temp!=0) && (errno==EEXIST)); } bool localDrive::RemoveDir(char * dir) { char newdir[CROSS_LEN]; strcpy(newdir,basedir); strcat(newdir,dir); CROSS_FILENAME(newdir); #if defined (VITA) int temp=sceIoRmdir(dirCache.GetExpandName(newdir)); #else int temp=rmdir(dirCache.GetExpandName(newdir)); #endif if (temp==0) dirCache.DeleteEntry(newdir,true); return (temp==0); } #ifdef WII int access (const char *file, int type) { struct stat stbuf; gid_t gid; uid_t uid; if (file == NULL || (type & ~(R_OK|W_OK|X_OK|F_OK)) != 0) { errno = EINVAL; return -1; } if(stat(file, &stbuf) == -1) return -1; // No getgid() and getuid()? Well, we are God! // gid = getgid(); // uid = getuid(); uid = stbuf.st_uid; gid = stbuf.st_gid; if(uid == stbuf.st_uid) { if( ((type & R_OK) && !(stbuf.st_mode & S_IRUSR) ) || ((type & W_OK) && !(stbuf.st_mode & S_IWUSR) ) || ((type & X_OK) && !(stbuf.st_mode & S_IXUSR) ) ) { errno = EACCES; return -1; } } else if(gid == stbuf.st_gid) { if( ((type & R_OK) && !(stbuf.st_mode & S_IRGRP) ) || ((type & W_OK) && !(stbuf.st_mode & S_IWGRP) ) || ((type & X_OK) && !(stbuf.st_mode & S_IXGRP) ) ) { errno = EACCES; return -1; } } else { if( ((type & R_OK) && !(stbuf.st_mode & S_IROTH) ) || ((type & W_OK) && !(stbuf.st_mode & S_IWOTH) ) || ((type & X_OK) && !(stbuf.st_mode & S_IXOTH) ) ) { errno = EACCES; return -1; } } return 0; } #endif bool localDrive::TestDir(char * dir) { char newdir[CROSS_LEN]; strcpy(newdir,basedir); strcat(newdir,dir); CROSS_FILENAME(newdir); dirCache.ExpandName(newdir); // Skip directory test, if "\" size_t len = strlen(newdir); if (len && (newdir[len-1]!='\\')) { // It has to be a directory ! struct stat test; if (stat(newdir,&test)) return false; if ((test.st_mode & S_IFDIR)==0) return false; }; int temp=access(newdir,F_OK); return (temp==0); } bool localDrive::Rename(char * oldname,char * newname) { char newold[CROSS_LEN]; strcpy(newold,basedir); strcat(newold,oldname); CROSS_FILENAME(newold); dirCache.ExpandName(newold); char newnew[CROSS_LEN]; strcpy(newnew,basedir); strcat(newnew,newname); CROSS_FILENAME(newnew); #ifdef VITA int temp=sceIoRename(newold,dirCache.GetExpandName(newnew)); #else int temp=rename(newold,dirCache.GetExpandName(newnew)); #endif if (temp==0) dirCache.CacheOut(newnew); return (temp==0); } bool localDrive::AllocationInfo(Bit16u * _bytes_sector,Bit8u * _sectors_cluster,Bit16u * _total_clusters,Bit16u * _free_clusters) { *_bytes_sector=allocation.bytes_sector; *_sectors_cluster=allocation.sectors_cluster; *_total_clusters=allocation.total_clusters; *_free_clusters=allocation.free_clusters; return true; } bool localDrive::FileExists(const char* name) { char newname[CROSS_LEN]; strcpy(newname,basedir); strcat(newname,name); CROSS_FILENAME(newname); dirCache.ExpandName(newname); struct stat temp_stat; if(stat(newname,&temp_stat)!=0) return false; if(temp_stat.st_mode & S_IFDIR) return false; return true; } bool localDrive::FileStat(const char* name, FileStat_Block * const stat_block) { char newname[CROSS_LEN]; strcpy(newname,basedir); strcat(newname,name); CROSS_FILENAME(newname); dirCache.ExpandName(newname); struct stat temp_stat; if(stat(newname,&temp_stat)!=0) return false; /* Convert the stat to a FileStat */ struct tm *time; if((time=localtime((const time_t*)&temp_stat.st_mtime))!=0) { stat_block->time=DOS_PackTime((Bit16u)time->tm_hour,(Bit16u)time->tm_min,(Bit16u)time->tm_sec); stat_block->date=DOS_PackDate((Bit16u)(time->tm_year+1900),(Bit16u)(time->tm_mon+1),(Bit16u)time->tm_mday); } else { } stat_block->size=(Bit32u)temp_stat.st_size; return true; } Bit8u localDrive::GetMediaByte(void) { return allocation.mediaid; } bool localDrive::isRemote(void) { return false; } bool localDrive::isRemovable(void) { return false; } Bits localDrive::UnMount(void) { delete this; return 0; } localDrive::localDrive(const char * startdir,Bit16u _bytes_sector,Bit8u _sectors_cluster,Bit16u _total_clusters,Bit16u _free_clusters,Bit8u _mediaid) { strcpy(basedir,startdir); sprintf(info,"local directory %s",startdir); allocation.bytes_sector=_bytes_sector; allocation.sectors_cluster=_sectors_cluster; allocation.total_clusters=_total_clusters; allocation.free_clusters=_free_clusters; allocation.mediaid=_mediaid; dirCache.SetBaseDir(basedir); } //TODO Maybe use fflush, but that seemed to fuck up in visual c bool localFile::Read(Bit8u * data,Bit16u * size) { if ((this->flags & 0xf) == OPEN_WRITE) { // check if file opened in write-only mode DOS_SetError(DOSERR_ACCESS_DENIED); return false; } if (last_action==WRITE) fseek(fhandle,ftell(fhandle),SEEK_SET); last_action=READ; *size=(Bit16u)fread(data,1,*size,fhandle); /* Fake harddrive motion. Inspector Gadget with soundblaster compatible */ /* Same for Igor */ /* hardrive motion => unmask irq 2. Only do it when it's masked as unmasking is realitively heavy to emulate */ Bit8u mask = IO_Read(0x21); if(mask & 0x4 ) IO_Write(0x21,mask&0xfb); return true; } bool localFile::Write(Bit8u * data,Bit16u * size) { if ((this->flags & 0xf) == OPEN_READ) { // check if file opened in read-only mode DOS_SetError(DOSERR_ACCESS_DENIED); return false; } if (last_action==READ) fseek(fhandle,ftell(fhandle),SEEK_SET); last_action=WRITE; if(*size==0){ #ifdef VITA return true; #else return (!ftruncate(fileno(fhandle),ftell(fhandle))); #endif } else { *size=(Bit16u)fwrite(data,1,*size,fhandle); return true; } } bool localFile::Seek(Bit32u * pos,Bit32u type) { int seektype; switch (type) { case DOS_SEEK_SET:seektype=SEEK_SET;break; case DOS_SEEK_CUR:seektype=SEEK_CUR;break; case DOS_SEEK_END:seektype=SEEK_END;break; default: //TODO Give some doserrorcode; return false;//ERROR } int ret=fseek(fhandle,*reinterpret_cast(pos),seektype); if (ret!=0) { // Out of file range, pretend everythings ok // and move file pointer top end of file... ?! (Black Thorne) fseek(fhandle,0,SEEK_END); }; #if 0 fpos_t temppos; fgetpos(fhandle,&temppos); Bit32u * fake_pos=(Bit32u*)&temppos; *pos=*fake_pos; #endif *pos=(Bit32u)ftell(fhandle); last_action=NONE; return true; } bool localFile::Close() { // only close if one reference left if (refCtr==1) { if(fhandle) fclose(fhandle); fhandle = 0; open = false; }; return true; } Bit16u localFile::GetInformation(void) { return read_only_medium?0x40:0; } localFile::localFile(const char* _name, FILE * handle) { fhandle=handle; open=true; UpdateDateTimeFromHost(); attr=DOS_ATTR_ARCHIVE; last_action=NONE; read_only_medium=false; name=0; SetName(_name); } void localFile::FlagReadOnlyMedium(void) { read_only_medium = true; } bool localFile::UpdateDateTimeFromHost(void) { if(!open) return false; struct stat temp_stat; fstat(fileno(fhandle),&temp_stat); struct tm * ltime; if((ltime=localtime((const time_t*)&temp_stat.st_mtime))!=0) { time=DOS_PackTime((Bit16u)ltime->tm_hour,(Bit16u)ltime->tm_min,(Bit16u)ltime->tm_sec); date=DOS_PackDate((Bit16u)(ltime->tm_year+1900),(Bit16u)(ltime->tm_mon+1),(Bit16u)ltime->tm_mday); } else { time=1;date=1; } return true; } void localFile::Flush(void) { if (last_action==WRITE) { fseek(fhandle,ftell(fhandle),SEEK_SET); last_action=NONE; } } // ******************************************** // CDROM DRIVE // ******************************************** int MSCDEX_RemoveDrive(char driveLetter); int MSCDEX_AddDrive(char driveLetter, const char* physicalPath, Bit8u& subUnit); bool MSCDEX_HasMediaChanged(Bit8u subUnit); bool MSCDEX_GetVolumeName(Bit8u subUnit, char* name); cdromDrive::cdromDrive(const char driveLetter, const char * startdir,Bit16u _bytes_sector,Bit8u _sectors_cluster,Bit16u _total_clusters,Bit16u _free_clusters,Bit8u _mediaid, int& error) :localDrive(startdir,_bytes_sector,_sectors_cluster,_total_clusters,_free_clusters,_mediaid) { // Init mscdex error = MSCDEX_AddDrive(driveLetter,startdir,subUnit); strcpy(info, "CDRom "); strcat(info, startdir); this->driveLetter = driveLetter; // Get Volume Label char name[32]; if (MSCDEX_GetVolumeName(subUnit,name)) dirCache.SetLabel(name,true,true); } bool cdromDrive::FileOpen(DOS_File * * file,char * name,Bit32u flags) { if ((flags&0xf)==OPEN_READWRITE) { flags &= ~OPEN_READWRITE; } else if ((flags&0xf)==OPEN_WRITE) { DOS_SetError(DOSERR_ACCESS_DENIED); return false; } bool retcode = localDrive::FileOpen(file,name,flags); if(retcode) (dynamic_cast(*file))->FlagReadOnlyMedium(); return retcode; } bool cdromDrive::FileCreate(DOS_File * * /*file*/,char * /*name*/,Bit16u /*attributes*/) { DOS_SetError(DOSERR_ACCESS_DENIED); return false; } bool cdromDrive::FileUnlink(char * /*name*/) { DOS_SetError(DOSERR_ACCESS_DENIED); return false; } bool cdromDrive::RemoveDir(char * /*dir*/) { DOS_SetError(DOSERR_ACCESS_DENIED); return false; } bool cdromDrive::MakeDir(char * /*dir*/) { DOS_SetError(DOSERR_ACCESS_DENIED); return false; } bool cdromDrive::Rename(char * /*oldname*/,char * /*newname*/) { DOS_SetError(DOSERR_ACCESS_DENIED); return false; } bool cdromDrive::GetFileAttr(char * name,Bit16u * attr) { bool result = localDrive::GetFileAttr(name,attr); if (result) *attr |= DOS_ATTR_READ_ONLY; return result; } bool cdromDrive::GetFileAttrEx(char* name, struct stat *status) { return localDrive::GetFileAttrEx(name,status); } Bit32u cdromDrive::GetCompressedSize(char* name) { return localDrive::GetCompressedSize(name); } void* cdromDrive::CreateOpenFile(const char* name) { return localDrive::CreateOpenFile(name); } bool cdromDrive::FindFirst(char * _dir,DOS_DTA & dta,bool /*fcb_findfirst*/) { // If media has changed, reInit drivecache. if (MSCDEX_HasMediaChanged(subUnit)) { dirCache.EmptyCache(); // Get Volume Label char name[32]; if (MSCDEX_GetVolumeName(subUnit,name)) dirCache.SetLabel(name,true,true); } return localDrive::FindFirst(_dir,dta); } void cdromDrive::SetDir(const char* path) { // If media has changed, reInit drivecache. if (MSCDEX_HasMediaChanged(subUnit)) { dirCache.EmptyCache(); // Get Volume Label char name[32]; if (MSCDEX_GetVolumeName(subUnit,name)) dirCache.SetLabel(name,true,true); } localDrive::SetDir(path); } bool cdromDrive::isRemote(void) { return true; } bool cdromDrive::isRemovable(void) { return true; } Bits cdromDrive::UnMount(void) { if(MSCDEX_RemoveDrive(driveLetter)) { delete this; return 0; } return 2; }