// ScanFile scans a single file for viruses using the ClamAV databases. It returns the virus name // (if found), the number of bytes read from the file, in CountPrecision units, and a status code. // If the file is clean the error code will be Success (Clean) and virus name will be empty. If a // virus is found the error code will be the corresponding string for Virus (currently "Virus(es) // detected"). func (e *Engine) ScanFile(path string, opts uint) (string, uint, error) { var name *C.char var scanned C.ulong cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) err := ErrorCode(C.cl_scanfile(cpath, &name, &scanned, (*C.struct_cl_engine)(e), C.uint(opts))) if err == Success { return "", 0, nil } if err == Virus { if opts&ScanAllmatches > 0 { return C.GoString(C.fixup_clam_virus(&name)), uint(scanned), fmt.Errorf(StrError(err)) } else { return C.GoString(name), uint(scanned), fmt.Errorf(StrError(err)) } } return "", 0, fmt.Errorf(StrError(err)) }
// ScanFileCb scans a single file for viruses using the ClamAV databases and using callbacks from // ClamAV to read/resolve file data. The callbacks can be used to scan files in memory, to scan multiple // files inside archives, etc. The function returns the virus name // (if found), the number of bytes read from the file in CountPrecision units, and a status code. // If the file is clean the error code will be Success (Clean) and virus name will be empty. If a // virus is found the error code will be the corresponding string for Virus (currently "Virus(es) // detected"). // The context argument will be sent back to the callbacks, so effort must be made to retain it // throughout the execution of the scan from garbage collection func (e *Engine) ScanFileCb(path string, opts uint, context interface{}) (string, uint, error) { var name *C.char var scanned C.ulong // pass a C-allocated pointer to the path to avoid crashing with garbage collector cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) // find where to store the context in our callback map. we do _not_ pass the context to // C directly because aggressive garbage collection will move it around callbacks.Lock() // find the next available empty spot. uintptr overflow should be ok -- we don't expect // to have Max(uintptr) files scanned at the same time for _, ok := callbacks.cb[callbacks.nextId]; ok; callbacks.nextId++ { } cbidx := callbacks.nextId callbacks.cb[cbidx] = context callbacks.nextId++ callbacks.Unlock() // cleanup defer func() { callbacks.Lock() delete(callbacks.cb, cbidx) callbacks.Unlock() }() err := ErrorCode(C.cl_scanfile_callback(cpath, &name, &scanned, (*C.struct_cl_engine)(e), C.uint(opts), unsafe.Pointer(cbidx))) if err == Success { return "", 0, nil } if err == Virus { if opts&ScanAllmatches > 0 { return C.GoString(C.fixup_clam_virus(&name)), uint(scanned), fmt.Errorf(StrError(err)) } else { return C.GoString(name), uint(scanned), fmt.Errorf(StrError(err)) } } return "", 0, fmt.Errorf(StrError(err)) }