/** * Given a CCNxName, a directory path, a file name, and a requested chunk number, return a new CCNxContentObject * with that CCNxName and containing the specified chunk of the file. The new CCNxContentObject will also * contain the number of the last chunk required to transfer the complete file. Note that the last chunk of the * file being retrieved is calculated each time we retrieve a chunk so the file can be growing in size as we * transfer it. * The new CCnxContentObject must eventually be released by calling ccnxContentObject_Release(). * * @param [in] name The CCNxName to use when creating the new CCNxContentObject. * @param [in] directoryPath The directory in which to find the specified file. * @param [in] fileName The name of the file. * @param [in] requestedChunkNumber The number of the requested chunk from the file. * * @return A new CCNxContentObject instance containing the request chunk of the specified file, or NULL if * the file did not exist or was otherwise unavailable. */ static CCNxContentObject * _createFetchResponse(const CCNxName *name, const char *directoryPath, const char *fileName, uint64_t requestedChunkNumber) { CCNxContentObject *result = NULL; uint64_t finalChunkNumber = 0; // Combine the directoryPath and fileName into the full path name of the desired file size_t filePathBufferSize = strlen(fileName) + strlen(directoryPath) + 2; // +2 for '/' and trailing null. char *fullFilePath = parcMemory_Allocate(filePathBufferSize); assertNotNull(fullFilePath, "parcMemory_Allocate(%zu) returned NULL", filePathBufferSize); snprintf(fullFilePath, filePathBufferSize, "%s/%s", directoryPath, fileName); // Make sure the file exists and is accessible before creating a ContentObject response. if (tutorialFileIO_IsFileAvailable(fullFilePath)) { // Since the file's length can change (e.g. if it is being written to while we're fetching // it), the final chunk number can change between requests for content chunks. So, update // it each time this function is called. finalChunkNumber = _getFinalChunkNumberOfFile(fullFilePath, tutorialCommon_ChunkSize); // Get the actual contents of the specified chunk of the file. PARCBuffer *payload = tutorialFileIO_GetFileChunk(fullFilePath, tutorialCommon_ChunkSize, requestedChunkNumber); if (payload != NULL) { result = _createContentObject(name, payload, finalChunkNumber); parcBuffer_Release(&payload); } } parcMemory_Deallocate((void **) &fullFilePath); return result; // Could be NULL if there was no payload }
PARCBuffer * tutorialFileIO_CreateDirectoryListing(const char *directoryName) { DIR *directory = opendir(directoryName); assertNotNull(directory, "Couldn't open directory '%s' for reading.", directoryName); PARCBufferComposer *directoryListing = parcBufferComposer_Create(); struct dirent *entry; while ((entry = readdir(directory)) != NULL) { switch (entry->d_type) { case DT_REG: { // a regular file // We need the full file path to check its size. PARCBufferComposer *fullFilePath = parcBufferComposer_Create(); parcBufferComposer_Format(fullFilePath, "%s/%s", directoryName, entry->d_name); PARCBuffer *fileNameBuffer = parcBufferComposer_ProduceBuffer(fullFilePath); char *fullFilePathString = parcBuffer_ToString(fileNameBuffer); parcBuffer_Release(&fileNameBuffer); if (tutorialFileIO_IsFileAvailable(fullFilePathString)) { parcBufferComposer_Format(directoryListing, " %s (%zu bytes)\n", entry->d_name, tutorialFileIO_GetFileSize(fullFilePathString)); } parcBufferComposer_Release(&fullFilePath); parcMemory_Deallocate((void **) &fullFilePathString); break; } case DT_LNK: case DT_DIR: default: // ignore everything but regular files break; } } closedir(directory); PARCBuffer *result = parcBufferComposer_ProduceBuffer(directoryListing); parcBufferComposer_Release(&directoryListing); return result; }