#include "sisinstaller.h" #include "sisfile.h" #include "sisfilelink.h" #include "sisfilerecord.h" #include "sisreqrecord.h" #include "psion.h" #include #include #include #include static int continueRunning; static int checkAbortHash(void *, u_int32_t) { if (continueRunning) { if (logLevel >= 1) { printf("#"); fflush(stdout); } } return continueRunning; } SISInstaller::SISInstaller() { m_installed = 0; m_ownInstalled = false; } SISInstaller::~SISInstaller() { if (m_ownInstalled) { SISFileLink* curr = m_installed; while (curr) { delete curr->m_file; SISFileLink* next = curr->m_next; delete curr; curr = next; } } } void SISInstaller::createDirs(char* filename) { char* end = filename + strlen(filename); while (--end > filename) { char ch = *end; if ((ch == '/') || (ch == '\\')) { *end = 0; if (logLevel >= 1) fprintf(stderr, "Checking for existance of %s\n", filename); // if (!m_psion->dirExists(filename)) { if (logLevel >= 1) fprintf(stderr, "Creating dir %s\n", filename); Enum res; res = m_psion->mkdir(filename); if ((res != rfsv::E_PSI_GEN_NONE) && (res != rfsv::E_PSI_FILE_EXIST)) { fprintf(stderr, " -> Failed: %s\n", (const char*)res); } } *end = ch; return; } } } void SISInstaller::copyFile(SISFileRecord* fileRecord) { uint8_t* destptr = fileRecord->getDestPtr(); if (destptr == 0) return; if (destptr[0] == '!') { if (m_drive == 0) selectDrive(); m_file->setDrive(m_drive); } int len = fileRecord->m_destLength; char dest[256]; memcpy(dest, destptr, len); dest[len] = 0; if (dest[0] == '!') { dest[0] = m_drive; fileRecord->setMainDrive(m_drive); if (logLevel >= 2) fprintf(stderr, "Setting main drive to %c\n", m_drive); } char msgbuf[1024]; sprintf(msgbuf, "Copying %d bytes to %s\n", fileRecord->m_fileLengths[m_fileNo], dest); { printf("%s\n", msgbuf); } copyBuf(fileRecord->getFilePtr(m_fileNo), fileRecord->m_fileLengths[m_fileNo], dest); } void SISInstaller::copyBuf(const uint8_t* buf, int len, char* name) { createDirs(name); char srcName[32]; strcpy(srcName, "/tmp/plptools-sis-XXXXXX"); int fd = mkstemp(srcName); if (-1 == fd) { fprintf(stderr, "Couldn't create temp file: %s\n", strerror(errno)); return; } Enum res; if (logLevel >= 2) fprintf(stderr, "Storing in %s\n", srcName); write(fd, buf, len); close(fd); continueRunning = 1; res = m_psion->copyToPsion(srcName, name, NULL, checkAbortHash); if (res == rfsv::E_PSI_GEN_NONE) { if (logLevel >= 1) fprintf(stderr, " -> Success.\n"); } else { fprintf(stderr, " -> Fail: %s\n", (const char*)res); } unlink(srcName); } int SISInstaller::installFile(SISFileRecord* fileRecord) { char readbuf[8]; switch (fileRecord->m_fileType) { case 0: copyFile(fileRecord); break; case 1: { printf("Info:\n%.*s\n", fileRecord->m_fileLengths[m_fileNo], fileRecord->getFilePtr(m_fileNo)); switch (fileRecord->m_fileDetails) { case 0: printf("Continue\n"); fgets(readbuf, sizeof(readbuf), stdin); break; case 1: printf("(Install next file?) [Y]es/No\n"); fgets(readbuf, sizeof(readbuf), stdin); if (strchr("Nn", readbuf[0])) { return FILE_SKIP; } break; case 2: printf("(Continue installation?) [Y]es/No\n"); fgets(readbuf, sizeof(readbuf), stdin); if (strchr("Nn", readbuf[0])) { // Watch out if we have copied any files // already. // return FILE_ABORT; } break; } } break; case 2: { if (logLevel >= 1) fprintf(stderr, "Recursive sis file...\n"); SISFile sisFile; uint8_t* buf2 = fileRecord->getFilePtr(m_fileNo); off_t len = fileRecord->m_fileLengths[m_fileNo]; // if (m_lastSisFile < fileptr + len) // m_lastSisFile = fileptr + len; SisRC rc = sisFile.fillFrom(buf2, len); if (rc != SIS_OK) { fprintf(stderr, "Could not read contained sis file, rc = %d\n", rc); break; } SISInstaller installer; installer.setPsion(m_psion); installer.setInstalled(m_installed); rc = installer.run(&sisFile, buf2, len, m_file); if (0 == m_drive) { m_drive = sisFile.m_header.m_installationDrive; m_file->setDrive(m_drive); if (logLevel >= 1) fprintf(stderr, "Updated drive to %c from recursive sis file\n", m_drive); } break; } case 3: if (logLevel >= 1) fprintf(stderr, "Run %.*s during installation/remove\n", fileRecord->m_destLength, fileRecord->getDestPtr()); break; case 4: if (logLevel >= 2) fprintf(stderr, "Running the app will create %.*s\n", fileRecord->m_destLength, fileRecord->getDestPtr()); break; } return FILE_OK; } #define SYSTEMINSTALL "c:\\system\\install\\" SisRC SISInstaller::loadInstalled() { PlpDir files; Enum res; if ((res = m_psion->dir(SYSTEMINSTALL, files)) != rfsv::E_PSI_GEN_NONE) { return SIS_FAILED; } else { while (!files.empty()) { PlpDirent file = files[0]; if (logLevel >= 1) fprintf(stderr, "Loading sis file `%s'\n", file.getName()); char sisname[256]; sprintf(sisname, "%s%s", SYSTEMINSTALL, file.getName()); loadPsionSis(sisname); files.pop_front(); } } } void SISInstaller::loadPsionSis(const char* name) { char srcName[32]; strcpy(srcName, "/tmp/plptools-sis-XXXXXX"); int fd = mkstemp(srcName); if (-1 == fd) { fprintf(stderr, "Couldn't create temp file: %s\n", strerror(errno)); return; } Enum res; continueRunning = 1; if (logLevel >= 2) fprintf(stderr, "Copying from %s to temp file %s\n", name, srcName); res = m_psion->copyFromPsion(name, fd, checkAbortHash); if (res == rfsv::E_PSI_GEN_NONE) { off_t fileLen = lseek(fd, 0, SEEK_END); if (logLevel >= 2) fprintf(stderr, "Read %d bytes from the Psion file %s\n", (int)fileLen, name); lseek(fd, SEEK_SET, 0); uint8_t* sisbuf = new uint8_t[fileLen]; int rc = read(fd, sisbuf, fileLen); if (rc == fileLen) { SISFile* sisFile = new SISFile(); SisRC rc2 = sisFile->fillFrom(sisbuf, fileLen); if (rc2 == SIS_OK) { if (logLevel >= 1) fprintf(stderr, " Ok.\n"); SISFileLink* link = new SISFileLink(sisFile); link->m_next = m_installed; m_ownInstalled = true; m_installed = link; sisFile->ownBuffer(); } else { delete sisFile; delete[] sisbuf; } } } close(fd); unlink(srcName); } void SISInstaller::removeFile(SISFileRecord* fileRecord) { int len = fileRecord->m_destLength; char dest[256]; memcpy(dest, fileRecord->getDestPtr(), len); dest[len] = 0; if (logLevel >= 1) fprintf(stderr, "Removing file component %s.\n", dest); m_psion->remove(dest); } SisRC SISInstaller::run(SISFile* file, uint8_t* buf, off_t len) { return run(file, buf, len, 0); } SisRC SISInstaller::run(SISFile* file, uint8_t* buf, off_t len, SISFile* parent) { int n; long lang; m_file = file; m_buf = buf; char msgbuf[1024]; if (parent == 0) { n = m_file->m_header.m_nlangs; if (n == 1) { sprintf(msgbuf, _("You have only one language: %s"), m_file->getLanguage(0)->m_name); printf("%s\n", msgbuf); lang = 0; } else { printf("Select a language (%d alternatives):\n", n); for (int i = 0; i < n; ++i) printf(" %d. %s\n", i, m_file->getLanguage(i)->m_name); lang = 0; } } else { // This needs to check the _name_ of the language, since the // recursive sis file might have a different language list. // For now, defalt to 0. // lang = parent->getLanguage(); lang = 0; if (logLevel >= 1) fprintf(stderr, "Forcing language to %ld\n", lang); } m_file->setLanguage(lang); uint8_t* compName = m_file->getName(); sprintf(msgbuf, _("Installing component: `%s'"), compName); printf("%s\n", msgbuf); // In order to check requisites and previous versions, we need to // load all sis files from the c:/system/install directory. // This is the only way to find out if a specific application or // library has been loaded, since the sis file names could be just // about anything. // if (m_installed == 0) loadInstalled(); // Check Requisites. // n = m_file->m_header.m_nreqs; if (logLevel >= 1) fprintf(stderr, "Found %d requisites, of some sort.\n", n); for (int i = 0; i < n; ++i) { SISReqRecord* reqRecord = &m_file->m_reqRecords[i]; if (logLevel >= 1) fprintf(stderr, " Check if app with uid %08x exists with version >= %d.%d\n", reqRecord->m_uid, reqRecord->m_major, reqRecord->m_minor); } // Check previous version. // if (logLevel >= 1) fprintf(stderr, "Checking if this app (uid %08x) exists with a version less than %d.%d.\n", m_file->m_header.m_uid1, m_file->m_header.m_major, m_file->m_header.m_minor); bool uninstallFirst = false; SISFileLink* curr = m_installed; SISFile* oldFile = 0; while (curr) { SISFile* sisFile = curr->m_file; switch (sisFile->compareApp(m_file)) { case SIS_VER_EARLIER: uninstallFirst = true; oldFile = sisFile; break; case SIS_SAME_OR_LATER: // Ask for confirmation. uninstallFirst = true; oldFile = sisFile; break; case SIS_OTHER_VARIANT: // Ask for confirmation. uninstallFirst = true; oldFile = sisFile; break; } curr = curr->m_next; } if (uninstallFirst) { // printf("You should uninstall the previous version first.\n"); // if (!m_forced) // return SIS_ABORTED; // printf("Forced mode... Installing anyway!\n"); if (oldFile == 0) fprintf(stderr, "Already installed, but 0?\n"); else { sprintf(msgbuf, "%s", _("Uninstalling the previous version first.")); printf("%s\n", msgbuf); uninstall(oldFile); } } // Install file components. // n = m_file->m_header.m_nfiles; if (logLevel >= 1) fprintf(stderr, "Found %d files.\n", n); m_drive = (parent == 0) ? 0 : parent->m_header.m_installationDrive; int nCopiedFiles = 0; m_lastSisFile = 0; bool skipnext = false; bool aborted = false; while (!aborted && (n-- > 0)) { SISFileRecord* fileRecord = &m_file->m_fileRecords[n]; m_fileNo = (fileRecord->m_flags & 1) ? lang : 0; if (skipnext) { skipnext = false; } else { switch (installFile(fileRecord)) { case FILE_OK: break; case FILE_SKIP: skipnext = true; break; case FILE_ABORT: aborted = true; break; } } if (!aborted) nCopiedFiles++; } m_file->setFiles(nCopiedFiles); if (logLevel >= 1) fprintf(stderr, "Installed %d files of %d, cutting at offset %d.\n", m_file->m_header.m_installationFiles, m_file->m_header.m_nfiles, m_file->getResidualEnd()); if (nCopiedFiles == 0) { // There is no need to copy any uninstall information to the // psion, unless we've actually copied anything there. // return SIS_ABORTED; } // Copy the updated sis file to the epoc machine. // char resname[256]; int namelen = 0; while (compName[namelen] != 0) { if (compName[namelen] == ' ') break; namelen++; } sprintf(resname, "C:\\System\\Install\\%.*s.sis", namelen, compName); if (logLevel >= 1) fprintf(stderr, "Creating residual sis file %s\n", resname); copyBuf(buf, m_file->getResidualEnd(), resname); if (aborted) return SIS_ABORTED; return SIS_OK; } void SISInstaller::selectDrive() { u_int32_t devbits = 0; Enum res; char drivelist[26]; int space[26]; int size[26]; int ndrives = 0; if ((res = m_psion->devlist(devbits)) == rfsv::E_PSI_GEN_NONE) { for (int i = 0; i < 26; i++) { PlpDrive plpdrive; if (((devbits & 1) != 0) && (m_psion->devinfo(i + 'A', plpdrive) == rfsv::E_PSI_GEN_NONE)) { u_int32_t mediaType = plpdrive.getMediaType(); if ((mediaType == 3) || (mediaType == 5)) { drivelist[ndrives] = 'A' + i; space[ndrives] = plpdrive.getSpace(); size[ndrives] = plpdrive.getSize(); printf("%c: %lld bytes free, %lld bytes total\n", 'A' + i, plpdrive.getSpace(), plpdrive.getSize()); ++ndrives; } } devbits >>= 1; } } drivelist[ndrives] = 0; if (ndrives == 0) { m_drive = 'C'; if (logLevel >= 1) printf("Selecting the only drive %c\n", m_drive); } else if (ndrives == 1) { m_drive = drivelist[0]; if (logLevel >= 1) printf("Selecting the only drive %c\n", m_drive); } else { { printf("Please select a drive\n"); char ch; char readbuf[2]; while (m_drive == 0) { fgets(readbuf, 2, stdin); ch = readbuf[0]; if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { m_drive = toupper(ch); if (!strchr(drivelist, m_drive)) { m_drive = 0; printf("Please select a valid drive: %s\n", drivelist); } } } } } } void SISInstaller::setPsion(Psion* psion) { m_psion = psion; } void SISInstaller::uninstall(SISFile* file) { int n = file->m_header.m_nfiles; int fileix = n - file->m_header.m_installationFiles; if (logLevel >= 1) fprintf(stderr, "Uninstalling %d files, from a total of %d.\n", file->m_header.m_installationFiles, file->m_header.m_nfiles); int lang = file->getLanguage(); while (fileix < n) { SISFileRecord* fileRecord = &file->m_fileRecords[fileix]; m_fileNo = (fileRecord->m_flags & 1) ? lang : 0; char drive = file->m_header.m_installationDrive; fileRecord->setMainDrive(drive); uninstallFile(fileRecord); ++fileix; } } void SISInstaller::uninstallFile(SISFileRecord* fileRecord) { switch (fileRecord->m_fileType) { case 0: case 4: removeFile(fileRecord); break; case 2: { #if 0 // This is messy... We can't remove the sis component unless // we've stored the entire component in the residual sis // file on the target machine. if (logLevel >= 1) fprintf(stderr, "Recursive sis file...\n"); SISFile sisFile; int fileptr = fileRecord->m_filePtrs[m_fileNo]; uint8_t* buf2 = m_buf + fileptr; off_t len = fileRecord->m_fileLengths[m_fileNo]; if (m_lastSisFile < fileptr + len) m_lastSisFile = fileptr + len; SisRC rc = sisFile.fillFrom(buf2, len); if (rc != SIS_OK) { fprintf(stderr, "Could not read contained sis file, rc = %d\n", rc); break; } SISInstaller installer; installer.setPsion(m_psion); installer.setInstalled(m_installed); rc = installer.run(&sisFile, buf2, len, m_file); if (0 == m_drive) { m_drive = sisFile.m_header.m_installationDrive; m_file->setDrive(m_drive); if (logLevel >= 1) fprintf(stderr, "Updated drive to %c from recursive sis file\n", m_drive); } #endif break; } } }